Files
Wino-Mail/Wino.Mail.ViewModels/MailListPageViewModel.cs

1147 lines
42 KiB
C#
Raw Permalink Normal View History

2024-04-18 01:44:37 +02:00
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using System.Reactive.Linq;
using System.Threading;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using MoreLinq;
using Nito.AsyncEx;
using Serilog;
using Wino.Core.Domain;
using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Entities.Shared;
2024-04-18 01:44:37 +02:00
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
Folder operations, Gmail folder sync improvements and rework of menu items. (#273) * New rename folder dialog keys. * Insfra work for folder operations and rename folder code. * RenameFolder for Gmail. * Fixed input dialog to take custom take for primary button. * Missing rename for DS call. * Outlook to throw exception in case of error. * Implemented rename folder functionality for Outlook. * Remove default primary text from input dialog. * Fixed an issue where outlook folder rename does not work. * Disable vertical scroll for composing page editor items. * Fixing some issues with imap folder sync. * fix copy pasta * TODO folder update/removed overrides for shell. * New rename folder dialog keys. * Insfra work for folder operations and rename folder code. * RenameFolder for Gmail. * Fixed input dialog to take custom take for primary button. * Missing rename for DS call. * Outlook to throw exception in case of error. * Implemented rename folder functionality for Outlook. * Remove default primary text from input dialog. * Fixed an issue where outlook folder rename does not work. * Disable vertical scroll for composing page editor items. * Fixing some issues with imap folder sync. * fix copy pasta * TODO folder update/removed overrides for shell. * New rename folder dialog keys. * Insfra work for folder operations and rename folder code. * RenameFolder for Gmail. * Fixed input dialog to take custom take for primary button. * Missing rename for DS call. * Outlook to throw exception in case of error. * Implemented rename folder functionality for Outlook. * Remove default primary text from input dialog. * Fixed an issue where outlook folder rename does not work. * Disable vertical scroll for composing page editor items. * Fixing some issues with imap folder sync. * fix copy pasta * TODO folder update/removed overrides for shell. * New rename folder dialog keys. * Fixed an issue where redundant older updates causing pivots to be re-created. * New empty folder request * New rename folder dialog keys. * Insfra work for folder operations and rename folder code. * RenameFolder for Gmail. * Fixed input dialog to take custom take for primary button. * Missing rename for DS call. * Outlook to throw exception in case of error. * Implemented rename folder functionality for Outlook. * Remove default primary text from input dialog. * Fixed an issue where outlook folder rename does not work. * Fixing some issues with imap folder sync. * fix copy pasta * TODO folder update/removed overrides for shell. * New rename folder dialog keys. * New rename folder dialog keys. * New rename folder dialog keys. * Fixed an issue where redundant older updates causing pivots to be re-created. * New empty folder request * Enable empty folder on base sync. * Move updates on event listeners. * Remove folder UI messages. * Reworked folder synchronization for gmail. * Loading folders on the fly as the selected account changed instead of relying on cached menu items. * Merged account folder items, re-navigating to existing rendering page. * - Reworked merged account menu system. - Reworked unread item count loadings. - Fixed back button visibility. - Instant rendering of mails if renderer is active. - Animation fixes. - Menu item re-load crash/hang fixes. * Handle folder renaming on the UI. * Empty folder for all synchronizers. * New execution delay mechanism and handling folder mark as read for all synchronizers. * Revert UI changes on failure for IMAP. * Remove duplicate translation keys. * Cleanup.
2024-07-09 01:05:16 +02:00
using Wino.Core.Domain.Models.Folders;
2024-04-18 01:44:37 +02:00
using Wino.Core.Domain.Models.MailItem;
using Wino.Core.Domain.Models.Menus;
using Wino.Core.Domain.Models.Reader;
using Wino.Core.Domain.Models.Server;
2024-04-18 01:44:37 +02:00
using Wino.Core.Domain.Models.Synchronization;
using Wino.Mail.ViewModels.Collections;
using Wino.Mail.ViewModels.Data;
using Wino.Mail.ViewModels.Messages;
Full trust Wino Server implementation. (#295) * Separation of messages. Introducing Wino.Messages library. * Wino.Server and Wino.Packaging projects. Enabling full trust for UWP and app service connection manager basics. * Remove debug code. * Enable generating assembly info to deal with unsupported os platform warnings. * Fix server-client connection. * UIMessage communication. Single instancing for server and re-connection mechanism on suspension. * Removed IWinoSynchronizerFactory from UWP project. * Removal of background task service from core. * Delegating changes to UI and triggering new background synchronization. * Fix build error. * Moved core lib messages to Messaging project. * Better client-server communication. Handling of requests in the server. New synchronizer factory in the server. * WAM broker and MSAL token caching for OutlookAuthenticator. Handling account creation for Outlook. * WinoServerResponse basics. * Delegating protocol activation for Gmail authenticator. * Adding margin to searchbox to match action bar width. * Move libraries into lib folder. * Storing base64 encoded mime on draft creation instead of MimeMessage object. Fixes serialization/deserialization issue with S.T.Json * Scrollbar adjustments * WınoExpander for thread expander layout ıssue. * Handling synchronizer state changes. * Double init on background activation. * FIxing packaging issues and new Wino Mail launcher protocol for activation from full thrust process. * Remove debug deserialization. * Remove debug code. * Making sure the server connection is established when the app is launched. * Thrust -> Trust string replacement... * Rename package to Wino Mail * Enable translated values in the server. * Fixed an issue where toast activation can't find the clicked mail after the folder is initialized. * Revert debug code. * Change server background sync to every 3 minute and Inbox only synchronization. * Revert google auth changes. * App preferences page. * Changing tray icon visibility on preference change. * Start the server with invisible tray icon if set to invisible. * Reconnect button on the title bar. * Handling of toast actions. * Enable x86 build for server during packaging. * Get rid of old background tasks and v180 migration. * Terminate client when Exit clicked in server. * Introducing SynchronizationSource to prevent notifying UI after server tick synchronization. * Remove confirmAppClose restricted capability and unused debug code in manifest. * Closing the reconnect info popup when reconnect is clicked. * Custom RetryHandler for OutlookSynchronizer and separating client/server logs. * Running server on Windows startup. * Fix startup exe. * Fix for expander list view item paddings. * Force full sync on app launch instead of Inbox. * Fix draft creation. * Fix an issue with custom folder sync logic. * Reporting back account sync progress from server. * Fix sending drafts and missing notifications for imap. * Changing imap folder sync requirements. * Retain file count is set to 3. * Disabled swipe gestures temporarily due to native crash with SwipeControl * Save all attachments implementation. * Localization for save all attachments button. * Fix logging dates for logs. * Fixing ARM64 build. * Add ARM64 build config to packaging project. * Comment out OutOfProcPDB for ARM64. * Hnadling GONE response for Outlook folder synchronization.
2024-08-05 00:36:26 +02:00
using Wino.Messaging.Client.Mails;
using Wino.Messaging.Server;
using Wino.Messaging.UI;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
namespace Wino.Mail.ViewModels;
public partial class MailListPageViewModel : MailBaseViewModel,
IRecipient<MailItemNavigationRequested>,
IRecipient<ActiveMailFolderChangedEvent>,
IRecipient<MailItemSelectedEvent>,
IRecipient<MailItemSelectionRemovedEvent>,
IRecipient<AccountSynchronizationCompleted>,
IRecipient<NewMailSynchronizationRequested>,
IRecipient<AccountSynchronizerStateChanged>,
feat: Enhanced sender avatars with gravatar and favicons integration (#685) * feat: Enhanced sender avatars with gravatar and favicons integration * chore: Remove unused known companies thumbnails * feat(thumbnail): add IThumbnailService and refactor usage - Introduced a new interface `IThumbnailService` for handling thumbnail-related functionalities. - Registered `IThumbnailService` with its implementation `ThumbnailService` in the service container. - Updated `NotificationBuilder` to use an instance of `IThumbnailService` instead of static methods. - Refactored `ThumbnailService` from a static class to a regular class with instance methods and variables. - Modified `ImagePreviewControl` to utilize the new `IThumbnailService` instance. - Completed integration of `IThumbnailService` in the application by registering it in `App.xaml.cs`. * style: Show favicons as squares - Changed `hintCrop` in `NotificationBuilder` to `None` for app logo display. - Added `FaviconSquircle`, `FaviconImage`, and `isFavicon` to `ImagePreviewControl` for favicon handling. - Updated `UpdateInformation` method to manage favicon visibility. - Introduced `GetBitmapImageAsync` for converting Base64 to Bitmap images. - Enhanced XAML to include `FaviconSquircle` for improved UI appearance. * refactor thumbnail service * Removed old code and added clear method * added prefetch function * Change key from host to email * Remove redundant code * Test event * Fixed an issue with the thumbnail updated event. * Fix cutted favicons * exclude some domain from favicons * add yandex.ru * fix buttons in settings * remove prefetch method * Added thumbnails propagation to mailRenderingPage * Revert MailItemViewModel to object * Remove redundant code * spaces * await load parameter added * fix spaces * fix case sensativity for mail list thumbnails * change duckdns to google * Some cleanup. --------- Co-authored-by: Aleh Khantsevich <aleh.khantsevich@gmail.com> Co-authored-by: Burak Kaan Köse <bkaankose@outlook.com>
2025-06-21 01:40:25 +02:00
IRecipient<AccountCacheResetMessage>,
IRecipient<ThumbnailAdded>
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
private bool isChangingFolder = false;
private Guid? trackingSynchronizationId = null;
private int completedTrackingSynchronizationCount = 0;
/* [Bug] Unread folder reads All emails automatically with setting "Mark as Read: When Selected" enabled
* https://github.com/bkaankose/Wino-Mail/issues/162
* We store the UniqueIds of the mails that are marked as read in Gmail Unread folder
* to prevent them from being removed from the list when they are marked as read.
*/
private readonly HashSet<Guid> gmailUnreadFolderMarkedAsReadUniqueIds = [];
private IObservable<System.Reactive.EventPattern<NotifyCollectionChangedEventArgs>> selectionChangedObservable = null;
2025-02-26 19:59:20 +01:00
public WinoMailCollection MailCollection { get; }
2025-02-16 11:54:23 +01:00
public ObservableCollection<MailItemViewModel> SelectedItems { get; set; } = [];
public ObservableCollection<FolderPivotViewModel> PivotFolders { get; set; } = [];
public ObservableCollection<MailOperationMenuItem> ActionItems { get; set; } = [];
private readonly SemaphoreSlim listManipulationSemepahore = new SemaphoreSlim(1);
private CancellationTokenSource listManipulationCancellationTokenSource = new CancellationTokenSource();
public INavigationService NavigationService { get; }
public IStatePersistanceService StatePersistenceService { get; }
public IPreferencesService PreferencesService { get; }
public IThemeService ThemeService { get; }
private readonly IAccountService _accountService;
private readonly IMailDialogService _mailDialogService;
2025-02-16 11:54:23 +01:00
private readonly IMailService _mailService;
private readonly IFolderService _folderService;
private readonly IThreadingStrategyProvider _threadingStrategyProvider;
private readonly IContextMenuItemService _contextMenuItemService;
private readonly IWinoRequestDelegator _winoRequestDelegator;
private readonly IKeyPressService _keyPressService;
private readonly IWinoLogger _winoLogger;
2025-02-16 11:54:23 +01:00
private readonly IWinoServerConnectionManager _winoServerConnectionManager;
private MailItemViewModel _activeMailItem;
public List<SortingOption> SortingOptions { get; } =
[
new(Translator.SortingOption_Date, SortingOptionType.ReceiveDate),
new(Translator.SortingOption_Name, SortingOptionType.Sender),
];
public List<FilterOption> FilterOptions { get; } =
[
new (Translator.FilteringOption_All, FilterOptionType.All),
new (Translator.FilteringOption_Unread, FilterOptionType.Unread),
new (Translator.FilteringOption_Flagged, FilterOptionType.Flagged),
new (Translator.FilteringOption_Files, FilterOptionType.Files)
];
private FolderPivotViewModel _selectedFolderPivot;
[ObservableProperty]
private bool isMultiSelectionModeEnabled;
[ObservableProperty]
public partial string SearchQuery { get; set; }
2025-02-16 11:54:23 +01:00
[ObservableProperty]
private FilterOption _selectedFilterOption;
private SortingOption _selectedSortingOption;
// Indicates state when folder is initializing. It can happen after folder navigation, search or filter change applied or loading more items.
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(IsEmpty))]
[NotifyPropertyChangedFor(nameof(IsFolderEmpty))]
[NotifyPropertyChangedFor(nameof(IsProgressRing))]
private bool isInitializingFolder;
[ObservableProperty]
private InfoBarMessageType barSeverity;
[ObservableProperty]
private string barMessage;
[ObservableProperty]
private double mailListLength = 420;
[ObservableProperty]
private double maxMailListLength = 1200;
[ObservableProperty]
private string barTitle;
[ObservableProperty]
private bool isBarOpen;
/// <summary>
/// Current folder that is being represented from the menu.
/// </summary>
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(CanSynchronize))]
[NotifyPropertyChangedFor(nameof(IsFolderSynchronizationEnabled))]
private IBaseFolderMenuItem activeFolder;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(CanSynchronize))]
private bool isAccountSynchronizerInSynchronization;
public MailListPageViewModel(IMailDialogService dialogService,
INavigationService navigationService,
IAccountService accountService,
IMailDialogService mailDialogService,
2025-02-16 11:54:23 +01:00
IMailService mailService,
IStatePersistanceService statePersistenceService,
IFolderService folderService,
IThreadingStrategyProvider threadingStrategyProvider,
IContextMenuItemService contextMenuItemService,
IWinoRequestDelegator winoRequestDelegator,
IKeyPressService keyPressService,
IPreferencesService preferencesService,
IThemeService themeService,
IWinoLogger winoLogger,
2025-02-16 11:54:23 +01:00
IWinoServerConnectionManager winoServerConnectionManager)
2024-04-18 01:44:37 +02:00
{
2025-02-26 19:59:20 +01:00
MailCollection = new WinoMailCollection(threadingStrategyProvider);
2025-02-16 11:54:23 +01:00
PreferencesService = preferencesService;
ThemeService = themeService;
_winoLogger = winoLogger;
2025-02-16 11:54:23 +01:00
_winoServerConnectionManager = winoServerConnectionManager;
StatePersistenceService = statePersistenceService;
NavigationService = navigationService;
_accountService = accountService;
_mailDialogService = mailDialogService;
2025-02-16 11:54:23 +01:00
_mailService = mailService;
_folderService = folderService;
_threadingStrategyProvider = threadingStrategyProvider;
_contextMenuItemService = contextMenuItemService;
_winoRequestDelegator = winoRequestDelegator;
_keyPressService = keyPressService;
SelectedFilterOption = FilterOptions[0];
SelectedSortingOption = SortingOptions[0];
mailListLength = statePersistenceService.MailListPaneLength;
selectionChangedObservable = Observable.FromEventPattern<NotifyCollectionChangedEventArgs>(SelectedItems, nameof(SelectedItems.CollectionChanged));
selectionChangedObservable
.Throttle(TimeSpan.FromMilliseconds(100))
.Subscribe(async a =>
{
await ExecuteUIThread(() => { SelectedItemCollectionUpdated(a.EventArgs); });
});
2025-02-16 11:54:23 +01:00
MailCollection.MailItemRemoved += (c, removedItem) =>
{
if (removedItem is ThreadMailItemViewModel removedThreadViewModelItem)
{
2025-02-16 11:54:23 +01:00
foreach (var viewModel in removedThreadViewModelItem.ThreadItems.Cast<MailItemViewModel>())
{
2025-02-16 11:54:23 +01:00
if (SelectedItems.Contains(viewModel))
{
2025-02-16 11:54:23 +01:00
SelectedItems.Remove(viewModel);
}
}
2025-02-16 11:54:23 +01:00
}
else if (removedItem is MailItemViewModel removedMailItemViewModel && SelectedItems.Contains(removedMailItemViewModel))
{
SelectedItems.Remove(removedMailItemViewModel);
}
};
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
private void SetupTopBarActions()
{
ActionItems.Clear();
var actions = GetAvailableMailActions(SelectedItems);
actions.ForEach(a => ActionItems.Add(a));
}
2024-08-31 13:25:55 +02:00
2025-02-16 11:54:23 +01:00
#region Properties
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
/// <summary>
/// Selected internal folder. This can be either folder's own name or Focused-Other.
/// </summary>
public FolderPivotViewModel SelectedFolderPivot
{
get => _selectedFolderPivot;
set
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
if (_selectedFolderPivot != null)
_selectedFolderPivot.SelectedItemCount = 0;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
SetProperty(ref _selectedFolderPivot, value);
2024-04-18 01:44:37 +02:00
}
2025-02-16 11:54:23 +01:00
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
/// <summary>
/// Selected sorting option.
/// </summary>
public SortingOption SelectedSortingOption
{
get => _selectedSortingOption;
set
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
if (SetProperty(ref _selectedSortingOption, value))
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
if (value != null && MailCollection != null)
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
MailCollection.SortingType = value.Type;
2024-04-18 01:44:37 +02:00
}
}
}
2025-02-16 11:54:23 +01:00
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
public bool CanSynchronize => !IsAccountSynchronizerInSynchronization && IsFolderSynchronizationEnabled;
public bool IsFolderSynchronizationEnabled => ActiveFolder?.IsSynchronizationEnabled ?? false;
public int SelectedItemCount => SelectedItems.Count;
public bool HasMultipleItemSelections => SelectedItemCount > 1;
public bool HasSingleItemSelection => SelectedItemCount == 1;
public bool HasSelectedItems => SelectedItems.Any();
public bool IsArchiveSpecialFolder => ActiveFolder?.SpecialFolderType == SpecialFolderType.Archive;
public string SelectedMessageText => HasSelectedItems ? string.Format(Translator.MailsSelected, SelectedItemCount) : Translator.NoMailSelected;
2025-02-16 11:54:23 +01:00
/// <summary>
/// Indicates current state of the mail list. Doesn't matter it's loading or no.
/// </summary>
public bool IsEmpty => MailCollection.Count == 0;
/// <summary>
/// Progress ring only should be visible when the folder is initializing and there are no items. We don't need to show it when there are items.
/// </summary>
public bool IsProgressRing => IsInitializingFolder && IsEmpty;
public bool IsFolderEmpty => !IsInitializingFolder && IsEmpty;
public bool HasNoOnlineSearchResult { get; private set; }
[ObservableProperty]
public partial bool IsInSearchMode { get; set; }
[ObservableProperty]
public partial bool IsOnlineSearchButtonVisible { get; set; }
2025-02-16 11:54:23 +01:00
[ObservableProperty]
public partial bool IsOnlineSearchEnabled { get; set; }
[ObservableProperty]
public partial bool AreSearchResultsOnline { get; set; }
2025-02-16 11:54:23 +01:00
#endregion
private async void ActiveMailItemChanged(MailItemViewModel selectedMailItemViewModel)
{
if (_activeMailItem == selectedMailItemViewModel) return;
2024-05-09 00:51:16 +02:00
2025-02-16 11:54:23 +01:00
// Don't update active mail item if Ctrl key is pressed or multi selection is enabled.
// User is probably trying to select multiple items.
// This is not the same behavior in Windows Mail,
// but it's a trash behavior.
2025-02-16 11:54:23 +01:00
var isCtrlKeyPressed = _keyPressService.IsCtrlKeyPressed();
2024-05-09 00:51:16 +02:00
2025-02-16 11:54:23 +01:00
bool isMultiSelecting = isCtrlKeyPressed || IsMultiSelectionModeEnabled;
2024-05-09 00:51:16 +02:00
2025-02-16 11:54:23 +01:00
if (isMultiSelecting && StatePersistenceService.IsReaderNarrowed)
{
return;
}
2024-05-09 00:51:16 +02:00
2025-02-16 11:54:23 +01:00
_activeMailItem = selectedMailItemViewModel;
2024-05-09 00:51:16 +02:00
2025-02-16 11:54:23 +01:00
Messenger.Send(new ActiveMailItemChangedEvent(_activeMailItem));
2024-05-09 00:51:16 +02:00
2025-02-16 11:54:23 +01:00
if (_activeMailItem == null || _activeMailItem.IsRead) return;
2024-05-09 00:51:16 +02:00
2025-02-16 11:54:23 +01:00
// Automatically set mark as read or not based on preferences.
2024-05-09 00:51:16 +02:00
2025-02-16 11:54:23 +01:00
var markAsPreference = PreferencesService.MarkAsPreference;
2025-02-16 11:54:23 +01:00
if (markAsPreference == MailMarkAsOption.WhenSelected)
{
var operation = MailOperation.MarkAsRead;
var package = new MailOperationPreperationRequest(operation, _activeMailItem.MailCopy);
2024-05-09 00:51:16 +02:00
2025-02-16 11:54:23 +01:00
if (ActiveFolder?.SpecialFolderType == SpecialFolderType.Unread &&
!gmailUnreadFolderMarkedAsReadUniqueIds.Contains(_activeMailItem.UniqueId))
2024-05-09 00:51:16 +02:00
{
2025-02-16 11:54:23 +01:00
gmailUnreadFolderMarkedAsReadUniqueIds.Add(_activeMailItem.UniqueId);
2024-05-09 00:51:16 +02:00
}
2025-02-16 11:54:23 +01:00
await ExecuteMailOperationAsync(package);
2024-04-18 01:44:37 +02:00
}
2025-02-16 11:54:23 +01:00
else if (markAsPreference == MailMarkAsOption.AfterDelay && PreferencesService.MarkAsDelay >= 0)
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
// TODO: Start a timer then queue.
2024-04-18 01:44:37 +02:00
}
2025-02-16 11:54:23 +01:00
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
public void NotifyItemSelected()
{
OnPropertyChanged(nameof(SelectedMessageText));
OnPropertyChanged(nameof(HasSingleItemSelection));
OnPropertyChanged(nameof(HasSelectedItems));
OnPropertyChanged(nameof(SelectedItemCount));
OnPropertyChanged(nameof(HasMultipleItemSelections));
if (SelectedFolderPivot != null)
SelectedFolderPivot.SelectedItemCount = SelectedItemCount;
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
private void NotifyItemFoundState()
{
OnPropertyChanged(nameof(IsEmpty));
OnPropertyChanged(nameof(IsFolderEmpty));
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
protected override void OnDispatcherAssigned()
{
base.OnDispatcherAssigned();
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
MailCollection.CoreDispatcher = Dispatcher;
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
private async void UpdateBarMessage(InfoBarMessageType severity, string title, string message)
{
await ExecuteUIThread(() =>
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
BarSeverity = severity;
BarTitle = title;
BarMessage = message;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
IsBarOpen = true;
});
}
2025-02-16 11:54:23 +01:00
private void SelectedItemCollectionUpdated(NotifyCollectionChangedEventArgs e)
{
if (SelectedItems.Count == 1)
{
ActiveMailItemChanged(SelectedItems[0]);
2024-04-18 01:44:37 +02:00
}
2025-02-16 11:54:23 +01:00
else
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
// At this point, either we don't have any item selected
// or we have multiple item selected. In either case
// there should be no active item.
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
ActiveMailItemChanged(null);
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
NotifyItemSelected();
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
SetupTopBarActions();
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
private async Task UpdateFolderPivotsAsync()
{
if (ActiveFolder == null) return;
2024-04-18 01:44:37 +02:00
PivotFolders.Clear();
SelectedFolderPivot = null;
2024-04-18 01:44:37 +02:00
if (IsInSearchMode)
2025-02-16 11:54:23 +01:00
{
var isFocused = SelectedFolderPivot?.IsFocused;
PivotFolders.Add(new FolderPivotViewModel(Translator.SearchPivotName, isFocused));
2024-04-18 01:44:37 +02:00
}
else
{
// Merged folders don't support focused feature.
if (ActiveFolder is IMergedAccountFolderMenuItem)
2025-02-16 11:54:23 +01:00
{
PivotFolders.Add(new FolderPivotViewModel(ActiveFolder.FolderName, null));
2025-02-16 11:54:23 +01:00
}
else if (ActiveFolder is IFolderMenuItem singleFolderMenuItem)
2025-02-16 11:54:23 +01:00
{
var parentAccount = singleFolderMenuItem.ParentAccount;
bool isFocusedInboxEnabled = await _accountService.IsAccountFocusedEnabledAsync(parentAccount.Id);
bool isInboxFolder = ActiveFolder.SpecialFolderType == SpecialFolderType.Inbox;
// Folder supports Focused - Other
if (isInboxFolder && isFocusedInboxEnabled)
{
// Can be passed as empty string. Focused - Other will be used regardless.
var focusedItem = new FolderPivotViewModel(string.Empty, true);
var otherItem = new FolderPivotViewModel(string.Empty, false);
PivotFolders.Add(focusedItem);
PivotFolders.Add(otherItem);
}
else
{
// If the account and folder doesn't support focused feature, just add itself.
PivotFolders.Add(new FolderPivotViewModel(singleFolderMenuItem.FolderName, null));
}
2025-02-16 11:54:23 +01:00
}
}
2025-02-16 11:54:23 +01:00
// This will trigger refresh.
SelectedFolderPivot = PivotFolders.FirstOrDefault();
}
2025-02-16 11:54:23 +01:00
#region Commands
2025-02-16 11:54:23 +01:00
[RelayCommand]
public Task ExecuteHoverAction(MailOperationPreperationRequest request) => ExecuteMailOperationAsync(request);
2025-02-16 11:54:23 +01:00
[RelayCommand]
private async Task ExecuteTopBarAction(MailOperationMenuItem menuItem)
{
if (menuItem == null || !SelectedItems.Any()) return;
2025-02-16 11:54:23 +01:00
await HandleMailOperation(menuItem.Operation, SelectedItems);
}
2025-02-16 11:54:23 +01:00
/// <summary>
/// Executes the requested mail operation for currently selected items.
/// </summary>
/// <param name="operation">Action to execute for selected items.</param>
[RelayCommand]
private async Task ExecuteMailOperation(MailOperation mailOperation)
{
if (!SelectedItems.Any()) return;
2025-02-16 11:54:23 +01:00
await HandleMailOperation(mailOperation, SelectedItems);
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
private async Task HandleMailOperation(MailOperation mailOperation, IEnumerable<MailItemViewModel> mailItems)
{
if (!mailItems.Any()) return;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
var package = new MailOperationPreperationRequest(mailOperation, mailItems.Select(a => a.MailCopy));
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
await ExecuteMailOperationAsync(package);
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
/// <summary>
/// Sens a new message to synchronize current folder.
/// </summary>
[RelayCommand]
private void SyncFolder()
{
if (!CanSynchronize) return;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// Only synchronize listed folders.
2025-02-16 11:54:23 +01:00
// When doing linked inbox sync, we need to save the sync id to report progress back only once.
// Otherwise, we will report progress for each folder and that's what we don't want.
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
trackingSynchronizationId = Guid.NewGuid();
completedTrackingSynchronizationCount = 0;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
foreach (var folder in ActiveFolder.HandlingFolders)
{
2025-02-16 11:54:23 +01:00
var options = new MailSynchronizationOptions()
{
AccountId = folder.MailAccountId,
Type = MailSynchronizationType.CustomFolders,
SynchronizationFolderIds = [folder.Id],
GroupedSynchronizationTrackingId = trackingSynchronizationId
};
2024-05-08 23:59:50 +02:00
2025-02-16 11:54:23 +01:00
Messenger.Send(new NewMailSynchronizationRequested(options, SynchronizationSource.Client));
}
2025-02-16 11:54:23 +01:00
}
2024-05-08 23:59:50 +02:00
2025-02-16 11:54:23 +01:00
[RelayCommand]
private async Task SelectedPivotChanged()
{
if (isChangingFolder) return;
2024-05-08 23:59:50 +02:00
2025-02-16 11:54:23 +01:00
await InitializeFolderAsync();
}
2024-05-08 23:59:50 +02:00
2025-02-16 11:54:23 +01:00
[RelayCommand]
private async Task SelectedSortingChanged(SortingOption option)
{
SelectedSortingOption = option;
2024-05-08 23:59:50 +02:00
2025-02-16 11:54:23 +01:00
if (isChangingFolder) return;
2025-02-16 11:35:43 +01:00
2025-02-16 11:54:23 +01:00
await InitializeFolderAsync();
}
2025-02-16 11:35:43 +01:00
2025-02-16 11:54:23 +01:00
[RelayCommand]
private async Task SelectedFilterChanged(FilterOption option)
{
SelectedFilterOption = option;
2025-02-16 11:54:23 +01:00
if (isChangingFolder) return;
await InitializeFolderAsync();
}
[RelayCommand]
public async Task PerformSearchAsync()
{
IsOnlineSearchEnabled = false;
AreSearchResultsOnline = false;
IsInSearchMode = !string.IsNullOrEmpty(SearchQuery);
if (IsInSearchMode)
{
IsOnlineSearchButtonVisible = false;
}
await UpdateFolderPivotsAsync();
2025-02-16 11:54:23 +01:00
}
2025-02-16 11:54:23 +01:00
[RelayCommand]
private async Task EnableFolderSynchronizationAsync()
{
if (ActiveFolder == null) return;
2025-02-16 11:54:23 +01:00
foreach (var folder in ActiveFolder.HandlingFolders)
{
await _folderService.ChangeFolderSynchronizationStateAsync(folder.Id, true);
}
2025-02-16 11:54:23 +01:00
}
2025-02-16 11:54:23 +01:00
[RelayCommand]
private async Task LoadMoreItemsAsync()
{
if (IsInitializingFolder || IsOnlineSearchEnabled) return;
2025-02-16 11:54:23 +01:00
await ExecuteUIThread(() => { IsInitializingFolder = true; });
2024-05-09 00:51:16 +02:00
2025-02-16 11:54:23 +01:00
var initializationOptions = new MailListInitializationOptions(ActiveFolder.HandlingFolders,
SelectedFilterOption.Type,
SelectedSortingOption.Type,
PreferencesService.IsThreadingEnabled,
SelectedFolderPivot.IsFocused,
IsInSearchMode ? SearchQuery : string.Empty,
MailCollection.MailCopyIdHashSet);
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
var items = await _mailService.FetchMailsAsync(initializationOptions).ConfigureAwait(false);
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
var viewModels = PrepareMailViewModels(items);
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
await ExecuteUIThread(() => { MailCollection.AddRange(viewModels, clearIdCache: false); });
await ExecuteUIThread(() => { IsInitializingFolder = false; });
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
#endregion
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
public Task ExecuteMailOperationAsync(MailOperationPreperationRequest package) => _winoRequestDelegator.ExecuteAsync(package);
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
public IEnumerable<MailItemViewModel> GetTargetMailItemViewModels(IMailItem clickedItem)
{
// Threat threads as a whole and include everything in the group. Except single selections outside of the thread.
IEnumerable<MailItemViewModel> contextMailItems = null;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
if (clickedItem is ThreadMailItemViewModel clickedThreadItem)
{
// Clicked item is a thread.
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
clickedThreadItem.IsThreadExpanded = true;
contextMailItems = clickedThreadItem.ThreadItems.Cast<MailItemViewModel>();
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// contextMailItems = clickedThreadItem.GetMailCopies();
}
else if (clickedItem is MailItemViewModel clickedMailItemViewModel)
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
// If the clicked item is included in SelectedItems, then we need to thing them as whole.
// If there are selected items, but clicked item is not one of them, then it's a single context menu.
bool includedInSelectedItems = SelectedItems.Contains(clickedItem);
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
if (includedInSelectedItems)
contextMailItems = SelectedItems;
else
contextMailItems = [clickedMailItemViewModel];
2024-04-18 01:44:37 +02:00
}
2025-02-16 11:54:23 +01:00
return contextMailItems;
}
Full trust Wino Server implementation. (#295) * Separation of messages. Introducing Wino.Messages library. * Wino.Server and Wino.Packaging projects. Enabling full trust for UWP and app service connection manager basics. * Remove debug code. * Enable generating assembly info to deal with unsupported os platform warnings. * Fix server-client connection. * UIMessage communication. Single instancing for server and re-connection mechanism on suspension. * Removed IWinoSynchronizerFactory from UWP project. * Removal of background task service from core. * Delegating changes to UI and triggering new background synchronization. * Fix build error. * Moved core lib messages to Messaging project. * Better client-server communication. Handling of requests in the server. New synchronizer factory in the server. * WAM broker and MSAL token caching for OutlookAuthenticator. Handling account creation for Outlook. * WinoServerResponse basics. * Delegating protocol activation for Gmail authenticator. * Adding margin to searchbox to match action bar width. * Move libraries into lib folder. * Storing base64 encoded mime on draft creation instead of MimeMessage object. Fixes serialization/deserialization issue with S.T.Json * Scrollbar adjustments * WınoExpander for thread expander layout ıssue. * Handling synchronizer state changes. * Double init on background activation. * FIxing packaging issues and new Wino Mail launcher protocol for activation from full thrust process. * Remove debug deserialization. * Remove debug code. * Making sure the server connection is established when the app is launched. * Thrust -> Trust string replacement... * Rename package to Wino Mail * Enable translated values in the server. * Fixed an issue where toast activation can't find the clicked mail after the folder is initialized. * Revert debug code. * Change server background sync to every 3 minute and Inbox only synchronization. * Revert google auth changes. * App preferences page. * Changing tray icon visibility on preference change. * Start the server with invisible tray icon if set to invisible. * Reconnect button on the title bar. * Handling of toast actions. * Enable x86 build for server during packaging. * Get rid of old background tasks and v180 migration. * Terminate client when Exit clicked in server. * Introducing SynchronizationSource to prevent notifying UI after server tick synchronization. * Remove confirmAppClose restricted capability and unused debug code in manifest. * Closing the reconnect info popup when reconnect is clicked. * Custom RetryHandler for OutlookSynchronizer and separating client/server logs. * Running server on Windows startup. * Fix startup exe. * Fix for expander list view item paddings. * Force full sync on app launch instead of Inbox. * Fix draft creation. * Fix an issue with custom folder sync logic. * Reporting back account sync progress from server. * Fix sending drafts and missing notifications for imap. * Changing imap folder sync requirements. * Retain file count is set to 3. * Disabled swipe gestures temporarily due to native crash with SwipeControl * Save all attachments implementation. * Localization for save all attachments button. * Fix logging dates for logs. * Fixing ARM64 build. * Add ARM64 build config to packaging project. * Comment out OutOfProcPDB for ARM64. * Hnadling GONE response for Outlook folder synchronization.
2024-08-05 00:36:26 +02:00
2025-02-16 11:54:23 +01:00
public IEnumerable<MailOperationMenuItem> GetAvailableMailActions(IEnumerable<IMailItem> contextMailItems)
=> _contextMenuItemService.GetMailItemContextMenuActions(contextMailItems);
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
public void ChangeCustomFocusedState(IEnumerable<IMailItem> mailItems, bool isFocused)
=> mailItems.OfType<MailItemViewModel>().ForEach(a => a.IsCustomFocused = isFocused);
2025-02-16 11:54:23 +01:00
private bool ShouldPreventItemAdd(IMailItem mailItem)
{
bool condition = mailItem.IsRead
&& SelectedFilterOption.Type == FilterOptionType.Unread
|| !mailItem.IsFlagged
&& SelectedFilterOption.Type == FilterOptionType.Flagged;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
return condition;
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
protected override async void OnMailAdded(MailCopy addedMail)
{
base.OnMailAdded(addedMail);
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
if (addedMail.AssignedAccount == null || addedMail.AssignedFolder == null) return;
2025-02-16 11:54:23 +01:00
try
{
if (ActiveFolder == null) return;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// At least one of the accounts we are listing must match with the account of the added mail.
if (!ActiveFolder.HandlingFolders.Any(a => a.MailAccountId == addedMail.AssignedAccount.Id)) return;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// Messages coming to sent or draft folder must be inserted regardless of the filter.
bool shouldPreventIgnoringFilter = addedMail.AssignedFolder.SpecialFolderType == SpecialFolderType.Draft ||
addedMail.AssignedFolder.SpecialFolderType == SpecialFolderType.Sent;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// Item does not belong to this folder and doesn't have special type to be inserted.
if (!shouldPreventIgnoringFilter && !ActiveFolder.HandlingFolders.Any(a => a.Id == addedMail.AssignedFolder.Id)) return;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// Item should be prevented from being added to the list due to filter.
if (!shouldPreventIgnoringFilter && ShouldPreventItemAdd(addedMail)) return;
2024-08-31 13:25:55 +02:00
2025-02-16 11:54:23 +01:00
await listManipulationSemepahore.WaitAsync();
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
await MailCollection.AddAsync(addedMail);
2025-02-16 11:54:23 +01:00
await ExecuteUIThread(() => { NotifyItemFoundState(); });
}
catch { }
finally
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
listManipulationSemepahore.Release();
}
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
protected override async void OnMailUpdated(MailCopy updatedMail)
{
base.OnMailUpdated(updatedMail);
Folder operations, Gmail folder sync improvements and rework of menu items. (#273) * New rename folder dialog keys. * Insfra work for folder operations and rename folder code. * RenameFolder for Gmail. * Fixed input dialog to take custom take for primary button. * Missing rename for DS call. * Outlook to throw exception in case of error. * Implemented rename folder functionality for Outlook. * Remove default primary text from input dialog. * Fixed an issue where outlook folder rename does not work. * Disable vertical scroll for composing page editor items. * Fixing some issues with imap folder sync. * fix copy pasta * TODO folder update/removed overrides for shell. * New rename folder dialog keys. * Insfra work for folder operations and rename folder code. * RenameFolder for Gmail. * Fixed input dialog to take custom take for primary button. * Missing rename for DS call. * Outlook to throw exception in case of error. * Implemented rename folder functionality for Outlook. * Remove default primary text from input dialog. * Fixed an issue where outlook folder rename does not work. * Disable vertical scroll for composing page editor items. * Fixing some issues with imap folder sync. * fix copy pasta * TODO folder update/removed overrides for shell. * New rename folder dialog keys. * Insfra work for folder operations and rename folder code. * RenameFolder for Gmail. * Fixed input dialog to take custom take for primary button. * Missing rename for DS call. * Outlook to throw exception in case of error. * Implemented rename folder functionality for Outlook. * Remove default primary text from input dialog. * Fixed an issue where outlook folder rename does not work. * Disable vertical scroll for composing page editor items. * Fixing some issues with imap folder sync. * fix copy pasta * TODO folder update/removed overrides for shell. * New rename folder dialog keys. * Fixed an issue where redundant older updates causing pivots to be re-created. * New empty folder request * New rename folder dialog keys. * Insfra work for folder operations and rename folder code. * RenameFolder for Gmail. * Fixed input dialog to take custom take for primary button. * Missing rename for DS call. * Outlook to throw exception in case of error. * Implemented rename folder functionality for Outlook. * Remove default primary text from input dialog. * Fixed an issue where outlook folder rename does not work. * Fixing some issues with imap folder sync. * fix copy pasta * TODO folder update/removed overrides for shell. * New rename folder dialog keys. * New rename folder dialog keys. * New rename folder dialog keys. * Fixed an issue where redundant older updates causing pivots to be re-created. * New empty folder request * Enable empty folder on base sync. * Move updates on event listeners. * Remove folder UI messages. * Reworked folder synchronization for gmail. * Loading folders on the fly as the selected account changed instead of relying on cached menu items. * Merged account folder items, re-navigating to existing rendering page. * - Reworked merged account menu system. - Reworked unread item count loadings. - Fixed back button visibility. - Instant rendering of mails if renderer is active. - Animation fixes. - Menu item re-load crash/hang fixes. * Handle folder renaming on the UI. * Empty folder for all synchronizers. * New execution delay mechanism and handling folder mark as read for all synchronizers. * Revert UI changes on failure for IMAP. * Remove duplicate translation keys. * Cleanup.
2024-07-09 01:05:16 +02:00
2025-02-16 11:54:23 +01:00
Debug.WriteLine($"Updating {updatedMail.Id}-> {updatedMail.UniqueId}");
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
await MailCollection.UpdateMailCopy(updatedMail);
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
await ExecuteUIThread(() => { SetupTopBarActions(); });
}
2025-02-16 11:54:23 +01:00
protected override async void OnMailRemoved(MailCopy removedMail)
{
base.OnMailRemoved(removedMail);
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
if (removedMail.AssignedAccount == null || removedMail.AssignedFolder == null) return;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// We should delete the items only if:
// 1. They are deleted from the active folder.
// 2. Deleted from draft or sent folder.
// 3. Removal is not caused by Gmail Unread folder action.
// Delete/sent are special folders that can list their items in other folders.
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
bool removedFromActiveFolder = ActiveFolder.HandlingFolders.Any(a => a.Id == removedMail.AssignedFolder.Id);
bool removedFromDraftOrSent = removedMail.AssignedFolder.SpecialFolderType == SpecialFolderType.Draft ||
removedMail.AssignedFolder.SpecialFolderType == SpecialFolderType.Sent;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
bool isDeletedByGmailUnreadFolderAction = ActiveFolder.SpecialFolderType == SpecialFolderType.Unread &&
gmailUnreadFolderMarkedAsReadUniqueIds.Contains(removedMail.UniqueId);
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
if ((removedFromActiveFolder || removedFromDraftOrSent) && !isDeletedByGmailUnreadFolderAction)
{
bool isDeletedMailSelected = SelectedItems.Any(a => a.MailCopy.UniqueId == removedMail.UniqueId);
2025-02-16 11:35:43 +01:00
2025-02-16 11:54:23 +01:00
// Automatically select the next item in the list if the setting is enabled.
MailItemViewModel nextItem = null;
if (isDeletedMailSelected && PreferencesService.AutoSelectNextItem)
2024-04-18 01:44:37 +02:00
{
await ExecuteUIThread(() =>
{
nextItem = MailCollection.GetNextItem(removedMail);
});
2024-04-18 01:44:37 +02:00
}
2025-02-16 11:54:23 +01:00
// Remove the deleted item from the list.
await MailCollection.RemoveAsync(removedMail);
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
if (nextItem != null)
WeakReferenceMessenger.Default.Send(new SelectMailItemContainerEvent(nextItem, ScrollToItem: true));
else if (isDeletedMailSelected)
{
2025-02-16 11:54:23 +01:00
// There are no next item to select, but we removed the last item which was selected.
// Clearing selected item will dispose rendering page.
2025-02-16 11:35:43 +01:00
2025-02-16 11:54:23 +01:00
SelectedItems.Clear();
}
2025-02-16 11:54:23 +01:00
await ExecuteUIThread(() => { NotifyItemFoundState(); });
}
else if (isDeletedByGmailUnreadFolderAction)
2025-02-16 11:35:43 +01:00
{
2025-02-16 11:54:23 +01:00
// Remove the entry from the set so we can listen to actual deletes next time.
gmailUnreadFolderMarkedAsReadUniqueIds.Remove(removedMail.UniqueId);
2025-02-16 11:35:43 +01:00
}
2025-02-16 11:54:23 +01:00
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
protected override async void OnDraftCreated(MailCopy draftMail, MailAccount account)
{
base.OnDraftCreated(draftMail, account);
try
2025-02-16 11:35:43 +01:00
{
2025-02-16 11:54:23 +01:00
// If the draft is created in another folder, we need to wait for that folder to be initialized.
// Otherwise the draft mail item will be duplicated on the next add execution.
await listManipulationSemepahore.WaitAsync();
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// Create the item. Draft folder navigation is already done at this point.
await MailCollection.AddAsync(draftMail);
await ExecuteUIThread(() =>
{
2025-02-16 11:54:23 +01:00
// New draft is created by user. Select the item.
Messenger.Send(new MailItemNavigationRequested(draftMail.UniqueId, ScrollToItem: true));
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
NotifyItemFoundState();
});
}
finally
{
listManipulationSemepahore.Release();
}
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
private IEnumerable<IMailItem> PrepareMailViewModels(IEnumerable<IMailItem> mailItems)
{
foreach (var item in mailItems)
{
if (item is MailCopy singleMailItem)
yield return new MailItemViewModel(singleMailItem);
else if (item is ThreadMailItem threadMailItem)
yield return new ThreadMailItemViewModel(threadMailItem);
}
}
2024-04-18 01:44:37 +02:00
[RelayCommand]
private async Task PerformOnlineSearchAsync()
{
IsOnlineSearchButtonVisible = false;
IsOnlineSearchEnabled = true;
await InitializeFolderAsync();
}
2025-02-16 11:54:23 +01:00
private async Task InitializeFolderAsync()
{
if (SelectedFilterOption == null || SelectedFolderPivot == null || SelectedSortingOption == null)
return;
2025-02-16 11:54:23 +01:00
try
{
MailCollection.Clear();
MailCollection.MailCopyIdHashSet.Clear();
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
SelectedItems.Clear();
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
if (ActiveFolder == null)
return;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
await ExecuteUIThread(() => { IsInitializingFolder = true; });
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// Folder is changed during initialization.
// Just cancel the existing one and wait for new initialization.
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
if (!listManipulationCancellationTokenSource.IsCancellationRequested)
{
listManipulationCancellationTokenSource.Cancel();
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
listManipulationCancellationTokenSource = new CancellationTokenSource();
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
var cancellationToken = listManipulationCancellationTokenSource.Token;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
await listManipulationSemepahore.WaitAsync(cancellationToken);
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// Setup MailCollection configuration.
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// Don't pass any threading strategy if disabled in settings.
MailCollection.ThreadingStrategyProvider = PreferencesService.IsThreadingEnabled ? _threadingStrategyProvider : null;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// TODO: This should go inside
MailCollection.PruneSingleNonDraftItems = ActiveFolder.SpecialFolderType == SpecialFolderType.Draft;
2025-02-16 11:35:43 +01:00
2025-02-16 11:54:23 +01:00
// Here items are sorted and filtered.
2024-04-18 01:44:37 +02:00
List<IMailItem> items = null;
List<MailCopy> onlineSearchItems = null;
bool isDoingSearch = !string.IsNullOrEmpty(SearchQuery);
bool isDoingOnlineSearch = false;
if (isDoingSearch)
{
isDoingOnlineSearch = PreferencesService.DefaultSearchMode == SearchMode.Online || IsOnlineSearchEnabled;
// Perform online search.
if (isDoingOnlineSearch)
{
WinoServerResponse<OnlineSearchResult> onlineSearchResult = null;
string onlineSearchFailedMessage = null;
try
{
var accountIds = ActiveFolder.HandlingFolders.Select(a => a.MailAccountId).ToList();
var folders = ActiveFolder.HandlingFolders.ToList();
var searchRequest = new OnlineSearchRequested(accountIds, SearchQuery, folders);
onlineSearchResult = await _winoServerConnectionManager.GetResponseAsync<OnlineSearchResult, OnlineSearchRequested>(searchRequest, cancellationToken);
if (onlineSearchResult.IsSuccess)
{
await ExecuteUIThread(() => { AreSearchResultsOnline = true; });
onlineSearchItems = onlineSearchResult.Data.SearchResult;
}
else
{
onlineSearchFailedMessage = onlineSearchResult.Message;
}
}
catch (OperationCanceledException)
{
throw;
}
catch (Exception ex)
{
Log.Warning(ex, "Failed to perform online search.");
onlineSearchFailedMessage = ex.Message;
}
if (onlineSearchResult != null && !onlineSearchResult.IsSuccess)
{
// Query or server error.
var serverErrorMessage = string.Format(Translator.OnlineSearchFailed_Message, onlineSearchResult.Message);
_mailDialogService.InfoBarMessage(Translator.GeneralTitle_Error, serverErrorMessage, InfoBarMessageType.Warning);
}
else if (!string.IsNullOrEmpty(onlineSearchFailedMessage))
{
// Fatal error.
var serverErrorMessage = string.Format(Translator.OnlineSearchFailed_Message, onlineSearchFailedMessage);
_mailDialogService.InfoBarMessage(Translator.GeneralTitle_Error, serverErrorMessage, InfoBarMessageType.Warning);
}
}
}
2025-02-16 11:54:23 +01:00
var initializationOptions = new MailListInitializationOptions(ActiveFolder.HandlingFolders,
SelectedFilterOption.Type,
SelectedSortingOption.Type,
PreferencesService.IsThreadingEnabled,
SelectedFolderPivot.IsFocused,
SearchQuery,
MailCollection.MailCopyIdHashSet,
onlineSearchItems);
2024-04-18 01:44:37 +02:00
items = await _mailService.FetchMailsAsync(initializationOptions, cancellationToken).ConfigureAwait(false);
2025-02-16 11:54:23 +01:00
if (!listManipulationCancellationTokenSource.IsCancellationRequested)
{
2025-02-16 11:54:23 +01:00
// Here they are already threaded if needed.
// We don't need to insert them one by one.
// Just create VMs and do bulk insert.
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
var viewModels = PrepareMailViewModels(items);
2025-02-16 11:35:43 +01:00
await ExecuteUIThread(() =>
{
MailCollection.AddRange(viewModels, true);
if (isDoingSearch && !isDoingOnlineSearch)
{
IsOnlineSearchButtonVisible = true;
}
});
}
2025-02-16 11:35:43 +01:00
}
2025-02-16 11:54:23 +01:00
catch (OperationCanceledException)
{
2025-02-16 11:54:23 +01:00
Debug.WriteLine("Initialization of mails canceled.");
2025-02-16 11:35:43 +01:00
}
2025-02-16 11:54:23 +01:00
catch (Exception ex)
{
2025-02-16 11:54:23 +01:00
Debugger.Break();
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
if (IsInSearchMode)
Log.Error(ex, "Failed to perform search.");
else
Log.Error(ex, "Failed to refresh listed mails.");
}
finally
{
2025-02-16 11:54:23 +01:00
listManipulationSemepahore.Release();
2025-02-16 11:54:23 +01:00
await ExecuteUIThread(() =>
{
IsInitializingFolder = false;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
OnPropertyChanged(nameof(CanSynchronize));
NotifyItemFoundState();
});
}
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
#region Receivers
2025-02-16 11:54:23 +01:00
void IRecipient<MailItemSelectedEvent>.Receive(MailItemSelectedEvent message)
{
if (!SelectedItems.Contains(message.SelectedMailItem)) SelectedItems.Add(message.SelectedMailItem);
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
void IRecipient<MailItemSelectionRemovedEvent>.Receive(MailItemSelectionRemovedEvent message)
{
if (SelectedItems.Contains(message.RemovedMailItem)) SelectedItems.Remove(message.RemovedMailItem);
}
Full trust Wino Server implementation. (#295) * Separation of messages. Introducing Wino.Messages library. * Wino.Server and Wino.Packaging projects. Enabling full trust for UWP and app service connection manager basics. * Remove debug code. * Enable generating assembly info to deal with unsupported os platform warnings. * Fix server-client connection. * UIMessage communication. Single instancing for server and re-connection mechanism on suspension. * Removed IWinoSynchronizerFactory from UWP project. * Removal of background task service from core. * Delegating changes to UI and triggering new background synchronization. * Fix build error. * Moved core lib messages to Messaging project. * Better client-server communication. Handling of requests in the server. New synchronizer factory in the server. * WAM broker and MSAL token caching for OutlookAuthenticator. Handling account creation for Outlook. * WinoServerResponse basics. * Delegating protocol activation for Gmail authenticator. * Adding margin to searchbox to match action bar width. * Move libraries into lib folder. * Storing base64 encoded mime on draft creation instead of MimeMessage object. Fixes serialization/deserialization issue with S.T.Json * Scrollbar adjustments * WınoExpander for thread expander layout ıssue. * Handling synchronizer state changes. * Double init on background activation. * FIxing packaging issues and new Wino Mail launcher protocol for activation from full thrust process. * Remove debug deserialization. * Remove debug code. * Making sure the server connection is established when the app is launched. * Thrust -> Trust string replacement... * Rename package to Wino Mail * Enable translated values in the server. * Fixed an issue where toast activation can't find the clicked mail after the folder is initialized. * Revert debug code. * Change server background sync to every 3 minute and Inbox only synchronization. * Revert google auth changes. * App preferences page. * Changing tray icon visibility on preference change. * Start the server with invisible tray icon if set to invisible. * Reconnect button on the title bar. * Handling of toast actions. * Enable x86 build for server during packaging. * Get rid of old background tasks and v180 migration. * Terminate client when Exit clicked in server. * Introducing SynchronizationSource to prevent notifying UI after server tick synchronization. * Remove confirmAppClose restricted capability and unused debug code in manifest. * Closing the reconnect info popup when reconnect is clicked. * Custom RetryHandler for OutlookSynchronizer and separating client/server logs. * Running server on Windows startup. * Fix startup exe. * Fix for expander list view item paddings. * Force full sync on app launch instead of Inbox. * Fix draft creation. * Fix an issue with custom folder sync logic. * Reporting back account sync progress from server. * Fix sending drafts and missing notifications for imap. * Changing imap folder sync requirements. * Retain file count is set to 3. * Disabled swipe gestures temporarily due to native crash with SwipeControl * Save all attachments implementation. * Localization for save all attachments button. * Fix logging dates for logs. * Fixing ARM64 build. * Add ARM64 build config to packaging project. * Comment out OutOfProcPDB for ARM64. * Hnadling GONE response for Outlook folder synchronization.
2024-08-05 00:36:26 +02:00
2025-02-16 11:54:23 +01:00
async void IRecipient<ActiveMailFolderChangedEvent>.Receive(ActiveMailFolderChangedEvent message)
{
NotifyItemSelected();
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
isChangingFolder = true;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
ActiveFolder = message.BaseFolderMenuItem;
gmailUnreadFolderMarkedAsReadUniqueIds.Clear();
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
trackingSynchronizationId = null;
completedTrackingSynchronizationCount = 0;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// Notify change for archive-unarchive app bar button.
OnPropertyChanged(nameof(IsArchiveSpecialFolder));
2024-04-18 01:44:37 +02:00
IsInSearchMode = false;
IsOnlineSearchButtonVisible = false;
AreSearchResultsOnline = false;
2025-02-16 11:54:23 +01:00
// Prepare Focused - Other or folder name tabs.
await UpdateFolderPivotsAsync();
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// Reset filters and sorting options.
ResetFilters();
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
await InitializeFolderAsync();
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// TODO: This should be done in a better way.
while (IsInitializingFolder)
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
await Task.Delay(100);
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// Check whether the account synchronizer that this folder belongs to is already in synchronization.
await CheckIfAccountIsSynchronizingAsync();
2025-02-16 11:54:23 +01:00
// Let awaiters know about the completion of mail init.
message.FolderInitLoadAwaitTask?.TrySetResult(true);
2025-02-16 11:54:23 +01:00
await Task.Yield();
2025-02-16 11:54:23 +01:00
isChangingFolder = false;
2025-02-16 11:54:23 +01:00
void ResetFilters()
{
// Expected that FilterOptions and SortingOptions have default value in 0 index.
SelectedFilterOption = FilterOptions[0];
SelectedSortingOption = SortingOptions[0];
SearchQuery = string.Empty;
IsInSearchMode = false;
IsOnlineSearchEnabled = false;
2025-02-16 11:54:23 +01:00
}
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
public void Receive(AccountSynchronizationCompleted message)
{
if (ActiveFolder == null) return;
2025-02-16 11:54:23 +01:00
bool isLinkedInboxSyncResult = message.SynchronizationTrackingId == trackingSynchronizationId;
2025-02-16 11:54:23 +01:00
if (isLinkedInboxSyncResult)
Folder operations, Gmail folder sync improvements and rework of menu items. (#273) * New rename folder dialog keys. * Insfra work for folder operations and rename folder code. * RenameFolder for Gmail. * Fixed input dialog to take custom take for primary button. * Missing rename for DS call. * Outlook to throw exception in case of error. * Implemented rename folder functionality for Outlook. * Remove default primary text from input dialog. * Fixed an issue where outlook folder rename does not work. * Disable vertical scroll for composing page editor items. * Fixing some issues with imap folder sync. * fix copy pasta * TODO folder update/removed overrides for shell. * New rename folder dialog keys. * Insfra work for folder operations and rename folder code. * RenameFolder for Gmail. * Fixed input dialog to take custom take for primary button. * Missing rename for DS call. * Outlook to throw exception in case of error. * Implemented rename folder functionality for Outlook. * Remove default primary text from input dialog. * Fixed an issue where outlook folder rename does not work. * Disable vertical scroll for composing page editor items. * Fixing some issues with imap folder sync. * fix copy pasta * TODO folder update/removed overrides for shell. * New rename folder dialog keys. * Insfra work for folder operations and rename folder code. * RenameFolder for Gmail. * Fixed input dialog to take custom take for primary button. * Missing rename for DS call. * Outlook to throw exception in case of error. * Implemented rename folder functionality for Outlook. * Remove default primary text from input dialog. * Fixed an issue where outlook folder rename does not work. * Disable vertical scroll for composing page editor items. * Fixing some issues with imap folder sync. * fix copy pasta * TODO folder update/removed overrides for shell. * New rename folder dialog keys. * Fixed an issue where redundant older updates causing pivots to be re-created. * New empty folder request * New rename folder dialog keys. * Insfra work for folder operations and rename folder code. * RenameFolder for Gmail. * Fixed input dialog to take custom take for primary button. * Missing rename for DS call. * Outlook to throw exception in case of error. * Implemented rename folder functionality for Outlook. * Remove default primary text from input dialog. * Fixed an issue where outlook folder rename does not work. * Fixing some issues with imap folder sync. * fix copy pasta * TODO folder update/removed overrides for shell. * New rename folder dialog keys. * New rename folder dialog keys. * New rename folder dialog keys. * Fixed an issue where redundant older updates causing pivots to be re-created. * New empty folder request * Enable empty folder on base sync. * Move updates on event listeners. * Remove folder UI messages. * Reworked folder synchronization for gmail. * Loading folders on the fly as the selected account changed instead of relying on cached menu items. * Merged account folder items, re-navigating to existing rendering page. * - Reworked merged account menu system. - Reworked unread item count loadings. - Fixed back button visibility. - Instant rendering of mails if renderer is active. - Animation fixes. - Menu item re-load crash/hang fixes. * Handle folder renaming on the UI. * Empty folder for all synchronizers. * New execution delay mechanism and handling folder mark as read for all synchronizers. * Revert UI changes on failure for IMAP. * Remove duplicate translation keys. * Cleanup.
2024-07-09 01:05:16 +02:00
{
2025-02-16 11:54:23 +01:00
var isCompletedAccountListed = ActiveFolder.HandlingFolders.Any(a => a.MailAccountId == message.AccountId);
Folder operations, Gmail folder sync improvements and rework of menu items. (#273) * New rename folder dialog keys. * Insfra work for folder operations and rename folder code. * RenameFolder for Gmail. * Fixed input dialog to take custom take for primary button. * Missing rename for DS call. * Outlook to throw exception in case of error. * Implemented rename folder functionality for Outlook. * Remove default primary text from input dialog. * Fixed an issue where outlook folder rename does not work. * Disable vertical scroll for composing page editor items. * Fixing some issues with imap folder sync. * fix copy pasta * TODO folder update/removed overrides for shell. * New rename folder dialog keys. * Insfra work for folder operations and rename folder code. * RenameFolder for Gmail. * Fixed input dialog to take custom take for primary button. * Missing rename for DS call. * Outlook to throw exception in case of error. * Implemented rename folder functionality for Outlook. * Remove default primary text from input dialog. * Fixed an issue where outlook folder rename does not work. * Disable vertical scroll for composing page editor items. * Fixing some issues with imap folder sync. * fix copy pasta * TODO folder update/removed overrides for shell. * New rename folder dialog keys. * Insfra work for folder operations and rename folder code. * RenameFolder for Gmail. * Fixed input dialog to take custom take for primary button. * Missing rename for DS call. * Outlook to throw exception in case of error. * Implemented rename folder functionality for Outlook. * Remove default primary text from input dialog. * Fixed an issue where outlook folder rename does not work. * Disable vertical scroll for composing page editor items. * Fixing some issues with imap folder sync. * fix copy pasta * TODO folder update/removed overrides for shell. * New rename folder dialog keys. * Fixed an issue where redundant older updates causing pivots to be re-created. * New empty folder request * New rename folder dialog keys. * Insfra work for folder operations and rename folder code. * RenameFolder for Gmail. * Fixed input dialog to take custom take for primary button. * Missing rename for DS call. * Outlook to throw exception in case of error. * Implemented rename folder functionality for Outlook. * Remove default primary text from input dialog. * Fixed an issue where outlook folder rename does not work. * Fixing some issues with imap folder sync. * fix copy pasta * TODO folder update/removed overrides for shell. * New rename folder dialog keys. * New rename folder dialog keys. * New rename folder dialog keys. * Fixed an issue where redundant older updates causing pivots to be re-created. * New empty folder request * Enable empty folder on base sync. * Move updates on event listeners. * Remove folder UI messages. * Reworked folder synchronization for gmail. * Loading folders on the fly as the selected account changed instead of relying on cached menu items. * Merged account folder items, re-navigating to existing rendering page. * - Reworked merged account menu system. - Reworked unread item count loadings. - Fixed back button visibility. - Instant rendering of mails if renderer is active. - Animation fixes. - Menu item re-load crash/hang fixes. * Handle folder renaming on the UI. * Empty folder for all synchronizers. * New execution delay mechanism and handling folder mark as read for all synchronizers. * Revert UI changes on failure for IMAP. * Remove duplicate translation keys. * Cleanup.
2024-07-09 01:05:16 +02:00
2025-02-16 11:54:23 +01:00
if (isCompletedAccountListed) completedTrackingSynchronizationCount++;
2025-02-16 11:54:23 +01:00
// Group sync is started but not all folders are synchronized yet. Don't report progress.
if (completedTrackingSynchronizationCount < ActiveFolder.HandlingFolders.Count()) return;
}
Folder operations, Gmail folder sync improvements and rework of menu items. (#273) * New rename folder dialog keys. * Insfra work for folder operations and rename folder code. * RenameFolder for Gmail. * Fixed input dialog to take custom take for primary button. * Missing rename for DS call. * Outlook to throw exception in case of error. * Implemented rename folder functionality for Outlook. * Remove default primary text from input dialog. * Fixed an issue where outlook folder rename does not work. * Disable vertical scroll for composing page editor items. * Fixing some issues with imap folder sync. * fix copy pasta * TODO folder update/removed overrides for shell. * New rename folder dialog keys. * Insfra work for folder operations and rename folder code. * RenameFolder for Gmail. * Fixed input dialog to take custom take for primary button. * Missing rename for DS call. * Outlook to throw exception in case of error. * Implemented rename folder functionality for Outlook. * Remove default primary text from input dialog. * Fixed an issue where outlook folder rename does not work. * Disable vertical scroll for composing page editor items. * Fixing some issues with imap folder sync. * fix copy pasta * TODO folder update/removed overrides for shell. * New rename folder dialog keys. * Insfra work for folder operations and rename folder code. * RenameFolder for Gmail. * Fixed input dialog to take custom take for primary button. * Missing rename for DS call. * Outlook to throw exception in case of error. * Implemented rename folder functionality for Outlook. * Remove default primary text from input dialog. * Fixed an issue where outlook folder rename does not work. * Disable vertical scroll for composing page editor items. * Fixing some issues with imap folder sync. * fix copy pasta * TODO folder update/removed overrides for shell. * New rename folder dialog keys. * Fixed an issue where redundant older updates causing pivots to be re-created. * New empty folder request * New rename folder dialog keys. * Insfra work for folder operations and rename folder code. * RenameFolder for Gmail. * Fixed input dialog to take custom take for primary button. * Missing rename for DS call. * Outlook to throw exception in case of error. * Implemented rename folder functionality for Outlook. * Remove default primary text from input dialog. * Fixed an issue where outlook folder rename does not work. * Fixing some issues with imap folder sync. * fix copy pasta * TODO folder update/removed overrides for shell. * New rename folder dialog keys. * New rename folder dialog keys. * New rename folder dialog keys. * Fixed an issue where redundant older updates causing pivots to be re-created. * New empty folder request * Enable empty folder on base sync. * Move updates on event listeners. * Remove folder UI messages. * Reworked folder synchronization for gmail. * Loading folders on the fly as the selected account changed instead of relying on cached menu items. * Merged account folder items, re-navigating to existing rendering page. * - Reworked merged account menu system. - Reworked unread item count loadings. - Fixed back button visibility. - Instant rendering of mails if renderer is active. - Animation fixes. - Menu item re-load crash/hang fixes. * Handle folder renaming on the UI. * Empty folder for all synchronizers. * New execution delay mechanism and handling folder mark as read for all synchronizers. * Revert UI changes on failure for IMAP. * Remove duplicate translation keys. * Cleanup.
2024-07-09 01:05:16 +02:00
2025-02-16 11:54:23 +01:00
bool isReportingActiveAccountResult = ActiveFolder.HandlingFolders.Any(a => a.MailAccountId == message.AccountId);
Folder operations, Gmail folder sync improvements and rework of menu items. (#273) * New rename folder dialog keys. * Insfra work for folder operations and rename folder code. * RenameFolder for Gmail. * Fixed input dialog to take custom take for primary button. * Missing rename for DS call. * Outlook to throw exception in case of error. * Implemented rename folder functionality for Outlook. * Remove default primary text from input dialog. * Fixed an issue where outlook folder rename does not work. * Disable vertical scroll for composing page editor items. * Fixing some issues with imap folder sync. * fix copy pasta * TODO folder update/removed overrides for shell. * New rename folder dialog keys. * Insfra work for folder operations and rename folder code. * RenameFolder for Gmail. * Fixed input dialog to take custom take for primary button. * Missing rename for DS call. * Outlook to throw exception in case of error. * Implemented rename folder functionality for Outlook. * Remove default primary text from input dialog. * Fixed an issue where outlook folder rename does not work. * Disable vertical scroll for composing page editor items. * Fixing some issues with imap folder sync. * fix copy pasta * TODO folder update/removed overrides for shell. * New rename folder dialog keys. * Insfra work for folder operations and rename folder code. * RenameFolder for Gmail. * Fixed input dialog to take custom take for primary button. * Missing rename for DS call. * Outlook to throw exception in case of error. * Implemented rename folder functionality for Outlook. * Remove default primary text from input dialog. * Fixed an issue where outlook folder rename does not work. * Disable vertical scroll for composing page editor items. * Fixing some issues with imap folder sync. * fix copy pasta * TODO folder update/removed overrides for shell. * New rename folder dialog keys. * Fixed an issue where redundant older updates causing pivots to be re-created. * New empty folder request * New rename folder dialog keys. * Insfra work for folder operations and rename folder code. * RenameFolder for Gmail. * Fixed input dialog to take custom take for primary button. * Missing rename for DS call. * Outlook to throw exception in case of error. * Implemented rename folder functionality for Outlook. * Remove default primary text from input dialog. * Fixed an issue where outlook folder rename does not work. * Fixing some issues with imap folder sync. * fix copy pasta * TODO folder update/removed overrides for shell. * New rename folder dialog keys. * New rename folder dialog keys. * New rename folder dialog keys. * Fixed an issue where redundant older updates causing pivots to be re-created. * New empty folder request * Enable empty folder on base sync. * Move updates on event listeners. * Remove folder UI messages. * Reworked folder synchronization for gmail. * Loading folders on the fly as the selected account changed instead of relying on cached menu items. * Merged account folder items, re-navigating to existing rendering page. * - Reworked merged account menu system. - Reworked unread item count loadings. - Fixed back button visibility. - Instant rendering of mails if renderer is active. - Animation fixes. - Menu item re-load crash/hang fixes. * Handle folder renaming on the UI. * Empty folder for all synchronizers. * New execution delay mechanism and handling folder mark as read for all synchronizers. * Revert UI changes on failure for IMAP. * Remove duplicate translation keys. * Cleanup.
2024-07-09 01:05:16 +02:00
2025-02-16 11:54:23 +01:00
if (!isReportingActiveAccountResult) return;
2025-02-16 11:35:43 +01:00
2025-02-16 11:54:23 +01:00
// At this point either all folders or a single folder sync is completed.
switch (message.Result)
{
case SynchronizationCompletedState.Success:
UpdateBarMessage(InfoBarMessageType.Success, ActiveFolder.FolderName, Translator.SynchronizationFolderReport_Success);
break;
case SynchronizationCompletedState.Failed:
UpdateBarMessage(InfoBarMessageType.Error, ActiveFolder.FolderName, Translator.SynchronizationFolderReport_Failed);
break;
default:
break;
}
2025-02-16 11:54:23 +01:00
}
2025-02-16 11:35:43 +01:00
2025-02-16 11:54:23 +01:00
void IRecipient<MailItemNavigationRequested>.Receive(MailItemNavigationRequested message)
{
Debug.WriteLine($"Mail item navigation requested");
// Find mail item and add to selected items.
2025-02-16 11:35:43 +01:00
2025-02-16 11:54:23 +01:00
MailItemViewModel navigatingMailItem = null;
ThreadMailItemViewModel threadMailItemViewModel = null;
2025-02-16 11:35:43 +01:00
2025-02-16 11:54:23 +01:00
for (int i = 0; i < 3; i++)
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
var mailContainer = MailCollection.GetMailItemContainer(message.UniqueMailId);
2025-02-16 11:35:43 +01:00
2025-02-16 11:54:23 +01:00
if (mailContainer != null)
{
2025-02-16 11:54:23 +01:00
navigatingMailItem = mailContainer.ItemViewModel;
threadMailItemViewModel = mailContainer.ThreadViewModel;
2025-02-16 11:35:43 +01:00
2025-02-16 11:54:23 +01:00
break;
}
}
2025-02-16 11:35:43 +01:00
2025-02-16 11:54:23 +01:00
if (threadMailItemViewModel != null)
threadMailItemViewModel.IsThreadExpanded = true;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
if (navigatingMailItem != null)
WeakReferenceMessenger.Default.Send(new SelectMailItemContainerEvent(navigatingMailItem, message.ScrollToItem));
}
#endregion
public async void Receive(NewMailSynchronizationRequested message)
=> await ExecuteUIThread(() => { OnPropertyChanged(nameof(CanSynchronize)); });
protected override async void OnFolderSynchronizationEnabled(IMailItemFolder mailItemFolder)
{
if (ActiveFolder?.EntityId != mailItemFolder.Id) return;
await ExecuteUIThread(() =>
2025-02-16 11:35:43 +01:00
{
2025-02-16 11:54:23 +01:00
ActiveFolder.UpdateFolder(mailItemFolder);
OnPropertyChanged(nameof(CanSynchronize));
OnPropertyChanged(nameof(IsFolderSynchronizationEnabled));
});
SyncFolderCommand?.Execute(null);
}
public async void Receive(AccountSynchronizerStateChanged message)
=> await CheckIfAccountIsSynchronizingAsync();
2025-02-16 11:35:43 +01:00
2025-02-16 11:54:23 +01:00
private async Task CheckIfAccountIsSynchronizingAsync()
{
bool isAnyAccountSynchronizing = false;
// Check each account that this page is listing folders from.
// If any of the synchronizers are synchronizing, we disable sync.
2025-02-16 11:54:23 +01:00
if (ActiveFolder != null)
{
var accountIds = ActiveFolder.HandlingFolders.Select(a => a.MailAccountId);
foreach (var accountId in accountIds)
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
var serverResponse = await _winoServerConnectionManager.GetResponseAsync<bool, SynchronizationExistenceCheckRequest>(new SynchronizationExistenceCheckRequest(accountId));
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
if (serverResponse.IsSuccess && serverResponse.Data == true)
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
isAnyAccountSynchronizing = true;
break;
2024-04-18 01:44:37 +02:00
}
}
}
2025-02-16 11:54:23 +01:00
await ExecuteUIThread(() => { IsAccountSynchronizerInSynchronization = isAnyAccountSynchronizing; });
2024-04-18 01:44:37 +02:00
}
public void Receive(AccountCacheResetMessage message)
{
if (message.Reason == AccountCacheResetReason.ExpiredCache &&
ActiveFolder.HandlingFolders.Any(a => a.MailAccountId == message.AccountId))
{
var handlingFolder = ActiveFolder.HandlingFolders.FirstOrDefault(a => a.MailAccountId == message.AccountId);
if (handlingFolder == null) return;
_ = ExecuteUIThread(() =>
{
MailCollection.Clear();
_mailDialogService.InfoBarMessage(Translator.AccountCacheReset_Title, Translator.AccountCacheReset_Message, InfoBarMessageType.Warning);
});
}
}
feat: Enhanced sender avatars with gravatar and favicons integration (#685) * feat: Enhanced sender avatars with gravatar and favicons integration * chore: Remove unused known companies thumbnails * feat(thumbnail): add IThumbnailService and refactor usage - Introduced a new interface `IThumbnailService` for handling thumbnail-related functionalities. - Registered `IThumbnailService` with its implementation `ThumbnailService` in the service container. - Updated `NotificationBuilder` to use an instance of `IThumbnailService` instead of static methods. - Refactored `ThumbnailService` from a static class to a regular class with instance methods and variables. - Modified `ImagePreviewControl` to utilize the new `IThumbnailService` instance. - Completed integration of `IThumbnailService` in the application by registering it in `App.xaml.cs`. * style: Show favicons as squares - Changed `hintCrop` in `NotificationBuilder` to `None` for app logo display. - Added `FaviconSquircle`, `FaviconImage`, and `isFavicon` to `ImagePreviewControl` for favicon handling. - Updated `UpdateInformation` method to manage favicon visibility. - Introduced `GetBitmapImageAsync` for converting Base64 to Bitmap images. - Enhanced XAML to include `FaviconSquircle` for improved UI appearance. * refactor thumbnail service * Removed old code and added clear method * added prefetch function * Change key from host to email * Remove redundant code * Test event * Fixed an issue with the thumbnail updated event. * Fix cutted favicons * exclude some domain from favicons * add yandex.ru * fix buttons in settings * remove prefetch method * Added thumbnails propagation to mailRenderingPage * Revert MailItemViewModel to object * Remove redundant code * spaces * await load parameter added * fix spaces * fix case sensativity for mail list thumbnails * change duckdns to google * Some cleanup. --------- Co-authored-by: Aleh Khantsevich <aleh.khantsevich@gmail.com> Co-authored-by: Burak Kaan Köse <bkaankose@outlook.com>
2025-06-21 01:40:25 +02:00
public void Receive(ThumbnailAdded message) => MailCollection.UpdateThumbnails(message.Email);
2024-04-18 01:44:37 +02:00
}