Initial commit.
This commit is contained in:
8
Wino.Mail/Views/Abstract/AboutPageAbstract.cs
Normal file
8
Wino.Mail/Views/Abstract/AboutPageAbstract.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using Wino.Mail.ViewModels;
|
||||
|
||||
namespace Wino.Views.Abstract
|
||||
{
|
||||
public abstract class AboutPageAbstract : BasePage<AboutPageViewModel>
|
||||
{
|
||||
}
|
||||
}
|
||||
8
Wino.Mail/Views/Abstract/AccountDetailsPageAbstract.cs
Normal file
8
Wino.Mail/Views/Abstract/AccountDetailsPageAbstract.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using Wino.Mail.ViewModels;
|
||||
|
||||
namespace Wino.Views.Abstract
|
||||
{
|
||||
public abstract class AccountDetailsPageAbstract : BasePage<AccountDetailsPageViewModel>
|
||||
{
|
||||
}
|
||||
}
|
||||
10
Wino.Mail/Views/Abstract/AccountManagementPageAbstract.cs
Normal file
10
Wino.Mail/Views/Abstract/AccountManagementPageAbstract.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using Wino.Mail.ViewModels;
|
||||
|
||||
namespace Wino.Views.Abstract
|
||||
{
|
||||
public abstract class AccountManagementPageAbstract : BasePage<AccountManagementViewModel>
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
8
Wino.Mail/Views/Abstract/AppShellAbstract.cs
Normal file
8
Wino.Mail/Views/Abstract/AppShellAbstract.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using Wino.Mail.ViewModels;
|
||||
|
||||
namespace Wino.Views.Abstract
|
||||
{
|
||||
public abstract class AppShellAbstract : BasePage<AppShellViewModel>
|
||||
{
|
||||
}
|
||||
}
|
||||
8
Wino.Mail/Views/Abstract/ComposePageAbstract.cs
Normal file
8
Wino.Mail/Views/Abstract/ComposePageAbstract.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using Wino.Mail.ViewModels;
|
||||
|
||||
namespace Wino.Views.Abstract
|
||||
{
|
||||
public abstract class ComposePageAbstract : BasePage<ComposePageViewModel>
|
||||
{
|
||||
}
|
||||
}
|
||||
8
Wino.Mail/Views/Abstract/IdlePageAbstract.cs
Normal file
8
Wino.Mail/Views/Abstract/IdlePageAbstract.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using Wino.Mail.ViewModels;
|
||||
|
||||
namespace Wino.Views.Abstract
|
||||
{
|
||||
public abstract class IdlePageAbstract : BasePage<IdlePageViewModel>
|
||||
{
|
||||
}
|
||||
}
|
||||
9
Wino.Mail/Views/Abstract/MailListPageAbstract.cs
Normal file
9
Wino.Mail/Views/Abstract/MailListPageAbstract.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Wino.Mail.ViewModels;
|
||||
|
||||
namespace Wino.Views.Abstract
|
||||
{
|
||||
public class MailListPageAbstract : BasePage<MailListPageViewModel>
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
26
Wino.Mail/Views/Abstract/MailRenderingPageAbstract.cs
Normal file
26
Wino.Mail/Views/Abstract/MailRenderingPageAbstract.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using Windows.UI.Xaml;
|
||||
using Wino.Mail.ViewModels;
|
||||
|
||||
namespace Wino.Views.Abstract
|
||||
{
|
||||
public abstract class MailRenderingPageAbstract : BasePage<MailRenderingPageViewModel>
|
||||
{
|
||||
public bool IsDarkEditor
|
||||
{
|
||||
get { return (bool)GetValue(IsDarkEditorProperty); }
|
||||
set { SetValue(IsDarkEditorProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IsDarkEditorProperty = DependencyProperty.Register(nameof(IsDarkEditor), typeof(bool), typeof(MailRenderingPageAbstract), new PropertyMetadata(false, OnIsComposerDarkModeChanged));
|
||||
|
||||
private static void OnIsComposerDarkModeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
if (obj is MailRenderingPageAbstract page)
|
||||
{
|
||||
page.OnEditorThemeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void OnEditorThemeChanged() { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using Wino.Mail.ViewModels;
|
||||
|
||||
namespace Wino.Views.Abstract
|
||||
{
|
||||
public abstract class MergedAccountDetailsPageAbstract : BasePage<MergedAccountDetailsPageViewModel>
|
||||
{
|
||||
}
|
||||
}
|
||||
6
Wino.Mail/Views/Abstract/MessageListPageAbstract.cs
Normal file
6
Wino.Mail/Views/Abstract/MessageListPageAbstract.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
using Wino.Mail.ViewModels;
|
||||
|
||||
namespace Wino.Views.Abstract
|
||||
{
|
||||
public abstract class MessageListPageAbstract : BasePage<MessageListPageViewModel> { }
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using Wino.Mail.ViewModels;
|
||||
|
||||
namespace Wino.Views.Abstract
|
||||
{
|
||||
public abstract class NewAccountManagementPageAbstract : BasePage<NewAccountManagementPageViewModel>
|
||||
{
|
||||
}
|
||||
}
|
||||
10
Wino.Mail/Views/Abstract/PersonalizationPageAbstract.cs
Normal file
10
Wino.Mail/Views/Abstract/PersonalizationPageAbstract.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using Windows.UI.Xaml;
|
||||
using Wino.Mail.ViewModels;
|
||||
|
||||
namespace Wino.Views.Abstract
|
||||
{
|
||||
public abstract class PersonalizationPageAbstract : SettingsPageBase<PersonalizationPageViewModel>
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
6
Wino.Mail/Views/Abstract/ReadingPanePageAbstract.cs
Normal file
6
Wino.Mail/Views/Abstract/ReadingPanePageAbstract.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
using Wino.Mail.ViewModels;
|
||||
|
||||
namespace Wino.Views.Abstract
|
||||
{
|
||||
public abstract class ReadingPanePageAbstract : BasePage<ReadingPanePageViewModel> { }
|
||||
}
|
||||
8
Wino.Mail/Views/Abstract/SettingOptionsPageAbstract.cs
Normal file
8
Wino.Mail/Views/Abstract/SettingOptionsPageAbstract.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using Wino.Mail.ViewModels;
|
||||
|
||||
namespace Wino.Views.Abstract
|
||||
{
|
||||
public abstract class SettingOptionsPageAbstract : SettingsPageBase<SettingOptionsPageViewModel>
|
||||
{
|
||||
}
|
||||
}
|
||||
6
Wino.Mail/Views/Abstract/SettingsPageAbstract.cs
Normal file
6
Wino.Mail/Views/Abstract/SettingsPageAbstract.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
using Wino.Mail.ViewModels;
|
||||
|
||||
namespace Wino.Views.Abstract
|
||||
{
|
||||
public abstract class SettingsPageAbstract : BasePage<SettingsPageViewModel> { }
|
||||
}
|
||||
16
Wino.Mail/Views/Abstract/SettingsPageBase.cs
Normal file
16
Wino.Mail/Views/Abstract/SettingsPageBase.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using Windows.UI.Xaml;
|
||||
using Wino.Mail.ViewModels;
|
||||
|
||||
namespace Wino.Views.Abstract
|
||||
{
|
||||
public class SettingsPageBase<T> : BasePage<T> where T : BaseViewModel
|
||||
{
|
||||
public string Title
|
||||
{
|
||||
get { return (string)GetValue(TitleProperty); }
|
||||
set { SetValue(TitleProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty TitleProperty = DependencyProperty.Register(nameof(Title), typeof(string), typeof(SettingsPageBase<T>), new PropertyMetadata(string.Empty));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
using Wino.Mail.ViewModels;
|
||||
|
||||
namespace Wino.Views.Abstract
|
||||
{
|
||||
public abstract class SignatureManagementPageAbstract : BasePage<SignatureManagementPageViewModel> { }
|
||||
}
|
||||
9
Wino.Mail/Views/Abstract/WelcomePageAbstract.cs
Normal file
9
Wino.Mail/Views/Abstract/WelcomePageAbstract.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Wino.Mail.ViewModels;
|
||||
|
||||
namespace Wino.Views.Abstract
|
||||
{
|
||||
public abstract class WelcomePageAbstract : BasePage<WelcomePageViewModel>
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
212
Wino.Mail/Views/Account/AccountDetailsPage.xaml
Normal file
212
Wino.Mail/Views/Account/AccountDetailsPage.xaml
Normal file
File diff suppressed because one or more lines are too long
30
Wino.Mail/Views/Account/AccountDetailsPage.xaml.cs
Normal file
30
Wino.Mail/Views/Account/AccountDetailsPage.xaml.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Wino.Core.Domain.Models.Folders;
|
||||
using Wino.Views.Abstract;
|
||||
|
||||
namespace Wino.Views
|
||||
{
|
||||
public sealed partial class AccountDetailsPage : AccountDetailsPageAbstract
|
||||
{
|
||||
public AccountDetailsPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private async void SyncFolderToggled(object sender, Windows.UI.Xaml.RoutedEventArgs e)
|
||||
{
|
||||
if (sender is CheckBox checkBox && checkBox.Tag is IMailItemFolder folder)
|
||||
{
|
||||
await ViewModel.FolderSyncToggledAsync(folder, checkBox.IsChecked.GetValueOrDefault());
|
||||
}
|
||||
}
|
||||
|
||||
private async void UnreadBadgeCheckboxToggled(object sender, Windows.UI.Xaml.RoutedEventArgs e)
|
||||
{
|
||||
if (sender is CheckBox checkBox && checkBox.Tag is IMailItemFolder folder)
|
||||
{
|
||||
await ViewModel.FolderShowUnreadToggled(folder, checkBox.IsChecked.GetValueOrDefault());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
262
Wino.Mail/Views/Account/AccountManagementPage.xaml
Normal file
262
Wino.Mail/Views/Account/AccountManagementPage.xaml
Normal file
File diff suppressed because one or more lines are too long
16
Wino.Mail/Views/Account/AccountManagementPage.xaml.cs
Normal file
16
Wino.Mail/Views/Account/AccountManagementPage.xaml.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using Wino.Views.Abstract;
|
||||
|
||||
namespace Wino.Views
|
||||
{
|
||||
public sealed partial class AccountManagementPage : AccountManagementPageAbstract
|
||||
{
|
||||
public AccountManagementPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
NavigationCacheMode = NavigationCacheMode.Enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
169
Wino.Mail/Views/Account/MergedAccountDetailsPage.xaml
Normal file
169
Wino.Mail/Views/Account/MergedAccountDetailsPage.xaml
Normal file
File diff suppressed because one or more lines are too long
13
Wino.Mail/Views/Account/MergedAccountDetailsPage.xaml.cs
Normal file
13
Wino.Mail/Views/Account/MergedAccountDetailsPage.xaml.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using Wino.Views.Abstract;
|
||||
|
||||
|
||||
namespace Wino.Views.Account
|
||||
{
|
||||
public sealed partial class MergedAccountDetailsPage : MergedAccountDetailsPageAbstract
|
||||
{
|
||||
public MergedAccountDetailsPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
656
Wino.Mail/Views/ComposePage.xaml
Normal file
656
Wino.Mail/Views/ComposePage.xaml
Normal file
File diff suppressed because one or more lines are too long
570
Wino.Mail/Views/ComposePage.xaml.cs
Normal file
570
Wino.Mail/Views/ComposePage.xaml.cs
Normal file
@@ -0,0 +1,570 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.WinUI.Controls;
|
||||
using EmailValidation;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.Web.WebView2.Core;
|
||||
using MimeKit;
|
||||
using Newtonsoft.Json;
|
||||
using Windows.Foundation;
|
||||
using Windows.Storage.Pickers;
|
||||
using Windows.UI.ViewManagement.Core;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media.Animation;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Entities;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Messages.Mails;
|
||||
using Wino.Core.Messages.Shell;
|
||||
using Wino.Extensions;
|
||||
using Wino.Mail.ViewModels.Data;
|
||||
using Wino.Views.Abstract;
|
||||
|
||||
namespace Wino.Views
|
||||
{
|
||||
public sealed partial class ComposePage : ComposePageAbstract,
|
||||
IRecipient<NavigationPaneModeChanged>,
|
||||
IRecipient<CreateNewComposeMailRequested>,
|
||||
IRecipient<ApplicationThemeChanged>
|
||||
{
|
||||
public bool IsComposerDarkMode
|
||||
{
|
||||
get { return (bool)GetValue(IsComposerDarkModeProperty); }
|
||||
set { SetValue(IsComposerDarkModeProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IsComposerDarkModeProperty = DependencyProperty.Register(nameof(IsComposerDarkMode), typeof(bool), typeof(ComposePage), new PropertyMetadata(false, OnIsComposerDarkModeChanged));
|
||||
|
||||
|
||||
public WebView2 GetWebView() => Chromium;
|
||||
|
||||
private TaskCompletionSource<bool> DOMLoadedTask = new TaskCompletionSource<bool>();
|
||||
|
||||
private List<IDisposable> Disposables = new List<IDisposable>();
|
||||
|
||||
public ComposePage()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
Environment.SetEnvironmentVariable("WEBVIEW2_DEFAULT_BACKGROUND_COLOR", "00FFFFFF");
|
||||
}
|
||||
|
||||
private static async void OnIsComposerDarkModeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
if (obj is ComposePage page)
|
||||
{
|
||||
await page.UpdateEditorThemeAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private IDisposable GetSuggestionBoxDisposable(TokenizingTextBox box)
|
||||
{
|
||||
return Observable.FromEventPattern<TypedEventHandler<AutoSuggestBox, AutoSuggestBoxTextChangedEventArgs>, AutoSuggestBoxTextChangedEventArgs>(
|
||||
x => box.TextChanged += x,
|
||||
x => box.TextChanged -= x)
|
||||
.Throttle(TimeSpan.FromMilliseconds(120))
|
||||
.ObserveOn(SynchronizationContext.Current)
|
||||
.Subscribe(t =>
|
||||
{
|
||||
if (t.EventArgs.Reason == AutoSuggestionBoxTextChangeReason.UserInput)
|
||||
{
|
||||
if (t.Sender is AutoSuggestBox senderBox && senderBox.Text.Length >= 3)
|
||||
{
|
||||
_ = ViewModel.ContactService.GetAddressInformationAsync(senderBox.Text).ContinueWith(x =>
|
||||
{
|
||||
_ = ViewModel.ExecuteUIThread(() =>
|
||||
{
|
||||
var addresses = x.Result;
|
||||
|
||||
senderBox.ItemsSource = addresses;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async void AddFilesClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// TODO: Pick files
|
||||
var picker = new FileOpenPicker()
|
||||
{
|
||||
SuggestedStartLocation = PickerLocationId.Desktop
|
||||
};
|
||||
|
||||
picker.FileTypeFilter.Add("*");
|
||||
var files = await picker.PickMultipleFilesAsync();
|
||||
|
||||
if (files == null) return;
|
||||
|
||||
// Convert files to MailAttachmentViewModel.
|
||||
|
||||
if (files.Any())
|
||||
{
|
||||
foreach (var file in files)
|
||||
{
|
||||
if (!ViewModel.IncludedAttachments.Any(a => a.FileName == file.Path))
|
||||
{
|
||||
var attachmentViewModel = await file.ToAttachmentViewModelAsync();
|
||||
|
||||
ViewModel.IncludedAttachments.Add(attachmentViewModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async void BoldButtonClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await InvokeScriptSafeAsync("document.getElementById('boldButton').click();");
|
||||
}
|
||||
|
||||
private async void ItalicButtonClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await InvokeScriptSafeAsync("document.getElementById('italicButton').click();");
|
||||
}
|
||||
|
||||
private async void UnderlineButtonClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await InvokeScriptSafeAsync("document.getElementById('underlineButton').click();");
|
||||
}
|
||||
|
||||
private async void StrokeButtonClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await InvokeScriptSafeAsync("document.getElementById('strikeButton').click();");
|
||||
}
|
||||
|
||||
private async void BulletListButtonClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await InvokeScriptSafeAsync("document.getElementById('bulletListButton').click();");
|
||||
}
|
||||
|
||||
private async void OrderedListButtonClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await InvokeScriptSafeAsync("document.getElementById('orderedListButton').click();");
|
||||
}
|
||||
|
||||
private async void IncreaseIndentClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await InvokeScriptSafeAsync("document.getElementById('increaseIndentButton').click();");
|
||||
}
|
||||
|
||||
private async void DecreaseIndentClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await InvokeScriptSafeAsync("document.getElementById('decreaseIndentButton').click();");
|
||||
}
|
||||
|
||||
private async void DirectionButtonClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await InvokeScriptSafeAsync("document.getElementById('directionButton').click();");
|
||||
}
|
||||
|
||||
private async void AlignmentChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
var selectedItem = AlignmentListView.SelectedItem as ComboBoxItem;
|
||||
var alignment = selectedItem.Tag.ToString();
|
||||
|
||||
switch (alignment)
|
||||
{
|
||||
case "left":
|
||||
await InvokeScriptSafeAsync("document.getElementById('ql-align-left').click();");
|
||||
break;
|
||||
case "center":
|
||||
await InvokeScriptSafeAsync("document.getElementById('ql-align-center').click();");
|
||||
break;
|
||||
case "right":
|
||||
await InvokeScriptSafeAsync("document.getElementById('ql-align-right').click();");
|
||||
break;
|
||||
case "justify":
|
||||
await InvokeScriptSafeAsync("document.getElementById('ql-align-justify').click();");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<string> ExecuteScriptFunctionAsync(string functionName, params object[] parameters)
|
||||
{
|
||||
string script = functionName + "(";
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
script += JsonConvert.SerializeObject(parameters[i]);
|
||||
if (i < parameters.Length - 1)
|
||||
{
|
||||
script += ", ";
|
||||
}
|
||||
}
|
||||
script += ");";
|
||||
|
||||
return await Chromium.ExecuteScriptAsync(script);
|
||||
}
|
||||
|
||||
private async Task<string> InvokeScriptSafeAsync(string function)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await Chromium.ExecuteScriptAsync(function);
|
||||
}
|
||||
catch (Exception) { }
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private async void AddImageClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await InvokeScriptSafeAsync("document.getElementById('addImageButton').click();");
|
||||
}
|
||||
|
||||
private async Task FocusEditorAsync()
|
||||
{
|
||||
await InvokeScriptSafeAsync("quill.focus();");
|
||||
|
||||
Chromium.Focus(FocusState.Keyboard);
|
||||
Chromium.Focus(FocusState.Programmatic);
|
||||
}
|
||||
|
||||
private async void EmojiButtonClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
CoreInputView.GetForCurrentView().TryShow(CoreInputViewKind.Emoji);
|
||||
|
||||
await FocusEditorAsync();
|
||||
}
|
||||
|
||||
private async Task<string> TryGetSelectedTextAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
return await Chromium.ExecuteScriptAsync("getSelectedText();");
|
||||
}
|
||||
catch (Exception) { }
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private async void LinkButtonClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Get selected text from Quill.
|
||||
|
||||
HyperlinkTextBox.Text = await TryGetSelectedTextAsync();
|
||||
}
|
||||
|
||||
public async Task UpdateEditorThemeAsync()
|
||||
{
|
||||
await DOMLoadedTask.Task;
|
||||
|
||||
if (IsComposerDarkMode)
|
||||
{
|
||||
await InvokeScriptSafeAsync("DarkReader.enable();");
|
||||
}
|
||||
else
|
||||
{
|
||||
await InvokeScriptSafeAsync("DarkReader.disable();");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RenderInternalAsync(string htmlBody)
|
||||
{
|
||||
await DOMLoadedTask.Task;
|
||||
|
||||
await UpdateEditorThemeAsync();
|
||||
|
||||
if (string.IsNullOrEmpty(htmlBody))
|
||||
{
|
||||
await ExecuteScriptFunctionAsync("RenderHTML", " ");
|
||||
}
|
||||
else
|
||||
{
|
||||
await ExecuteScriptFunctionAsync("RenderHTML", htmlBody);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
|
||||
{
|
||||
base.OnNavigatingFrom(e);
|
||||
|
||||
DisposeDisposables();
|
||||
|
||||
Chromium.CoreWebView2Initialized -= ChromiumInitialized;
|
||||
|
||||
if (Chromium.CoreWebView2 != null)
|
||||
{
|
||||
Chromium.CoreWebView2.DOMContentLoaded -= DOMLoaded;
|
||||
Chromium.CoreWebView2.WebMessageReceived -= ScriptMessageRecieved;
|
||||
}
|
||||
}
|
||||
|
||||
private void DisposeDisposables()
|
||||
{
|
||||
if (Disposables.Any())
|
||||
Disposables.ForEach(a => a.Dispose());
|
||||
}
|
||||
|
||||
protected override async void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
var anim = ConnectedAnimationService.GetForCurrentView().GetAnimation("WebViewConnectedAnimation");
|
||||
anim?.TryStart(Chromium);
|
||||
|
||||
DisposeDisposables();
|
||||
|
||||
Disposables.Add(GetSuggestionBoxDisposable(ToBox));
|
||||
Disposables.Add(GetSuggestionBoxDisposable(CCBox));
|
||||
Disposables.Add(GetSuggestionBoxDisposable(BccBox));
|
||||
|
||||
Chromium.CoreWebView2Initialized -= ChromiumInitialized;
|
||||
Chromium.CoreWebView2Initialized += ChromiumInitialized;
|
||||
|
||||
await Chromium.EnsureCoreWebView2Async();
|
||||
|
||||
ViewModel.GetHTMLBodyFunction = new Func<Task<string>>(async () =>
|
||||
{
|
||||
var quillContent = await InvokeScriptSafeAsync("GetHTMLContent();");
|
||||
|
||||
return JsonConvert.DeserializeObject<string>(quillContent);
|
||||
});
|
||||
|
||||
var underlyingThemeService = App.Current.Services.GetService<IUnderlyingThemeService>();
|
||||
|
||||
IsComposerDarkMode = underlyingThemeService.IsUnderlyingThemeDark();
|
||||
}
|
||||
|
||||
private async void ChromiumInitialized(Microsoft.UI.Xaml.Controls.WebView2 sender, Microsoft.UI.Xaml.Controls.CoreWebView2InitializedEventArgs args)
|
||||
{
|
||||
var editorBundlePath = (await ViewModel.NativeAppService.GetQuillEditorBundlePathAsync()).Replace("full.html", string.Empty);
|
||||
|
||||
Chromium.CoreWebView2.SetVirtualHostNameToFolderMapping("app.example", editorBundlePath, CoreWebView2HostResourceAccessKind.Allow);
|
||||
Chromium.Source = new Uri("https://app.example/full.html");
|
||||
|
||||
Chromium.CoreWebView2.DOMContentLoaded -= DOMLoaded;
|
||||
Chromium.CoreWebView2.DOMContentLoaded += DOMLoaded;
|
||||
|
||||
Chromium.CoreWebView2.WebMessageReceived -= ScriptMessageRecieved;
|
||||
Chromium.CoreWebView2.WebMessageReceived += ScriptMessageRecieved;
|
||||
}
|
||||
|
||||
private void ScriptMessageRecieved(CoreWebView2 sender, CoreWebView2WebMessageReceivedEventArgs args)
|
||||
{
|
||||
var change = JsonConvert.DeserializeObject<string>(args.WebMessageAsJson);
|
||||
|
||||
bool isEnabled = change.EndsWith("ql-active");
|
||||
|
||||
if (change.StartsWith("ql-bold"))
|
||||
BoldButton.IsChecked = isEnabled;
|
||||
else if (change.StartsWith("ql-italic"))
|
||||
ItalicButton.IsChecked = isEnabled;
|
||||
else if (change.StartsWith("ql-underline"))
|
||||
UnderlineButton.IsChecked = isEnabled;
|
||||
else if (change.StartsWith("ql-strike"))
|
||||
StrokeButton.IsChecked = isEnabled;
|
||||
else if (change.StartsWith("orderedListButton"))
|
||||
OrderedListButton.IsChecked = isEnabled;
|
||||
else if (change.StartsWith("bulletListButton"))
|
||||
BulletListButton.IsChecked = isEnabled;
|
||||
else if (change.StartsWith("ql-direction"))
|
||||
DirectionButton.IsChecked = isEnabled;
|
||||
else if (change.StartsWith("ql-align-left"))
|
||||
{
|
||||
AlignmentListView.SelectionChanged -= AlignmentChanged;
|
||||
AlignmentListView.SelectedIndex = 0;
|
||||
AlignmentListView.SelectionChanged += AlignmentChanged;
|
||||
}
|
||||
else if (change.StartsWith("ql-align-center"))
|
||||
{
|
||||
AlignmentListView.SelectionChanged -= AlignmentChanged;
|
||||
AlignmentListView.SelectedIndex = 1;
|
||||
AlignmentListView.SelectionChanged += AlignmentChanged;
|
||||
}
|
||||
else if (change.StartsWith("ql-align-right"))
|
||||
{
|
||||
AlignmentListView.SelectionChanged -= AlignmentChanged;
|
||||
AlignmentListView.SelectedIndex = 2;
|
||||
AlignmentListView.SelectionChanged += AlignmentChanged;
|
||||
}
|
||||
else if (change.StartsWith("ql-align-justify"))
|
||||
{
|
||||
AlignmentListView.SelectionChanged -= AlignmentChanged;
|
||||
AlignmentListView.SelectedIndex = 3;
|
||||
AlignmentListView.SelectionChanged += AlignmentChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private void DOMLoaded(CoreWebView2 sender, CoreWebView2DOMContentLoadedEventArgs args) => DOMLoadedTask.TrySetResult(true);
|
||||
|
||||
void IRecipient<NavigationPaneModeChanged>.Receive(NavigationPaneModeChanged message)
|
||||
{
|
||||
if (message.NewMode == MenuPaneMode.Hidden)
|
||||
TopPanelGrid.Padding = new Thickness(48, 6, 6, 6);
|
||||
else
|
||||
TopPanelGrid.Padding = new Thickness(16, 6, 6, 6);
|
||||
}
|
||||
|
||||
async void IRecipient<CreateNewComposeMailRequested>.Receive(CreateNewComposeMailRequested message)
|
||||
{
|
||||
await RenderInternalAsync(message.RenderModel.RenderHtml);
|
||||
}
|
||||
|
||||
private async void HyperlinkAddClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await InvokeScriptSafeAsync($"addHyperlink('{LinkUrlTextBox.Text}')");
|
||||
|
||||
LinkUrlTextBox.Text = string.Empty;
|
||||
HyperlinkFlyout.Hide();
|
||||
}
|
||||
|
||||
private void BarDynamicOverflowChanging(CommandBar sender, DynamicOverflowItemsChangingEventArgs args)
|
||||
{
|
||||
if (args.Action == CommandBarDynamicOverflowAction.AddingToOverflow)
|
||||
sender.OverflowButtonVisibility = CommandBarOverflowButtonVisibility.Visible;
|
||||
else
|
||||
sender.OverflowButtonVisibility = CommandBarOverflowButtonVisibility.Collapsed;
|
||||
}
|
||||
|
||||
private void ShowCCBCCClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
CCBCCShowButton.Visibility = Visibility.Collapsed;
|
||||
|
||||
CCTextBlock.Visibility = Visibility.Visible;
|
||||
CCBox.Visibility = Visibility.Visible;
|
||||
BccTextBlock.Visibility = Visibility.Visible;
|
||||
BccBox.Visibility = Visibility.Visible;
|
||||
}
|
||||
|
||||
private async void TokenItemAdding(TokenizingTextBox sender, TokenItemAddingEventArgs args)
|
||||
{
|
||||
// Check is valid email.
|
||||
|
||||
if (!EmailValidator.Validate(args.TokenText))
|
||||
{
|
||||
args.Cancel = true;
|
||||
ViewModel.NotifyInvalidEmail(args.TokenText);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var deferal = args.GetDeferral();
|
||||
|
||||
AddressInformation addedItem = null;
|
||||
|
||||
var boxTag = sender.Tag?.ToString();
|
||||
|
||||
if (boxTag == "ToBox")
|
||||
addedItem = await ViewModel.GetAddressInformationAsync(args.TokenText, ViewModel.ToItems);
|
||||
else if (boxTag == "CCBox")
|
||||
addedItem = await ViewModel.GetAddressInformationAsync(args.TokenText, ViewModel.CCItemsItems);
|
||||
else if (boxTag == "BCCBox")
|
||||
addedItem = await ViewModel.GetAddressInformationAsync(args.TokenText, ViewModel.BCCItems);
|
||||
|
||||
if (addedItem == null)
|
||||
{
|
||||
args.Cancel = true;
|
||||
ViewModel.NotifyAddressExists();
|
||||
}
|
||||
else
|
||||
{
|
||||
args.Item = addedItem;
|
||||
}
|
||||
|
||||
deferal.Complete();
|
||||
}
|
||||
|
||||
void IRecipient<ApplicationThemeChanged>.Receive(ApplicationThemeChanged message)
|
||||
{
|
||||
IsComposerDarkMode = message.IsUnderlyingThemeDark;
|
||||
}
|
||||
|
||||
private void InvertComposerThemeClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
IsComposerDarkMode = !IsComposerDarkMode;
|
||||
}
|
||||
|
||||
private void ImportanceClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ImportanceFlyout.Hide();
|
||||
ImportanceSplitButton.IsChecked = true;
|
||||
|
||||
if (sender is Button senderButton)
|
||||
{
|
||||
var selectedImportance = (MessageImportance)senderButton.Tag;
|
||||
|
||||
ViewModel.SelectedMessageImportance = selectedImportance;
|
||||
(ImportanceSplitButton.Content as SymbolIcon).Symbol = (senderButton.Content as SymbolIcon).Symbol;
|
||||
}
|
||||
}
|
||||
|
||||
private void AttachmentClicked(object sender, ItemClickEventArgs e)
|
||||
{
|
||||
if (e.ClickedItem is MailAttachmentViewModel attachmentViewModel)
|
||||
{
|
||||
ViewModel.RemoveAttachmentCommand.Execute(attachmentViewModel);
|
||||
}
|
||||
}
|
||||
|
||||
private async void AddressBoxLostFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Automatically add current text as item if it is valid mail address.
|
||||
|
||||
if (sender is TokenizingTextBox tokenizingTextBox)
|
||||
{
|
||||
if (!(tokenizingTextBox.Items.LastOrDefault() is ITokenStringContainer info)) return;
|
||||
|
||||
var currentText = info.Text;
|
||||
|
||||
if (!string.IsNullOrEmpty(currentText) && EmailValidator.Validate(currentText))
|
||||
{
|
||||
var boxTag = tokenizingTextBox.Tag?.ToString();
|
||||
|
||||
AddressInformation addedItem = null;
|
||||
ObservableCollection<AddressInformation> addressCollection = null;
|
||||
|
||||
if (boxTag == "ToBox")
|
||||
addressCollection = ViewModel.ToItems;
|
||||
else if (boxTag == "CCBox")
|
||||
addressCollection = ViewModel.CCItemsItems;
|
||||
else if (boxTag == "BCCBox")
|
||||
addressCollection = ViewModel.BCCItems;
|
||||
|
||||
if (addressCollection != null)
|
||||
addedItem = await ViewModel.GetAddressInformationAsync(currentText, addressCollection);
|
||||
|
||||
// Item has already been added.
|
||||
if (addedItem == null)
|
||||
{
|
||||
tokenizingTextBox.Text = string.Empty;
|
||||
}
|
||||
else if (addressCollection != null)
|
||||
{
|
||||
addressCollection.Add(addedItem);
|
||||
tokenizingTextBox.Text = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hack: Tokenizing text box losing focus somehow on page Loaded and shifting focus to this element.
|
||||
// For once we'll switch back to it once CCBBCGotFocus element got focus.
|
||||
|
||||
private bool isInitialFocusHandled = false;
|
||||
|
||||
private void ComposerLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ToBox.Focus(FocusState.Programmatic);
|
||||
}
|
||||
|
||||
private void CCBBCGotFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!isInitialFocusHandled)
|
||||
{
|
||||
isInitialFocusHandled = true;
|
||||
ToBox.Focus(FocusState.Programmatic);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
30
Wino.Mail/Views/IdlePage.xaml
Normal file
30
Wino.Mail/Views/IdlePage.xaml
Normal file
@@ -0,0 +1,30 @@
|
||||
<abstract:IdlePageAbstract
|
||||
xmlns:abstract="using:Wino.Views.Abstract"
|
||||
x:Class="Wino.Views.IdlePage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:helpers="using:Wino.Helpers"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:controls="using:Wino.Controls"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<!-- Empty Page for Mail Rendering Sub Frame. -->
|
||||
<Grid>
|
||||
<StackPanel
|
||||
Opacity="0.5"
|
||||
Spacing="6"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center">
|
||||
|
||||
<controls:WinoFontIcon Icon="Mail" FontSize="80" />
|
||||
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
FontSize="31"
|
||||
Text="{x:Bind ViewModel.SelectedMessageText, Mode=OneWay}"
|
||||
Style="{StaticResource SubheaderTextBlockStyle}"
|
||||
x:Name="CountTextBlock" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</abstract:IdlePageAbstract>
|
||||
12
Wino.Mail/Views/IdlePage.xaml.cs
Normal file
12
Wino.Mail/Views/IdlePage.xaml.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Wino.Views.Abstract;
|
||||
|
||||
namespace Wino.Views
|
||||
{
|
||||
public sealed partial class IdlePage : IdlePageAbstract
|
||||
{
|
||||
public IdlePage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
268
Wino.Mail/Views/ImapSetup/AdvancedImapSetupPage.xaml
Normal file
268
Wino.Mail/Views/ImapSetup/AdvancedImapSetupPage.xaml
Normal file
@@ -0,0 +1,268 @@
|
||||
<Page
|
||||
x:Class="Wino.Views.ImapSetup.AdvancedImapSetupPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:domain="using:Wino.Core.Domain"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
d:RequestedTheme="Dark"
|
||||
xmlns:helpers="using:Wino.Helpers"
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
d:Background="Black"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid RowSpacing="4">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<ScrollViewer x:Name="MainScrollviewer" Padding="{StaticResource ImapSetupDialogSubPagePadding}">
|
||||
<StackPanel Spacing="12" Padding="0,0,16,0">
|
||||
<TextBlock
|
||||
d:Text="Advanced IMAP / SMTP Configuration"
|
||||
Text="{x:Bind domain:Translator.IMAPSetupDialog_Title}"
|
||||
Margin="1,0,0,0"
|
||||
Style="{StaticResource TitleTextBlockStyle}" />
|
||||
|
||||
<TextBox
|
||||
d:Header="Mail"
|
||||
Header="{x:Bind domain:Translator.IMAPSetupDialog_MailAddress}"
|
||||
PlaceholderText="{x:Bind domain:Translator.IMAPSetupDialog_MailAddressPlaceholder}"
|
||||
x:Name="AddressBox" />
|
||||
|
||||
<TextBox
|
||||
d:Header="Display Name"
|
||||
Header="{x:Bind domain:Translator.IMAPSetupDialog_DisplayName}"
|
||||
PlaceholderText="{x:Bind domain:Translator.IMAPSetupDialog_DisplayNamePlaceholder}"
|
||||
x:Name="DisplayNameBox" />
|
||||
|
||||
<CheckBox Content="{x:Bind domain:Translator.IMAPSetupDialog_UseSameConfig}" IsChecked="{x:Bind UseSameCredentialsForSending, Mode=TwoWay}" />
|
||||
|
||||
<muxc:TabView
|
||||
d:SelectedIndex="0"
|
||||
IsAddTabButtonVisible="False"
|
||||
CanReorderTabs="False"
|
||||
TabWidthMode="Equal">
|
||||
<muxc:TabView.TabStripFooter>
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<Button>
|
||||
<Button.Content>
|
||||
<StackPanel Orientation="Horizontal" Spacing="4">
|
||||
<FontIcon
|
||||
FontFamily="Segoe Fluent Icons"
|
||||
Glyph=""
|
||||
FontSize="13" />
|
||||
<!--<TextBlock
|
||||
Text="Help "
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="12" />-->
|
||||
</StackPanel>
|
||||
</Button.Content>
|
||||
<Button.Flyout>
|
||||
<Flyout />
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</muxc:TabView.TabStripFooter>
|
||||
<muxc:TabViewItem Header="IMAP Settings" IsClosable="False">
|
||||
<!-- IMAP -->
|
||||
<StackPanel Padding="12" Spacing="10">
|
||||
<!-- Server + Port -->
|
||||
<Grid ColumnSpacing="6">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBox
|
||||
d:Header="Incoming Server"
|
||||
TextChanged="IncomingServerChanged"
|
||||
PlaceholderText="eg. imap.gmail.com"
|
||||
Header="{x:Bind domain:Translator.IMAPSetupDialog_IncomingMailServer}"
|
||||
x:Name="IncomingServerBox" />
|
||||
|
||||
<TextBox
|
||||
d:Header="Port"
|
||||
PlaceholderText="993"
|
||||
Header="{x:Bind domain:Translator.IMAPSetupDialog_IncomingMailServerPort}"
|
||||
x:Name="IncomingServerPortBox"
|
||||
Grid.Column="1" />
|
||||
</Grid>
|
||||
|
||||
<!-- Username + Password -->
|
||||
<StackPanel Spacing="6">
|
||||
<TextBox
|
||||
d:Header="Username"
|
||||
TextChanged="IncomingUsernameChanged"
|
||||
Header="{x:Bind domain:Translator.IMAPSetupDialog_Username}"
|
||||
PlaceholderText="{x:Bind domain:Translator.IMAPSetupDialog_UsernamePlaceholder}"
|
||||
x:Name="UsernameBox" />
|
||||
|
||||
<PasswordBox
|
||||
PasswordChanged="IncomingPasswordChanged"
|
||||
d:Header="Password"
|
||||
Header="{x:Bind domain:Translator.IMAPSetupDialog_Password}"
|
||||
x:Name="PasswordBox" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Security and Authentication -->
|
||||
<Grid ColumnSpacing="12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<!-- Security -->
|
||||
<StackPanel Spacing="6">
|
||||
<TextBlock
|
||||
d:Text="Connection security"
|
||||
Text="{x:Bind domain:Translator.ImapAdvancedSetupDialog_ConnectionSecurity}"
|
||||
HorizontalAlignment="Center" />
|
||||
<ComboBox
|
||||
x:Name="IncomingConnectionSecurity"
|
||||
SelectedIndex="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
ItemsSource="{x:Bind AvailableConnectionSecurities}"
|
||||
DisplayMemberPath="DisplayName" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Authentication -->
|
||||
<StackPanel Grid.Column="1" Spacing="6">
|
||||
<TextBlock
|
||||
d:Text="Authentication method"
|
||||
Text="{x:Bind domain:Translator.ImapAdvancedSetupDialog_AuthenticationMethod}"
|
||||
HorizontalAlignment="Center" />
|
||||
<ComboBox
|
||||
x:Name="IncomingAuthenticationMethod"
|
||||
SelectedIndex="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
ItemsSource="{x:Bind AvailableAuthenticationMethods}"
|
||||
DisplayMemberPath="DisplayName" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
</StackPanel>
|
||||
</muxc:TabViewItem>
|
||||
<muxc:TabViewItem IsClosable="False" Header="SMTP Settings">
|
||||
<!-- SMTP -->
|
||||
<StackPanel Padding="12" Spacing="10">
|
||||
<!-- Server + Port -->
|
||||
<Grid ColumnSpacing="6">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBox
|
||||
d:Header="Outgoing Server"
|
||||
TextChanged="OutgoingServerChanged"
|
||||
PlaceholderText="eg. smtp.gmail.com"
|
||||
Header="{x:Bind domain:Translator.IMAPSetupDialog_OutgoingMailServer}"
|
||||
x:Name="OutgoingServerBox" />
|
||||
|
||||
<TextBox
|
||||
d:Header="Port"
|
||||
Header="{x:Bind domain:Translator.IMAPSetupDialog_OutgoingMailServerPort}"
|
||||
x:Name="OutgoingServerPort"
|
||||
Grid.Column="1" />
|
||||
</Grid>
|
||||
|
||||
<!-- Username + Password -->
|
||||
<StackPanel x:Name="OutgoingAuthenticationPanel" Spacing="6">
|
||||
<TextBox
|
||||
x:Name="OutgoingUsernameBox"
|
||||
d:Header="UserName"
|
||||
IsEnabled="{x:Bind helpers:XamlHelpers.ReverseBoolConverter(UseSameCredentialsForSending), Mode=OneWay}"
|
||||
Header="{x:Bind domain:Translator.IMAPSetupDialog_OutgoingMailServerUsername}" />
|
||||
<PasswordBox
|
||||
x:Name="OutgoingPasswordBox"
|
||||
IsEnabled="{x:Bind helpers:XamlHelpers.ReverseBoolConverter(UseSameCredentialsForSending), Mode=OneWay}"
|
||||
d:Header="Password"
|
||||
Header="{x:Bind domain:Translator.IMAPSetupDialog_OutgoingMailServerPassword}" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Security and Authentication -->
|
||||
<Grid ColumnSpacing="12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<!-- Security -->
|
||||
<StackPanel Spacing="6">
|
||||
<TextBlock Text="{x:Bind domain:Translator.ImapAdvancedSetupDialog_ConnectionSecurity}" HorizontalAlignment="Center" />
|
||||
<ComboBox
|
||||
x:Name="OutgoingConnectionSecurity"
|
||||
SelectedIndex="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
ItemsSource="{x:Bind AvailableConnectionSecurities}"
|
||||
DisplayMemberPath="DisplayName" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Authentication -->
|
||||
<StackPanel Grid.Column="1" Spacing="6">
|
||||
<TextBlock Text="{x:Bind domain:Translator.ImapAdvancedSetupDialog_AuthenticationMethod}" HorizontalAlignment="Center" />
|
||||
<ComboBox
|
||||
x:Name="OutgoingAuthenticationMethod"
|
||||
SelectedIndex="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
ItemsSource="{x:Bind AvailableAuthenticationMethods}"
|
||||
DisplayMemberPath="DisplayName" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</muxc:TabViewItem>
|
||||
<muxc:TabViewItem IsClosable="False" Header="Proxy">
|
||||
<!-- Proxy -->
|
||||
<StackPanel Spacing="10" Padding="12">
|
||||
<TextBlock Text="Define your optional proxy server for the connection if your mail server requires it. This is optional." />
|
||||
<Grid ColumnSpacing="12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBox Header="Proxy server" x:Name="ProxyServerBox" />
|
||||
<muxc:NumberBox
|
||||
Header="Port"
|
||||
Grid.Column="1"
|
||||
x:Name="ProxyServerPortBox" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</muxc:TabViewItem>
|
||||
</muxc:TabView>
|
||||
|
||||
<TextBlock
|
||||
x:Name="ErrorMessage"
|
||||
TextWrapping="Wrap"
|
||||
Foreground="{ThemeResource InfoBarWarningSeverityIconBackground}" />
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
<!-- Buttons -->
|
||||
<Grid
|
||||
Padding="{StaticResource ImapSetupDialogSubPagePadding}"
|
||||
Background="{ThemeResource ContentDialogBackground}"
|
||||
Grid.Row="1"
|
||||
VerticalAlignment="Bottom"
|
||||
ColumnSpacing="6">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button
|
||||
d:Content="Cancel"
|
||||
Content="{x:Bind domain:Translator.Buttons_Cancel}"
|
||||
HorizontalAlignment="Stretch"
|
||||
Click="CancelClicked" />
|
||||
|
||||
<Button
|
||||
d:Content="Sign In"
|
||||
Content="{x:Bind domain:Translator.Buttons_SignIn}"
|
||||
Click="SignInClicked"
|
||||
Style="{ThemeResource AccentButtonStyle}"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Stretch" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Page>
|
||||
220
Wino.Mail/Views/ImapSetup/AdvancedImapSetupPage.xaml.cs
Normal file
220
Wino.Mail/Views/ImapSetup/AdvancedImapSetupPage.xaml.cs
Normal file
@@ -0,0 +1,220 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using Wino.Core.Domain;
|
||||
using Wino.Core.Domain.Entities;
|
||||
using Wino.Core.Domain.Models.Accounts;
|
||||
using Wino.Core.Domain.Models.AutoDiscovery;
|
||||
using Wino.Core.Messages.Mails;
|
||||
using Wino.Extensions;
|
||||
|
||||
|
||||
namespace Wino.Views.ImapSetup
|
||||
{
|
||||
public sealed partial class AdvancedImapSetupPage : Page
|
||||
{
|
||||
public List<ImapAuthenticationMethodModel> AvailableAuthenticationMethods { get; } = new List<ImapAuthenticationMethodModel>()
|
||||
{
|
||||
new ImapAuthenticationMethodModel(Core.Domain.Enums.ImapAuthenticationMethod.Auto, Translator.ImapAuthenticationMethod_Auto),
|
||||
new ImapAuthenticationMethodModel(Core.Domain.Enums.ImapAuthenticationMethod.None, Translator.ImapAuthenticationMethod_None),
|
||||
new ImapAuthenticationMethodModel(Core.Domain.Enums.ImapAuthenticationMethod.NormalPassword, Translator.ImapAuthenticationMethod_Plain),
|
||||
new ImapAuthenticationMethodModel(Core.Domain.Enums.ImapAuthenticationMethod.EncryptedPassword, Translator.ImapAuthenticationMethod_EncryptedPassword),
|
||||
new ImapAuthenticationMethodModel(Core.Domain.Enums.ImapAuthenticationMethod.Ntlm, Translator.ImapAuthenticationMethod_Ntlm),
|
||||
new ImapAuthenticationMethodModel(Core.Domain.Enums.ImapAuthenticationMethod.CramMd5, Translator.ImapAuthenticationMethod_CramMD5),
|
||||
new ImapAuthenticationMethodModel(Core.Domain.Enums.ImapAuthenticationMethod.DigestMd5, Translator.ImapAuthenticationMethod_DigestMD5)
|
||||
};
|
||||
|
||||
public List<ImapConnectionSecurityModel> AvailableConnectionSecurities { get; set; } = new List<ImapConnectionSecurityModel>()
|
||||
{
|
||||
new ImapConnectionSecurityModel(Core.Domain.Enums.ImapConnectionSecurity.Auto, Translator.ImapConnectionSecurity_Auto),
|
||||
new ImapConnectionSecurityModel(Core.Domain.Enums.ImapConnectionSecurity.SslTls, Translator.ImapConnectionSecurity_SslTls),
|
||||
new ImapConnectionSecurityModel(Core.Domain.Enums.ImapConnectionSecurity.StartTls, Translator.ImapConnectionSecurity_StartTls),
|
||||
new ImapConnectionSecurityModel(Core.Domain.Enums.ImapConnectionSecurity.None, Translator.ImapConnectionSecurity_None)
|
||||
};
|
||||
|
||||
public bool UseSameCredentialsForSending
|
||||
{
|
||||
get { return (bool)GetValue(UseSameCredentialsForSendingProperty); }
|
||||
set { SetValue(UseSameCredentialsForSendingProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty UseSameCredentialsForSendingProperty = DependencyProperty.Register(nameof(UseSameCredentialsForSending), typeof(bool), typeof(AdvancedImapSetupPage), new PropertyMetadata(true, OnUseSameCredentialsForSendingChanged));
|
||||
|
||||
public AdvancedImapSetupPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
NavigationCacheMode = NavigationCacheMode.Enabled;
|
||||
}
|
||||
|
||||
private static void OnUseSameCredentialsForSendingChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
if (obj is AdvancedImapSetupPage page)
|
||||
{
|
||||
page.UpdateOutgoingAuthenticationPanel();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateOutgoingAuthenticationPanel()
|
||||
{
|
||||
if (UseSameCredentialsForSending)
|
||||
{
|
||||
OutgoingUsernameBox.Text = UsernameBox.Text;
|
||||
OutgoingPasswordBox.Password = PasswordBox.Password;
|
||||
}
|
||||
else
|
||||
{
|
||||
OutgoingUsernameBox.Text = string.Empty;
|
||||
OutgoingPasswordBox.Password = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
// Connection is succesfull but error occurred.
|
||||
// Imap and Smptp settings exists here at this point.
|
||||
|
||||
if (e.Parameter is AutoDiscoverySettings preDefinedSettings && preDefinedSettings.UserMinimalSettings != null)
|
||||
{
|
||||
// TODO: Auto discovery settings adjustments.
|
||||
|
||||
UsernameBox.Text = preDefinedSettings.UserMinimalSettings.Email;
|
||||
DisplayNameBox.Text = preDefinedSettings.UserMinimalSettings.DisplayName;
|
||||
PasswordBox.Password = preDefinedSettings.UserMinimalSettings.Password;
|
||||
|
||||
var serverInfo = preDefinedSettings.ToServerInformation();
|
||||
|
||||
IncomingServerBox.Text = serverInfo.IncomingServer;
|
||||
IncomingServerPortBox.Text = serverInfo.IncomingServerPort;
|
||||
|
||||
|
||||
OutgoingPasswordBox.Password = serverInfo.OutgoingServerPassword;
|
||||
|
||||
OutgoingServerBox.Text = serverInfo.OutgoingServer;
|
||||
OutgoingServerPort.Text = serverInfo.OutgoingServerPort;
|
||||
|
||||
OutgoingUsernameBox.Text = serverInfo.OutgoingServerUsername;
|
||||
|
||||
UseSameCredentialsForSending = OutgoingUsernameBox.Text == UsernameBox.Text;
|
||||
}
|
||||
else if (e.Parameter is AutoDiscoveryMinimalSettings autoDiscoveryMinimalSettings)
|
||||
{
|
||||
// Auto discovery failed. Only minimal settings are passed.
|
||||
|
||||
UsernameBox.Text = autoDiscoveryMinimalSettings.Email;
|
||||
DisplayNameBox.Text = autoDiscoveryMinimalSettings.DisplayName;
|
||||
PasswordBox.Password = autoDiscoveryMinimalSettings.Password;
|
||||
}
|
||||
else if (e.Parameter is AutoDiscoveryConnectionTestFailedPackage failedPackage)
|
||||
{
|
||||
ErrorMessage.Text = failedPackage.Error.Message;
|
||||
|
||||
MainScrollviewer.ScrollToElement(ErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
private void CancelClicked(object sender, RoutedEventArgs e) => WeakReferenceMessenger.Default.Send(new ImapSetupDismissRequested(null));
|
||||
|
||||
private string GetServerWithoutPort(string server)
|
||||
{
|
||||
var splitted = server.Split(':');
|
||||
|
||||
if (splitted.Length > 1)
|
||||
{
|
||||
return splitted[0];
|
||||
}
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
private void SignInClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ErrorMessage.Text = string.Empty;
|
||||
|
||||
var info = new CustomServerInformation()
|
||||
{
|
||||
IncomingServer = GetServerWithoutPort(IncomingServerBox.Text),
|
||||
Id = Guid.NewGuid(),
|
||||
|
||||
IncomingServerPassword = PasswordBox.Password,
|
||||
IncomingServerType = Core.Domain.Enums.CustomIncomingServerType.IMAP4,
|
||||
IncomingServerUsername = UsernameBox.Text,
|
||||
IncomingAuthenticationMethod = (IncomingAuthenticationMethod.SelectedItem as ImapAuthenticationMethodModel).ImapAuthenticationMethod,
|
||||
IncomingServerSocketOption = (IncomingConnectionSecurity.SelectedItem as ImapConnectionSecurityModel).ImapConnectionSecurity,
|
||||
IncomingServerPort = IncomingServerPortBox.Text,
|
||||
|
||||
OutgoingServer = GetServerWithoutPort(OutgoingServerBox.Text),
|
||||
OutgoingServerPort = OutgoingServerPort.Text,
|
||||
OutgoingServerPassword = OutgoingPasswordBox.Password,
|
||||
OutgoingAuthenticationMethod = (OutgoingAuthenticationMethod.SelectedItem as ImapAuthenticationMethodModel).ImapAuthenticationMethod,
|
||||
OutgoingServerSocketOption = (OutgoingConnectionSecurity.SelectedItem as ImapConnectionSecurityModel).ImapConnectionSecurity,
|
||||
OutgoingServerUsername = OutgoingUsernameBox.Text,
|
||||
|
||||
ProxyServer = ProxyServerBox.Text,
|
||||
ProxyServerPort = ProxyServerPortBox.Text,
|
||||
Address = AddressBox.Text,
|
||||
DisplayName = DisplayNameBox.Text
|
||||
};
|
||||
|
||||
if (UseSameCredentialsForSending)
|
||||
{
|
||||
info.OutgoingServerUsername = info.IncomingServerUsername;
|
||||
info.OutgoingServerPassword = info.IncomingServerPassword;
|
||||
}
|
||||
else
|
||||
{
|
||||
info.OutgoingServerUsername = OutgoingUsernameBox.Text;
|
||||
info.OutgoingServerPassword = OutgoingPasswordBox.Password;
|
||||
}
|
||||
|
||||
WeakReferenceMessenger.Default.Send(new ImapSetupNavigationRequested(typeof(TestingImapConnectionPage), info));
|
||||
}
|
||||
|
||||
private void IncomingServerChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
if (sender is TextBox senderTextBox)
|
||||
{
|
||||
var splitted = senderTextBox.Text.Split(':');
|
||||
|
||||
if (splitted.Length > 1)
|
||||
{
|
||||
IncomingServerPortBox.Text = splitted[splitted.Length - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OutgoingServerChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
if (sender is TextBox senderTextBox)
|
||||
{
|
||||
var splitted = senderTextBox.Text.Split(':');
|
||||
|
||||
if (splitted.Length > 1)
|
||||
{
|
||||
OutgoingServerPort.Text = splitted[splitted.Length - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void IncomingUsernameChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
if (UseSameCredentialsForSending)
|
||||
{
|
||||
OutgoingUsernameBox.Text = UsernameBox.Text;
|
||||
}
|
||||
}
|
||||
|
||||
private void IncomingPasswordChanged(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (UseSameCredentialsForSending)
|
||||
{
|
||||
OutgoingPasswordBox.Password = PasswordBox.Password;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
30
Wino.Mail/Views/ImapSetup/AutoDiscoveryPage.xaml
Normal file
30
Wino.Mail/Views/ImapSetup/AutoDiscoveryPage.xaml
Normal file
File diff suppressed because one or more lines are too long
50
Wino.Mail/Views/ImapSetup/AutoDiscoveryPage.xaml.cs
Normal file
50
Wino.Mail/Views/ImapSetup/AutoDiscoveryPage.xaml.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.AutoDiscovery;
|
||||
using Wino.Core.Messages.Mails;
|
||||
|
||||
namespace Wino.Views.ImapSetup
|
||||
{
|
||||
public sealed partial class AutoDiscoveryPage : Page
|
||||
{
|
||||
private readonly IAutoDiscoveryService _autoDiscoveryService;
|
||||
public AutoDiscoveryPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
_autoDiscoveryService = App.Current.Services.GetService<IAutoDiscoveryService>();
|
||||
}
|
||||
|
||||
protected override async void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
AutoDiscoverySettings discoverySettings = null;
|
||||
|
||||
if (e.Parameter is AutoDiscoveryMinimalSettings userMinimalSettings)
|
||||
{
|
||||
discoverySettings = await _autoDiscoveryService.GetAutoDiscoverySettings(userMinimalSettings);
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
if (discoverySettings == null)
|
||||
{
|
||||
// Couldn't find settings.
|
||||
|
||||
WeakReferenceMessenger.Default.Send(new ImapSetupBackNavigationRequested(typeof(WelcomeImapSetupPage), "Couldn't find mailbox settings for {userMinimalSettings.Email}. Please configure it manually."));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Settings are found. Test the connection with the given password.
|
||||
|
||||
discoverySettings.UserMinimalSettings = userMinimalSettings;
|
||||
|
||||
WeakReferenceMessenger.Default.Send(new ImapSetupNavigationRequested(typeof(TestingImapConnectionPage), discoverySettings));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
31
Wino.Mail/Views/ImapSetup/PreparingImapFoldersPage.xaml
Normal file
31
Wino.Mail/Views/ImapSetup/PreparingImapFoldersPage.xaml
Normal file
@@ -0,0 +1,31 @@
|
||||
<Page
|
||||
x:Class="Wino.Views.ImapSetup.PreparingImapFoldersPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:Wino.Views.ImapSetup"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
xmlns:domain="using:Wino.Core.Domain"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid>
|
||||
<!-- Preparing Folders Panel -->
|
||||
<StackPanel
|
||||
x:Name="PreparingFoldersPanel"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center">
|
||||
|
||||
<Viewbox
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Width="26"
|
||||
Height="26">
|
||||
<PathIcon Data="M1024,317.5L1024,507C1014.67,495.333 1004.67,484.167 994,473.5C983.333,462.833 972,453 960,444L960,320C960,311.333 958.333,303.083 955,295.25C951.667,287.417 947.083,280.583 941.25,274.75C935.417,268.917 928.583,264.333 920.75,261C912.917,257.667 904.667,256 896,256L522,256L458,298.5C436,312.833 412.333,320 387,320L64,320L64,832C64,841 65.6667,849.417 69,857.25C72.3333,865.083 76.8333,871.833 82.5,877.5C88.1667,883.167 94.9167,887.667 102.75,891C110.583,894.333 119,896 128,896L404.5,896C410.167,907.333 416.25,918.333 422.75,929C429.25,939.667 436.333,950 444,960L125.5,960C108.833,960 92.9167,956.583 77.75,949.75C62.5833,942.917 49.25,933.75 37.75,922.25C26.25,910.75 17.0833,897.417 10.25,882.25C3.41667,867.083 0,851.167 0,834.5L0,189.5C0,172.833 3.41667,156.917 10.25,141.75C17.0833,126.583 26.25,113.25 37.75,101.75C49.25,90.25 62.5833,81.0834 77.75,74.25C92.9167,67.4167 108.833,64.0001 125.5,64L368,64C388,64.0001 407.167,68.5001 425.5,77.5C443.833,86.5001 458.833,99.0001 470.5,115L528,192L898.5,192C915.167,192 931.083,195.417 946.25,202.25C961.417,209.083 974.75,218.25 986.25,229.75C997.75,241.25 1006.92,254.583 1013.75,269.75C1020.58,284.917 1024,300.833 1024,317.5ZM466,216L419,153.5C413,145.5 405.5,139.25 396.5,134.75C387.5,130.25 378,128 368,128L128,128C119,128 110.667,129.667 103,133C95.3333,136.333 88.5833,140.917 82.75,146.75C76.9167,152.583 72.3333,159.333 69,167C65.6667,174.667 64,183 64,192L64,256L387,256C394.333,256 401.5,254.667 408.5,252C415.5,249.333 422.25,246 428.75,242C435.25,238 441.583,233.667 447.75,229C453.917,224.333 460,220 466,216ZM1024,736C1024,775.667 1016.42,813 1001.25,848C986.083,883 965.5,913.5 939.5,939.5C913.5,965.5 883,986.083 848,1001.25C813,1016.42 775.667,1024 736,1024C696,1024 658.5,1016.5 623.5,1001.5C588.5,986.5 558,966 532,940C506,914 485.5,883.5 470.5,848.5C455.5,813.5 448,776 448,736C448,696.333 455.583,659 470.75,624C485.917,589 506.5,558.5 532.5,532.5C558.5,506.5 589,485.917 624,470.75C659,455.583 696.333,448 736,448C762.333,448 787.75,451.417 812.25,458.25C836.75,465.083 859.667,474.75 881,487.25C902.333,499.75 921.833,514.833 939.5,532.5C957.167,550.167 972.25,569.667 984.75,591C997.25,612.333 1006.92,635.25 1013.75,659.75C1020.58,684.25 1024,709.667 1024,736ZM896,576C896,567.333 892.833,559.833 886.5,553.5C880.167,547.167 872.667,544 864,544C857.667,544 852.5,545.167 848.5,547.5C844.5,549.833 841.25,552.917 838.75,556.75C836.25,560.583 834.5,565 833.5,570C832.5,575 832,580.167 832,585.5C816.333,577.167 800.917,570.833 785.75,566.5C770.583,562.167 754,560 736,560C724,560 711.75,561.25 699.25,563.75C686.75,566.25 674.5,569.917 662.5,574.75C650.5,579.583 639.083,585.417 628.25,592.25C617.417,599.083 608,607 600,616C597,619.333 594.667,622.75 593,626.25C591.333,629.75 590.5,633.833 590.5,638.5C590.5,647.5 593.667,655.167 600,661.5C606.333,667.833 614,671 623,671C628.667,671 634.75,668.583 641.25,663.75C647.75,658.917 655.333,653.5 664,647.5C672.667,641.5 682.75,636.083 694.25,631.25C705.75,626.417 719.333,624 735,624C746.667,624 757.5,625.25 767.5,627.75C777.5,630.25 787.667,634.333 798,640L785,640C779,640 773.083,640.25 767.25,640.75C761.417,641.25 756.167,642.583 751.5,644.75C746.833,646.917 743.083,650.167 740.25,654.5C737.417,658.833 736,664.667 736,672C736,680.667 739.167,688.167 745.5,694.5C751.833,700.833 759.333,704 768,704L864,704C872.667,704 880.167,700.833 886.5,694.5C892.833,688.167 896,680.667 896,672ZM881.5,833C881.5,824.333 878.333,816.833 872,810.5C865.667,804.167 858.167,801 849.5,801C842.833,801 836.333,803.417 830,808.25C823.667,813.083 816.333,818.5 808,824.5C799.667,830.5 789.833,835.917 778.5,840.75C767.167,845.583 753.333,848 737,848C725.333,848 714.5,846.75 704.5,844.25C694.5,841.75 684.333,837.667 674,832L687,832C692.667,832 698.417,831.75 704.25,831.25C710.083,830.75 715.333,829.417 720,827.25C724.667,825.083 728.5,821.833 731.5,817.5C734.5,813.167 736,807.333 736,800C736,791.333 732.833,783.833 726.5,777.5C720.167,771.167 712.667,768 704,768L608,768C599.333,768 591.833,771.167 585.5,777.5C579.167,783.833 576,791.333 576,800L576,896C576,904.667 579.167,912.167 585.5,918.5C591.833,924.833 599.333,928 608,928C614.333,928 619.5,926.833 623.5,924.5C627.5,922.167 630.75,919.083 633.25,915.25C635.75,911.417 637.5,907 638.5,902C639.5,897 640,891.833 640,886.5C655.667,894.833 671.083,901.167 686.25,905.5C701.417,909.833 718,912 736,912C748,912 760.333,910.75 773,908.25C785.667,905.75 797.917,902.083 809.75,897.25C821.583,892.417 832.833,886.583 843.5,879.75C854.167,872.917 863.667,865 872,856C878.333,849.333 881.5,841.667 881.5,833Z" />
|
||||
</Viewbox>
|
||||
|
||||
<TextBlock Text="{x:Bind domain:Translator.PreparingFoldersMessage}" />
|
||||
<muxc:ProgressBar IsIndeterminate="True" Margin="0,4,0,0" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Page>
|
||||
30
Wino.Mail/Views/ImapSetup/PreparingImapFoldersPage.xaml.cs
Normal file
30
Wino.Mail/Views/ImapSetup/PreparingImapFoldersPage.xaml.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Data;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238
|
||||
|
||||
namespace Wino.Views.ImapSetup
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class PreparingImapFoldersPage : Page
|
||||
{
|
||||
public PreparingImapFoldersPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
32
Wino.Mail/Views/ImapSetup/TestingImapConnectionPage.xaml
Normal file
32
Wino.Mail/Views/ImapSetup/TestingImapConnectionPage.xaml
Normal file
@@ -0,0 +1,32 @@
|
||||
<Page
|
||||
x:Class="Wino.Views.ImapSetup.TestingImapConnectionPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:Wino.Views.ImapSetup"
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:domain="using:Wino.Core.Domain"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid>
|
||||
<!-- Testing Connection Panel -->
|
||||
<StackPanel
|
||||
x:Name="TestingConnectionPanel"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center">
|
||||
|
||||
<Viewbox
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Width="26"
|
||||
Height="26">
|
||||
<PathIcon Data="F1 M 18.75 18.125 C 18.75 18.294271 18.68815 18.440756 18.564453 18.564453 C 18.440754 18.68815 18.29427 18.75 18.125 18.75 L 13.75 18.75 C 13.75 18.925781 13.717447 19.088541 13.652344 19.238281 C 13.587239 19.388021 13.497721 19.519857 13.383789 19.633789 C 13.269855 19.747721 13.13802 19.83724 12.988281 19.902344 C 12.838541 19.967447 12.675781 20 12.5 20 L 6.25 20 C 6.074219 20 5.909831 19.967447 5.756836 19.902344 C 5.603841 19.83724 5.472005 19.74935 5.361328 19.638672 C 5.250651 19.527994 5.16276 19.396158 5.097656 19.243164 C 5.032552 19.09017 5 18.925781 5 18.75 L 0.625 18.75 C 0.455729 18.75 0.309245 18.68815 0.185547 18.564453 C 0.061849 18.440756 0 18.294271 0 18.125 C 0 17.955729 0.061849 17.809244 0.185547 17.685547 C 0.309245 17.56185 0.455729 17.5 0.625 17.5 L 5 17.5 C 5 17.154949 5.120442 16.86198 5.361328 16.621094 C 5.608724 16.373699 5.904948 16.25 6.25 16.25 L 7.5 16.25 C 7.5 16.074219 7.532552 15.911459 7.597656 15.761719 C 7.66276 15.611979 7.752278 15.480144 7.866211 15.366211 C 7.980143 15.252279 8.111979 15.162761 8.261719 15.097656 C 8.411458 15.032553 8.574219 15 8.75 15 L 8.75 13.75 L 6.875 13.75 C 6.621094 13.75 6.380208 13.701172 6.152344 13.603516 C 5.924479 13.505859 5.724284 13.370769 5.551758 13.198242 C 5.379231 13.025717 5.244141 12.825521 5.146484 12.597656 C 5.048828 12.369792 5 12.128906 5 11.875 L 5 1.875 C 5 1.621094 5.048828 1.380209 5.146484 1.152344 C 5.244141 0.92448 5.379231 0.724285 5.551758 0.551758 C 5.724284 0.379232 5.924479 0.244141 6.152344 0.146484 C 6.380208 0.048828 6.621094 0 6.875 0 L 11.875 0 C 12.128906 0 12.369791 0.048828 12.597656 0.146484 C 12.825521 0.244141 13.025716 0.379232 13.198242 0.551758 C 13.370768 0.724285 13.505859 0.92448 13.603516 1.152344 C 13.701172 1.380209 13.75 1.621094 13.75 1.875 L 13.75 11.875 C 13.75 12.128906 13.701172 12.369792 13.603516 12.597656 C 13.505859 12.825521 13.370768 13.025717 13.198242 13.198242 C 13.025716 13.370769 12.825521 13.505859 12.597656 13.603516 C 12.369791 13.701172 12.128906 13.75 11.875 13.75 L 10 13.75 L 10 15 C 10.169271 15 10.330403 15.032553 10.483398 15.097656 C 10.636393 15.162761 10.769856 15.252279 10.883789 15.366211 C 10.997721 15.480144 11.087239 15.613607 11.152344 15.766602 C 11.217447 15.919597 11.25 16.080729 11.25 16.25 L 12.5 16.25 C 12.669271 16.25 12.828775 16.282553 12.978516 16.347656 C 13.128255 16.41276 13.261719 16.503906 13.378906 16.621094 C 13.626302 16.86849 13.75 17.161459 13.75 17.5 L 18.125 17.5 C 18.29427 17.5 18.440754 17.56185 18.564453 17.685547 C 18.68815 17.809244 18.75 17.955729 18.75 18.125 Z M 11.875 12.5 C 12.04427 12.5 12.190754 12.438151 12.314453 12.314453 C 12.43815 12.190756 12.5 12.044271 12.5 11.875 L 12.5 1.875 C 12.5 1.70573 12.43815 1.559246 12.314453 1.435547 C 12.190754 1.31185 12.04427 1.25 11.875 1.25 L 6.875 1.25 C 6.705729 1.25 6.559244 1.31185 6.435547 1.435547 C 6.311849 1.559246 6.25 1.70573 6.25 1.875 L 6.25 11.875 C 6.25 12.044271 6.311849 12.190756 6.435547 12.314453 C 6.559244 12.438151 6.705729 12.5 6.875 12.5 Z M 10.625 2.5 C 10.794271 2.5 10.940755 2.56185 11.064453 2.685547 C 11.18815 2.809246 11.25 2.95573 11.25 3.125 C 11.25 3.294271 11.18815 3.440756 11.064453 3.564453 C 10.940755 3.688152 10.794271 3.75 10.625 3.75 L 8.125 3.75 C 7.955729 3.75 7.809245 3.688152 7.685547 3.564453 C 7.561849 3.440756 7.5 3.294271 7.5 3.125 C 7.5 2.95573 7.561849 2.809246 7.685547 2.685547 C 7.809245 2.56185 7.955729 2.5 8.125 2.5 Z M 10.625 5 C 10.794271 5.000001 10.940755 5.06185 11.064453 5.185547 C 11.18815 5.309245 11.25 5.455729 11.25 5.625 C 11.25 5.794271 11.18815 5.940756 11.064453 6.064453 C 10.940755 6.188151 10.794271 6.25 10.625 6.25 L 8.125 6.25 C 7.955729 6.25 7.809245 6.188151 7.685547 6.064453 C 7.561849 5.940756 7.5 5.794271 7.5 5.625 C 7.5 5.455729 7.561849 5.309245 7.685547 5.185547 C 7.809245 5.06185 7.955729 5.000001 8.125 5 Z M 12.5 18.75 L 12.5 17.5 L 10.625 17.5 C 10.481771 17.5 10.367838 17.47233 10.283203 17.416992 C 10.198567 17.361654 10.135091 17.290039 10.092773 17.202148 C 10.050455 17.114258 10.022786 17.016602 10.009766 16.90918 C 9.996744 16.801758 9.990234 16.692709 9.990234 16.582031 C 9.990234 16.523438 9.991861 16.466471 9.995117 16.411133 C 9.998372 16.355795 10 16.302084 10 16.25 L 8.75 16.25 L 8.75 16.582031 C 8.75 16.692709 8.743489 16.801758 8.730469 16.90918 C 8.717447 17.016602 8.689778 17.114258 8.647461 17.202148 C 8.605143 17.290039 8.543294 17.361654 8.461914 17.416992 C 8.380533 17.47233 8.268229 17.5 8.125 17.5 L 6.25 17.5 L 6.25 18.75 Z " />
|
||||
</Viewbox>
|
||||
|
||||
<TextBlock Text="{x:Bind domain:Translator.TestingImapConnectionMessage}" />
|
||||
|
||||
<muxc:ProgressBar IsIndeterminate="True" Margin="0,4,0,0" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Page>
|
||||
80
Wino.Mail/Views/ImapSetup/TestingImapConnectionPage.xaml.cs
Normal file
80
Wino.Mail/Views/ImapSetup/TestingImapConnectionPage.xaml.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.AppCenter.Analytics;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Entities;
|
||||
using Wino.Core.Domain.Models.AutoDiscovery;
|
||||
using Wino.Core.Messages.Mails;
|
||||
|
||||
|
||||
namespace Wino.Views.ImapSetup
|
||||
{
|
||||
public sealed partial class TestingImapConnectionPage : Page
|
||||
{
|
||||
private IImapTestService _imapTestService;
|
||||
|
||||
public TestingImapConnectionPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
_imapTestService = App.Current.Services.GetService<IImapTestService>();
|
||||
}
|
||||
|
||||
private async Task TryTestConnectionAsync(CustomServerInformation serverInformation)
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
|
||||
await _imapTestService.TestImapConnectionAsync(serverInformation);
|
||||
|
||||
// All success. Finish setup with validated server information.
|
||||
|
||||
WeakReferenceMessenger.Default.Send(new ImapSetupDismissRequested(serverInformation));
|
||||
}
|
||||
|
||||
protected override async void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
// We either go back to welcome setup page or advanced config page.
|
||||
// Based on if we come from auto discovery or not.
|
||||
|
||||
if (e.Parameter is AutoDiscoverySettings autoDiscoverySettings)
|
||||
{
|
||||
var serverInformation = autoDiscoverySettings.ToServerInformation();
|
||||
|
||||
try
|
||||
{
|
||||
await TryTestConnectionAsync(serverInformation);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WeakReferenceMessenger.Default.Send(new ImapSetupBackNavigationRequested(typeof(WelcomeImapSetupPage),
|
||||
new AutoDiscoveryConnectionTestFailedPackage(autoDiscoverySettings, ex)));
|
||||
}
|
||||
}
|
||||
else if (e.Parameter is CustomServerInformation customServerInformation)
|
||||
{
|
||||
try
|
||||
{
|
||||
await TryTestConnectionAsync(customServerInformation);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Analytics.TrackEvent("IMAP Test Failed", new Dictionary<string, string>()
|
||||
{
|
||||
{ "Server", customServerInformation.IncomingServer },
|
||||
{ "Port", customServerInformation.IncomingServerPort },
|
||||
});
|
||||
|
||||
WeakReferenceMessenger.Default.Send(new ImapSetupBackNavigationRequested(typeof(AdvancedImapSetupPage),
|
||||
new AutoDiscoveryConnectionTestFailedPackage(ex)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
101
Wino.Mail/Views/ImapSetup/WelcomeImapSetupPage.xaml
Normal file
101
Wino.Mail/Views/ImapSetup/WelcomeImapSetupPage.xaml
Normal file
File diff suppressed because one or more lines are too long
90
Wino.Mail/Views/ImapSetup/WelcomeImapSetupPage.xaml.cs
Normal file
90
Wino.Mail/Views/ImapSetup/WelcomeImapSetupPage.xaml.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
using System;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using Wino.Core.Domain.Models.AutoDiscovery;
|
||||
using Wino.Core.Messages.Mails;
|
||||
using Wino.Extensions;
|
||||
|
||||
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238
|
||||
|
||||
namespace Wino.Views.ImapSetup
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class WelcomeImapSetupPage : Page
|
||||
{
|
||||
private AutoDiscoveryConnectionTestFailedPackage failedPackage;
|
||||
|
||||
public WelcomeImapSetupPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
NavigationCacheMode = Windows.UI.Xaml.Navigation.NavigationCacheMode.Enabled;
|
||||
}
|
||||
|
||||
private void SignInClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
failedPackage = null;
|
||||
|
||||
var minimalSettings = new AutoDiscoveryMinimalSettings()
|
||||
{
|
||||
Password = PasswordBox.Password,
|
||||
DisplayName = DisplayNameBox.Text,
|
||||
Email = AddressBox.Text,
|
||||
};
|
||||
|
||||
WeakReferenceMessenger.Default.Send(new ImapSetupNavigationRequested(typeof(AutoDiscoveryPage), minimalSettings));
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
if (e.Parameter is string errorMessage)
|
||||
{
|
||||
ErrorMessageText.Text = errorMessage;
|
||||
|
||||
MainScrollviewer.ScrollToElement(ErrorMessageText);
|
||||
}
|
||||
else if (e.Parameter is AutoDiscoveryConnectionTestFailedPackage autoDiscoveryConnectionTestFailedPackage)
|
||||
{
|
||||
failedPackage = autoDiscoveryConnectionTestFailedPackage;
|
||||
ErrorMessageText.Text = $"Discovery was successful but connection to the server failed.{Environment.NewLine}{Environment.NewLine}{autoDiscoveryConnectionTestFailedPackage.Error.Message}";
|
||||
|
||||
MainScrollviewer.ScrollToElement(ErrorMessageText);
|
||||
}
|
||||
}
|
||||
|
||||
private void CancelClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
WeakReferenceMessenger.Default.Send(new ImapSetupDismissRequested());
|
||||
}
|
||||
|
||||
private void AdvancedConfigurationClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var latestMinimalSettings = new AutoDiscoveryMinimalSettings()
|
||||
{
|
||||
DisplayName = DisplayNameBox.Text,
|
||||
Password = PasswordBox.Password,
|
||||
Email = AddressBox.Text
|
||||
};
|
||||
|
||||
if (failedPackage != null)
|
||||
{
|
||||
// Go to advanced settings with updated minimal settings.
|
||||
|
||||
failedPackage.Settings.UserMinimalSettings = latestMinimalSettings;
|
||||
|
||||
WeakReferenceMessenger.Default.Send(new ImapSetupNavigationRequested(typeof(AdvancedImapSetupPage), failedPackage.Settings));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Go to advanced page.
|
||||
|
||||
WeakReferenceMessenger.Default.Send(new ImapSetupNavigationRequested(typeof(AdvancedImapSetupPage), latestMinimalSettings));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
891
Wino.Mail/Views/MailListPage.xaml
Normal file
891
Wino.Mail/Views/MailListPage.xaml
Normal file
@@ -0,0 +1,891 @@
|
||||
<abstract:MailListPageAbstract
|
||||
x:Class="Wino.Views.MailListPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:abstract="using:Wino.Views.Abstract"
|
||||
xmlns:collections="using:CommunityToolkit.Mvvm.Collections"
|
||||
xmlns:controls="using:Wino.Controls"
|
||||
xmlns:controls1="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:enums="using:Wino.Core.Domain.Enums"
|
||||
xmlns:helpers="using:Wino.Helpers"
|
||||
xmlns:i="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:ic="using:Microsoft.Xaml.Interactions.Core"
|
||||
xmlns:listview="using:Wino.Controls.Advanced"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:menuflyouts="using:Wino.MenuFlyouts"
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
xmlns:selectors="using:Wino.Selectors"
|
||||
xmlns:domain="using:Wino.Core.Domain"
|
||||
xmlns:ui="using:Microsoft.Toolkit.Uwp.UI"
|
||||
xmlns:wino="using:Wino"
|
||||
xmlns:viewModelData="using:Wino.Mail.ViewModels.Data"
|
||||
x:Name="root"
|
||||
Loaded="MailListPageLoaded"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Page.Resources>
|
||||
<CollectionViewSource
|
||||
x:Name="MailCollectionViewSource"
|
||||
IsSourceGrouped="True"
|
||||
Source="{x:Bind ViewModel.MailCollection.MailItems, Mode=OneWay}" />
|
||||
|
||||
<Thickness x:Key="ExpanderHeaderPadding">0,0,0,0</Thickness>
|
||||
<Thickness x:Key="ExpanderChevronMargin">0,0,12,0</Thickness>
|
||||
<Thickness x:Key="ExpanderHeaderBorderThickness">0,0,0,0</Thickness>
|
||||
|
||||
<SolidColorBrush x:Key="CardBackgroundFillColorDefaultBrush">Transparent</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="CardBackgroundFillColorSecondaryBrush">Transparent</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="ExpanderContentBorderBrush">Transparent</SolidColorBrush>
|
||||
|
||||
<StaticResource x:Key="ExpanderContentBackground" ResourceKey="CardBackgroundFillColorSecondaryBrush" />
|
||||
<StaticResource x:Key="ExpanderHeaderBackground" ResourceKey="CardBackgroundFillColorDefaultBrush" />
|
||||
|
||||
<SolidColorBrush x:Key="SystemControlRevealFocusVisualBrush" Color="#FF4C4A48" />
|
||||
<SolidColorBrush x:Key="SystemControlFocusVisualSecondaryBrush" Color="#FFFFFFFF" />
|
||||
|
||||
<DataTemplate x:Key="FolderPivotTemplate" x:DataType="viewModelData:FolderPivotViewModel">
|
||||
<StackPanel Orientation="Horizontal" Spacing="4">
|
||||
<TextBlock Text="{x:Bind FolderTitle}" />
|
||||
<TextBlock
|
||||
x:Name="CountTextBlock"
|
||||
VerticalAlignment="Center"
|
||||
FontWeight="SemiBold"
|
||||
Visibility="{x:Bind ShouldDisplaySelectedItemCount, Mode=OneWay}">
|
||||
<Run Text="(" /><Run Text="{x:Bind SelectedItemCount, Mode=OneWay}" /><Run Text=")" />
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
|
||||
<!-- Header Templates -->
|
||||
<DataTemplate x:Key="MailGroupHeaderDefaultTemplate" x:DataType="collections:IReadOnlyObservableGroup">
|
||||
<Grid
|
||||
Margin="6,2"
|
||||
AllowFocusOnInteraction="False"
|
||||
Background="{ThemeResource MailListHeaderBackgroundColor}"
|
||||
CornerRadius="6">
|
||||
<TextBlock
|
||||
Padding="12"
|
||||
AllowFocusOnInteraction="False"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="13"
|
||||
FontWeight="SemiBold"
|
||||
Text="{x:Bind helpers:XamlHelpers.GetMailGroupDateString(Key)}" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
|
||||
<!--
|
||||
Thread items should not display multi select checkbox.
|
||||
We override one for thread view models, and default items should use
|
||||
what the system has by default. Overriding ListViewItem and ListViewItemPresenter styles
|
||||
are really dangerous since they are somehow messy at system level.
|
||||
-->
|
||||
|
||||
<!-- Thread Item Container Style -->
|
||||
<Style x:Key="ThreadItemContainerStyle" TargetType="ListViewItem">
|
||||
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
|
||||
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
|
||||
<Setter Property="Background" Value="{ThemeResource ListViewItemBackground}" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource ListViewItemForeground}" />
|
||||
<Setter Property="TabNavigation" Value="Local" />
|
||||
<Setter Property="IsHoldingEnabled" Value="True" />
|
||||
<Setter Property="Padding" Value="0" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||
<Setter Property="MinWidth" Value="{ThemeResource ListViewItemMinWidth}" />
|
||||
<Setter Property="MinHeight" Value="{ThemeResource ListViewItemMinHeight}" />
|
||||
<Setter Property="AllowDrop" Value="False" />
|
||||
<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}" />
|
||||
<Setter Property="FocusVisualMargin" Value="0" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ListViewItem">
|
||||
<ListViewItemPresenter
|
||||
x:Name="Root"
|
||||
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
CheckBoxBrush="Transparent"
|
||||
CheckBrush="Transparent"
|
||||
CheckMode="Overlay"
|
||||
ContentMargin="{TemplateBinding Padding}"
|
||||
ContentTransitions="{TemplateBinding ContentTransitions}"
|
||||
Control.IsTemplateFocusTarget="True"
|
||||
CornerRadius="5"
|
||||
DisabledOpacity="{ThemeResource ListViewItemDisabledThemeOpacity}"
|
||||
DragBackground="{ThemeResource ListViewItemDragBackground}"
|
||||
DragForeground="{ThemeResource ListViewItemDragForeground}"
|
||||
DragOpacity="{ThemeResource ListViewItemDragThemeOpacity}"
|
||||
FocusBorderBrush="{ThemeResource ListViewItemFocusBorderBrush}"
|
||||
FocusSecondaryBorderBrush="{ThemeResource ListViewItemFocusSecondaryBorderBrush}"
|
||||
FocusVisualMargin="{TemplateBinding FocusVisualMargin}"
|
||||
PlaceholderBackground="{ThemeResource ListViewItemPlaceholderBackground}"
|
||||
PointerOverBackground="{ThemeResource ListViewItemBackgroundPointerOver}"
|
||||
PointerOverForeground="{ThemeResource ListViewItemForegroundPointerOver}"
|
||||
PressedBackground="{ThemeResource ListViewItemBackgroundPressed}"
|
||||
ReorderHintOffset="{ThemeResource ListViewItemReorderHintThemeOffset}"
|
||||
RevealBackground="{ThemeResource ListViewItemRevealBackground}"
|
||||
RevealBorderBrush="{ThemeResource ListViewItemRevealBorderBrush}"
|
||||
RevealBorderThickness="{ThemeResource ListViewItemRevealBorderThemeThickness}"
|
||||
SelectedBackground="{ThemeResource ListViewItemBackgroundSelected}"
|
||||
SelectedForeground="{ThemeResource ListViewItemForegroundSelected}"
|
||||
SelectedPointerOverBackground="{ThemeResource ListViewItemBackgroundSelectedPointerOver}"
|
||||
SelectedPressedBackground="{ThemeResource ListViewItemBackgroundSelectedPressed}"
|
||||
SelectionCheckMarkVisualEnabled="False">
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="Selected" />
|
||||
<VisualState x:Name="PointerOver">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="Root.(RevealBrush.State)" Value="PointerOver" />
|
||||
<Setter Target="Root.RevealBorderBrush" Value="{ThemeResource ListViewItemRevealBorderBrushPointerOver}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="PointerOverSelected">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="Root.(RevealBrush.State)" Value="PointerOver" />
|
||||
<Setter Target="Root.RevealBorderBrush" Value="{ThemeResource ListViewItemRevealBorderBrushPointerOver}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="PointerOverPressed">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="Root.(RevealBrush.State)" Value="Pressed" />
|
||||
<Setter Target="Root.RevealBorderBrush" Value="{ThemeResource ListViewItemRevealBorderBrushPressed}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Pressed">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="Root.(RevealBrush.State)" Value="Pressed" />
|
||||
<Setter Target="Root.RevealBorderBrush" Value="{ThemeResource ListViewItemRevealBorderBrushPressed}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="PressedSelected">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="Root.(RevealBrush.State)" Value="Pressed" />
|
||||
<Setter Target="Root.RevealBorderBrush" Value="{ThemeResource ListViewItemRevealBorderBrushPressed}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="DisabledStates">
|
||||
<VisualState x:Name="Enabled" />
|
||||
<VisualState x:Name="Disabled">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="Root.RevealBorderThickness" Value="0" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</ListViewItemPresenter>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- Mail Item Container Style Selector -->
|
||||
<selectors:MailItemContainerStyleSelector x:Key="WinoContainerSelector" Thread="{StaticResource ThreadItemContainerStyle}" />
|
||||
|
||||
<!-- Swipe Items -->
|
||||
<!-- Left -->
|
||||
<muxc:SwipeItems x:Key="LeftSwipeItems" Mode="Execute">
|
||||
<muxc:SwipeItem
|
||||
BehaviorOnInvoked="Close"
|
||||
Invoked="LeftSwipeItemInvoked"
|
||||
Text="{x:Bind domain:Translator.MailOperation_Delete}">
|
||||
<muxc:SwipeItem.IconSource>
|
||||
<controls:WinoFontIconSource Icon="Delete" />
|
||||
</muxc:SwipeItem.IconSource>
|
||||
<muxc:SwipeItem.Background>
|
||||
<LinearGradientBrush StartPoint="0,0.5" EndPoint="1,0.5">
|
||||
<GradientStop Offset="0.0" Color="#FFF5A6A6" />
|
||||
<GradientStop Offset="0.5" Color="#FFEF4444" />
|
||||
<GradientStop Offset="1.0" Color="#FFF32525" />
|
||||
</LinearGradientBrush>
|
||||
</muxc:SwipeItem.Background>
|
||||
</muxc:SwipeItem>
|
||||
</muxc:SwipeItems>
|
||||
|
||||
<!-- Right -->
|
||||
<muxc:SwipeItems x:Key="RightSwipeItems" Mode="Execute">
|
||||
<muxc:SwipeItem
|
||||
BehaviorOnInvoked="Close"
|
||||
Invoked="RightSwipeItemInvoked"
|
||||
Text="{x:Bind domain:Translator.MarkReadUnread}">
|
||||
<muxc:SwipeItem.IconSource>
|
||||
<controls:WinoFontIconSource Icon="MarkRead" />
|
||||
</muxc:SwipeItem.IconSource>
|
||||
</muxc:SwipeItem>
|
||||
</muxc:SwipeItems>
|
||||
|
||||
<!-- Single Mail Item Template -->
|
||||
<DataTemplate x:Key="SingleMailItemTemplate" x:DataType="viewModelData:MailItemViewModel">
|
||||
<muxc:SwipeControl
|
||||
LeftItems="{StaticResource LeftSwipeItems}"
|
||||
RightItems="{StaticResource RightSwipeItems}"
|
||||
Tag="{x:Bind}">
|
||||
<controls:MailItemDisplayInformationControl
|
||||
x:DefaultBindMode="OneWay"
|
||||
MailItem="{Binding}"
|
||||
HoverActionExecutedCommand="{Binding ElementName=root, Path=ViewModel.ExecuteHoverActionCommand}"
|
||||
CenterHoverAction="{Binding ElementName=root, Path=ViewModel.PreferencesService.CenterHoverAction, Mode=OneWay}"
|
||||
ContextRequested="MailItemContextRequested"
|
||||
DisplayMode="{Binding ElementName=root, Path=ViewModel.PreferencesService.MailItemDisplayMode, Mode=OneWay}"
|
||||
FocusVisualMargin="8"
|
||||
FocusVisualPrimaryBrush="{StaticResource SystemControlRevealFocusVisualBrush}"
|
||||
FocusVisualPrimaryThickness="2"
|
||||
FocusVisualSecondaryBrush="{StaticResource SystemControlFocusVisualSecondaryBrush}"
|
||||
FocusVisualSecondaryThickness="1"
|
||||
FromAddress="{x:Bind FromAddress}"
|
||||
FromName="{x:Bind FromName}"
|
||||
HasAttachments="{x:Bind HasAttachments}"
|
||||
IsAvatarVisible="{Binding ElementName=root, Path=ViewModel.PreferencesService.IsShowSenderPicturesEnabled, Mode=OneWay}"
|
||||
IsCustomFocused="{x:Bind IsCustomFocused, Mode=OneWay}"
|
||||
IsDraft="{x:Bind IsDraft, Mode=OneWay}"
|
||||
IsFlagged="{x:Bind IsFlagged, Mode=OneWay}"
|
||||
IsRead="{x:Bind IsRead, Mode=OneWay}"
|
||||
LeftHoverAction="{Binding ElementName=root, Path=ViewModel.PreferencesService.LeftHoverAction, Mode=OneWay}"
|
||||
Prefer24HourTimeFormat="{Binding ElementName=root, Path=ViewModel.PreferencesService.Prefer24HourTimeFormat, Mode=OneWay}"
|
||||
ReceivedDate="{x:Bind CreationDate}"
|
||||
IsHoverActionsEnabled="{Binding ElementName=root, Path=ViewModel.PreferencesService.IsHoverActionsEnabled, Mode=OneWay}"
|
||||
RightHoverAction="{Binding ElementName=root, Path=ViewModel.PreferencesService.RightHoverAction, Mode=OneWay}"
|
||||
ShowPreviewText="{Binding ElementName=root, Path=ViewModel.PreferencesService.IsShowPreviewEnabled, Mode=OneWay}"
|
||||
Snippet="{x:Bind PreviewText}"
|
||||
Subject="{x:Bind Subject}" />
|
||||
</muxc:SwipeControl>
|
||||
</DataTemplate>
|
||||
|
||||
<!-- Single Mail Item Template for Threads -->
|
||||
<DataTemplate x:Key="ThreadSingleMailItemTemplate" x:DataType="viewModelData:MailItemViewModel">
|
||||
<muxc:SwipeControl
|
||||
LeftItems="{StaticResource LeftSwipeItems}"
|
||||
RightItems="{StaticResource RightSwipeItems}"
|
||||
Tag="{x:Bind}">
|
||||
<controls:MailItemDisplayInformationControl
|
||||
x:DefaultBindMode="OneWay"
|
||||
MailItem="{Binding}"
|
||||
HoverActionExecutedCommand="{Binding ElementName=root, Path=ViewModel.ExecuteHoverActionCommand}"
|
||||
CenterHoverAction="{Binding ElementName=root, Path=ViewModel.PreferencesService.CenterHoverAction, Mode=OneWay}"
|
||||
ContextRequested="MailItemContextRequested"
|
||||
DisplayMode="{Binding ElementName=root, Path=ViewModel.PreferencesService.MailItemDisplayMode, Mode=OneWay}"
|
||||
FocusVisualMargin="8"
|
||||
IsHoverActionsEnabled="{Binding ElementName=root, Path=ViewModel.PreferencesService.IsHoverActionsEnabled, Mode=OneWay}"
|
||||
FocusVisualPrimaryBrush="{StaticResource SystemControlRevealFocusVisualBrush}"
|
||||
FocusVisualPrimaryThickness="2"
|
||||
FocusVisualSecondaryBrush="{StaticResource SystemControlFocusVisualSecondaryBrush}"
|
||||
FocusVisualSecondaryThickness="1"
|
||||
FromAddress="{x:Bind FromAddress}"
|
||||
FromName="{x:Bind FromName}"
|
||||
HasAttachments="{x:Bind HasAttachments}"
|
||||
IsAvatarVisible="{Binding ElementName=root, Path=ViewModel.PreferencesService.IsShowSenderPicturesEnabled, Mode=OneWay}"
|
||||
Prefer24HourTimeFormat="{Binding ElementName=root, Path=ViewModel.PreferencesService.Prefer24HourTimeFormat, Mode=OneWay}"
|
||||
IsCustomFocused="{x:Bind IsCustomFocused, Mode=OneWay}"
|
||||
IsDraft="{x:Bind IsDraft, Mode=OneWay}"
|
||||
IsFlagged="{x:Bind IsFlagged, Mode=OneWay}"
|
||||
IsRead="{x:Bind IsRead, Mode=OneWay}"
|
||||
LeftHoverAction="{Binding ElementName=root, Path=ViewModel.PreferencesService.LeftHoverAction, Mode=OneWay}"
|
||||
ReceivedDate="{x:Bind CreationDate}"
|
||||
RightHoverAction="{Binding ElementName=root, Path=ViewModel.PreferencesService.RightHoverAction, Mode=OneWay}"
|
||||
ShowPreviewText="{Binding ElementName=root, Path=ViewModel.PreferencesService.IsShowPreviewEnabled, Mode=OneWay}"
|
||||
Snippet="{x:Bind PreviewText}"
|
||||
Subject="{x:Bind Subject}" />
|
||||
</muxc:SwipeControl>
|
||||
</DataTemplate>
|
||||
|
||||
<!-- Mail Item Content Selector -->
|
||||
<selectors:MailItemDisplaySelector x:Key="MailItemDisplaySelector" SingleMailItemTemplate="{StaticResource SingleMailItemTemplate}">
|
||||
<selectors:MailItemDisplaySelector.ThreadMailItemTemplate>
|
||||
<DataTemplate x:DataType="viewModelData:ThreadMailItemViewModel">
|
||||
<muxc:Expander
|
||||
x:Name="ThreadExpander"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
BackgroundSizing="InnerBorderEdge"
|
||||
BorderThickness="0"
|
||||
IsExpanded="{x:Bind IsThreadExpanded, Mode=TwoWay}">
|
||||
<muxc:Expander.Header>
|
||||
<controls:MailItemDisplayInformationControl
|
||||
x:DefaultBindMode="OneWay"
|
||||
Background="Transparent"
|
||||
BorderThickness="0"
|
||||
MailItem="{Binding}"
|
||||
Prefer24HourTimeFormat="{Binding ElementName=root, Path=ViewModel.PreferencesService.Prefer24HourTimeFormat, Mode=OneWay}"
|
||||
CanDrag="True"
|
||||
IsHoverActionsEnabled="{Binding ElementName=root, Path=ViewModel.PreferencesService.IsHoverActionsEnabled, Mode=OneWay}"
|
||||
HoverActionExecutedCommand="{Binding ElementName=root, Path=ViewModel.ExecuteHoverActionCommand}"
|
||||
LeftHoverAction="{Binding ElementName=root, Path=ViewModel.PreferencesService.LeftHoverAction, Mode=OneWay}"
|
||||
CenterHoverAction="{Binding ElementName=root, Path=ViewModel.PreferencesService.CenterHoverAction, Mode=OneWay}"
|
||||
RightHoverAction="{Binding ElementName=root, Path=ViewModel.PreferencesService.RightHoverAction, Mode=OneWay}"
|
||||
ConnectedExpander="{Binding ElementName=ThreadExpander}"
|
||||
ContextRequested="MailItemContextRequested"
|
||||
DisplayMode="{Binding ElementName=root, Path=ViewModel.PreferencesService.MailItemDisplayMode, Mode=OneWay}"
|
||||
DragStarting="ThreadHeaderDragStart"
|
||||
DropCompleted="ThreadHeaderDragFinished"
|
||||
FromAddress="{x:Bind FromAddress}"
|
||||
FromName="{x:Bind FromName}"
|
||||
IsHitTestVisible="True"
|
||||
HasAttachments="{x:Bind HasAttachments}"
|
||||
IsAvatarVisible="{Binding ElementName=root, Path=ViewModel.PreferencesService.IsShowSenderPicturesEnabled, Mode=OneWay}"
|
||||
IsDraft="{x:Bind IsDraft}"
|
||||
IsFlagged="{x:Bind IsFlagged}"
|
||||
IsRead="{x:Bind IsRead}"
|
||||
ReceivedDate="{x:Bind CreationDate}"
|
||||
ShowPreviewText="{Binding ElementName=root, Path=ViewModel.PreferencesService.IsShowPreviewEnabled, Mode=OneWay}"
|
||||
Snippet="{x:Bind PreviewText}"
|
||||
Subject="{x:Bind Subject}" />
|
||||
</muxc:Expander.Header>
|
||||
<muxc:Expander.Content>
|
||||
<listview:WinoListView
|
||||
x:Name="ThreadItemsList"
|
||||
ui:ListViewExtensions.ItemContainerStretchDirection="Horizontal"
|
||||
IsThreadListView="True"
|
||||
ItemTemplate="{StaticResource ThreadSingleMailItemTemplate}"
|
||||
ItemsSource="{x:Bind ThreadItems}"
|
||||
ProcessKeyboardAccelerators="ProcessMailItemKeyboardAccelerator"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Hidden">
|
||||
<ListView.ItemContainerTransitions>
|
||||
<TransitionCollection>
|
||||
<EdgeUIThemeTransition />
|
||||
</TransitionCollection>
|
||||
</ListView.ItemContainerTransitions>
|
||||
</listview:WinoListView>
|
||||
</muxc:Expander.Content>
|
||||
</muxc:Expander>
|
||||
</DataTemplate>
|
||||
</selectors:MailItemDisplaySelector.ThreadMailItemTemplate>
|
||||
</selectors:MailItemDisplaySelector>
|
||||
|
||||
<SolidColorBrush x:Key="ButtonBackgroundDisabled">Transparent</SolidColorBrush>
|
||||
|
||||
<Style
|
||||
x:Key="TopCommandBarButtonStyle"
|
||||
TargetType="Button"
|
||||
BasedOn="{StaticResource DefaultButtonStyle}">
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
||||
<Setter Property="Padding" Value="12" />
|
||||
</Style>
|
||||
|
||||
<Style
|
||||
x:Key="TopCommandBarToggleButtonStyle"
|
||||
TargetType="ToggleButton"
|
||||
BasedOn="{StaticResource DefaultToggleButtonStyle}">
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
||||
<Setter Property="Padding" Value="12" />
|
||||
</Style>
|
||||
</Page.Resources>
|
||||
|
||||
<wino:BasePage.ShellContent>
|
||||
<AutoSuggestBox
|
||||
x:Name="SearchBar"
|
||||
Margin="0,0,8,0"
|
||||
VerticalAlignment="Center"
|
||||
BorderBrush="Transparent"
|
||||
GotFocus="SearchBoxFocused"
|
||||
IsFocusEngagementEnabled="False"
|
||||
IsTabStop="False"
|
||||
LostFocus="SearchBarUnfocused"
|
||||
PlaceholderText="{x:Bind domain:Translator.SearchBarPlaceholder}"
|
||||
QueryIcon="Find"
|
||||
TabIndex="1000"
|
||||
Text="{x:Bind ViewModel.SearchQuery, Mode=TwoWay}">
|
||||
<i:Interaction.Behaviors>
|
||||
<ic:EventTriggerBehavior EventName="QuerySubmitted">
|
||||
<ic:InvokeCommandAction Command="{x:Bind ViewModel.PerformSearchCommand}" />
|
||||
</ic:EventTriggerBehavior>
|
||||
</i:Interaction.Behaviors>
|
||||
</AutoSuggestBox>
|
||||
</wino:BasePage.ShellContent>
|
||||
|
||||
<Grid x:Name="RootGrid">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition x:Name="ReaderColumn" Width="Auto" />
|
||||
<ColumnDefinition x:Name="RendererColumn" Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Mail Items -->
|
||||
<Grid
|
||||
x:Name="ReaderGrid"
|
||||
MaxWidth="{x:Bind ViewModel.StatePersistanceService.MailListPaneLength, Mode=OneWay}"
|
||||
Margin="-1,0,0,0"
|
||||
Padding="4,0,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
Background="{ThemeResource ReadingPaneBackgroundColorBrush}"
|
||||
CornerRadius="{ThemeResource MailListGridCornerRadius}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Top Area -->
|
||||
<Grid
|
||||
Padding="6"
|
||||
CornerRadius="8"
|
||||
RowSpacing="4">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Top Commands -->
|
||||
<Grid
|
||||
Grid.Row="0"
|
||||
Padding="2,0"
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
|
||||
CornerRadius="8">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="48" />
|
||||
<RowDefinition Height="2" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Commands -->
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button
|
||||
Style="{StaticResource TopCommandBarButtonStyle}"
|
||||
Command="{x:Bind ViewModel.SyncFolderCommand}"
|
||||
IsEnabled="{x:Bind ViewModel.CanSynchronize, Mode=OneWay}">
|
||||
<Button.Content>
|
||||
<controls:WinoFontIcon Icon="Sync" FontSize="16" />
|
||||
</Button.Content>
|
||||
</Button>
|
||||
|
||||
<ToggleButton
|
||||
x:Name="SelectionModeToggle"
|
||||
Grid.Column="1"
|
||||
Style="{StaticResource TopCommandBarToggleButtonStyle}"
|
||||
Checked="SelectionModeToggleChecked"
|
||||
Unchecked="SelectionModeToggleUnchecked">
|
||||
<ToggleButton.Content>
|
||||
<controls:WinoFontIcon FontSize="18" Icon="MultiSelect" />
|
||||
</ToggleButton.Content>
|
||||
</ToggleButton>
|
||||
|
||||
<AppBarSeparator Grid.Column="2" Margin="2,0" />
|
||||
|
||||
<Button
|
||||
x:Name="ArchiveAppBarButton"
|
||||
Grid.Column="3"
|
||||
x:Load="{x:Bind helpers:XamlHelpers.ReverseBoolConverter(ViewModel.IsArchiveSpecialFolder), Mode=OneWay}"
|
||||
Style="{StaticResource TopCommandBarButtonStyle}"
|
||||
Command="{x:Bind ViewModel.MailOperationCommand}"
|
||||
IsEnabled="{x:Bind ViewModel.HasSelectedItems, Mode=OneWay}"
|
||||
ToolTipService.ToolTip="{x:Bind domain:Translator.MailOperation_Archive}">
|
||||
<Button.Content>
|
||||
<controls:WinoFontIcon FontSize="18" Icon="Archive" />
|
||||
</Button.Content>
|
||||
<Button.CommandParameter>
|
||||
<enums:MailOperation>Archive</enums:MailOperation>
|
||||
</Button.CommandParameter>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
x:Name="UnarchiveAppBarButton"
|
||||
Grid.Column="3"
|
||||
x:Load="{x:Bind ViewModel.IsArchiveSpecialFolder, Mode=OneWay}"
|
||||
Style="{StaticResource TopCommandBarButtonStyle}"
|
||||
Command="{x:Bind ViewModel.MailOperationCommand}"
|
||||
IsEnabled="{x:Bind ViewModel.HasSelectedItems, Mode=OneWay}"
|
||||
ToolTipService.ToolTip="{x:Bind domain:Translator.MailOperation_Unarchive}">
|
||||
<Button.Content>
|
||||
<controls:WinoFontIcon FontSize="18" Icon="UnArchive" />
|
||||
</Button.Content>
|
||||
<Button.CommandParameter>
|
||||
<enums:MailOperation>UnArchive</enums:MailOperation>
|
||||
</Button.CommandParameter>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
Grid.Column="4"
|
||||
Style="{StaticResource TopCommandBarButtonStyle}"
|
||||
Command="{x:Bind ViewModel.MailOperationCommand}"
|
||||
IsEnabled="{x:Bind ViewModel.HasSelectedItems, Mode=OneWay}"
|
||||
ToolTipService.ToolTip="{x:Bind domain:Translator.MailOperation_Delete}">
|
||||
<Button.CommandParameter>
|
||||
<enums:MailOperation>SoftDelete</enums:MailOperation>
|
||||
</Button.CommandParameter>
|
||||
<Button.Content>
|
||||
<controls:WinoFontIcon FontSize="18" Icon="Delete" />
|
||||
</Button.Content>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
x:Name="MoveButtonAppBarButton"
|
||||
Grid.Column="5"
|
||||
Command="{x:Bind ViewModel.MailOperationCommand}"
|
||||
IsEnabled="{x:Bind ViewModel.HasSelectedItems, Mode=OneWay}"
|
||||
Style="{StaticResource TopCommandBarButtonStyle}"
|
||||
ToolTipService.ToolTip="{x:Bind domain:Translator.MailOperation_Move}">
|
||||
<Button.CommandParameter>
|
||||
<enums:MailOperation>Move</enums:MailOperation>
|
||||
</Button.CommandParameter>
|
||||
<Button.Content>
|
||||
<controls:WinoFontIcon FontSize="18" Icon="Move" />
|
||||
</Button.Content>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
Grid.Column="6"
|
||||
Style="{StaticResource TopCommandBarButtonStyle}"
|
||||
IsEnabled="{x:Bind ViewModel.HasSelectedItems, Mode=OneWay}">
|
||||
<Button.Content>
|
||||
<controls:WinoFontIcon FontSize="20" Icon="More" />
|
||||
</Button.Content>
|
||||
<Button.Flyout>
|
||||
<MenuFlyout AreOpenCloseAnimationsEnabled="False" Placement="BottomEdgeAlignedLeft">
|
||||
<MenuFlyoutItem Command="{x:Bind ViewModel.MailOperationCommand}" Text="{x:Bind domain:Translator.MailOperation_SetFlag}">
|
||||
<MenuFlyoutItem.CommandParameter>
|
||||
<enums:MailOperation>SetFlag</enums:MailOperation>
|
||||
</MenuFlyoutItem.CommandParameter>
|
||||
<MenuFlyoutItem.Icon>
|
||||
<controls:WinoFontIcon Icon="Flag" />
|
||||
</MenuFlyoutItem.Icon>
|
||||
</MenuFlyoutItem>
|
||||
<MenuFlyoutItem Command="{x:Bind ViewModel.MailOperationCommand}" Text="{x:Bind domain:Translator.MailOperation_ClearFlag}">
|
||||
<MenuFlyoutItem.CommandParameter>
|
||||
<enums:MailOperation>ClearFlag</enums:MailOperation>
|
||||
</MenuFlyoutItem.CommandParameter>
|
||||
<MenuFlyoutItem.Icon>
|
||||
<controls:WinoFontIcon Icon="ClearFlag" />
|
||||
</MenuFlyoutItem.Icon>
|
||||
</MenuFlyoutItem>
|
||||
<MenuFlyoutItem Command="{x:Bind ViewModel.MailOperationCommand}" Text="{x:Bind domain:Translator.MailOperation_MarkAsRead}">
|
||||
<MenuFlyoutItem.CommandParameter>
|
||||
<enums:MailOperation>MarkAsRead</enums:MailOperation>
|
||||
</MenuFlyoutItem.CommandParameter>
|
||||
<MenuFlyoutItem.Icon>
|
||||
<controls:WinoFontIcon Icon="MarkRead" />
|
||||
</MenuFlyoutItem.Icon>
|
||||
</MenuFlyoutItem>
|
||||
<MenuFlyoutItem Command="{x:Bind ViewModel.MailOperationCommand}" Text="{x:Bind domain:Translator.MailOperation_MarkAsUnread}">
|
||||
<MenuFlyoutItem.CommandParameter>
|
||||
<enums:MailOperation>MarkAsUnread</enums:MailOperation>
|
||||
</MenuFlyoutItem.CommandParameter>
|
||||
<MenuFlyoutItem.Icon>
|
||||
<controls:WinoFontIcon Icon="MarkUnread" />
|
||||
</MenuFlyoutItem.Icon>
|
||||
</MenuFlyoutItem>
|
||||
</MenuFlyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<muxc:ProgressBar
|
||||
x:Name="LoadingProgressBar"
|
||||
Grid.Row="1"
|
||||
IsIndeterminate="True"
|
||||
x:Load="{x:Bind ViewModel.IsInitializingFolder, Mode=OneWay}" />
|
||||
</Grid>
|
||||
|
||||
<!-- Search Mode Panel -->
|
||||
<Grid
|
||||
x:Name="SearchModePanel"
|
||||
Grid.Row="1"
|
||||
Padding="12"
|
||||
x:Load="{x:Bind ViewModel.IsInSearchMode, Mode=OneWay}"
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
|
||||
CornerRadius="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16"
|
||||
FontWeight="SemiBold"
|
||||
Text="{x:Bind domain:Translator.Results}" />
|
||||
|
||||
<Button
|
||||
x:Name="BackToFolderButton"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Right"
|
||||
x:Load="{x:Bind helpers:XamlHelpers.ReverseBoolConverter(ViewModel.IsPerformingSearch), Mode=OneWay}">
|
||||
<Viewbox Width="16">
|
||||
<PathIcon Data="M512,584.5L87.5,1009C77.5,1019 65.5,1024 51.5,1024C36.8333,1024 24.5833,1019.08 14.75,1009.25C4.91667,999.417 0,987.167 0,972.5C0,958.5 5,946.5 15,936.5L439.5,512L15,87.5C5,77.5 0,65.3334 0,51C0,44 1.33333,37.3334 4,31C6.66667,24.6667 10.3333,19.25 15,14.75C19.6667,10.25 25.1667,6.66669 31.5,4C37.8333,1.33337 44.5,0 51.5,0C65.5,0 77.5,5 87.5,15L512,439.5L936.5,15C946.5,5 958.667,0 973,0C980,0 986.583,1.33337 992.75,4C998.917,6.66669 1004.33,10.3334 1009,15C1013.67,19.6667 1017.33,25.0834 1020,31.25C1022.67,37.4167 1024,44 1024,51C1024,65.3334 1019,77.5 1009,87.5L584.5,512L1009,936.5C1019,946.5 1024,958.5 1024,972.5C1024,979.5 1022.67,986.167 1020,992.5C1017.33,998.833 1013.75,1004.33 1009.25,1009C1004.75,1013.67 999.333,1017.33 993,1020C986.667,1022.67 980,1024 973,1024C958.667,1024 946.5,1019 936.5,1009Z" />
|
||||
</Viewbox>
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<!-- Pivot + Sync + Multi Select -->
|
||||
<Grid
|
||||
Grid.Row="1"
|
||||
Margin="0,0,0,5"
|
||||
Visibility="{x:Bind helpers:XamlHelpers.ReverseBoolToVisibilityConverter(ViewModel.IsInSearchMode), Mode=OneWay}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Select All Checkbox -->
|
||||
<CheckBox
|
||||
x:Name="SelectAllCheckbox"
|
||||
Grid.Row="1"
|
||||
MinWidth="0"
|
||||
Margin="8,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Canvas.ZIndex="100"
|
||||
Checked="SelectAllCheckboxChecked"
|
||||
Unchecked="SelectAllCheckboxUnchecked"
|
||||
Visibility="{x:Bind helpers:XamlHelpers.IsSelectionModeMultiple(MailListView.SelectionMode), Mode=OneWay}" />
|
||||
|
||||
<!-- Folders -->
|
||||
<controls:WinoPivotControl
|
||||
x:Name="WinoPivot"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
DataTemplate="{StaticResource FolderPivotTemplate}"
|
||||
ItemsSource="{x:Bind ViewModel.PivotFolders, Mode=OneWay}"
|
||||
SelectedItem="{x:Bind ViewModel.SelectedFolderPivot, Mode=TwoWay}"
|
||||
SelectionChanged="FolderPivotChanged"
|
||||
SelectorPipeColor="{ThemeResource NavigationViewSelectionIndicatorForeground}" />
|
||||
|
||||
<!-- Filtering -->
|
||||
<muxc:DropDownButton
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Content="{x:Bind ViewModel.SelectedFilterOption.Title, Mode=OneWay}"
|
||||
ToolTipService.ToolTip="Filter">
|
||||
<muxc:DropDownButton.Flyout>
|
||||
<menuflyouts:FilterMenuFlyout
|
||||
x:Name="FilterMenuFlyout"
|
||||
AreOpenCloseAnimationsEnabled="True"
|
||||
FilterOptions="{x:Bind ViewModel.FilterOptions, Mode=OneTime}"
|
||||
Placement="Bottom"
|
||||
SelectedFilterChangedCommand="{x:Bind ViewModel.SelectedFilterChangedCommand}"
|
||||
SelectedFilterOption="{x:Bind ViewModel.SelectedFilterOption, Mode=TwoWay}"
|
||||
SelectedSortingOption="{x:Bind ViewModel.SelectedSortingOption, Mode=TwoWay}"
|
||||
SelectedSortingOptionChangedCommand="{x:Bind ViewModel.SelectedSortingChangedCommand}"
|
||||
SortingOptions="{x:Bind ViewModel.SortingOptions, Mode=OneTime}" />
|
||||
</muxc:DropDownButton.Flyout>
|
||||
</muxc:DropDownButton>
|
||||
|
||||
<muxc:InfoBar
|
||||
Title="{x:Bind domain:Translator.InfoBarTitle_SynchronizationDisabledFolder}"
|
||||
Grid.Row="0"
|
||||
Grid.ColumnSpan="3"
|
||||
IsClosable="True"
|
||||
IsOpen="{x:Bind ViewModel.IsFolderSynchronizationEnabled, Converter={StaticResource ReverseBooleanConverter}, Mode=OneWay}"
|
||||
Message="{x:Bind domain:Translator.InfoBarMessage_SynchronizationDisabledFolder}"
|
||||
Severity="Informational">
|
||||
<muxc:InfoBar.ActionButton>
|
||||
<Button Command="{x:Bind ViewModel.EnableFolderSynchronizationCommand}" Content="Enable" />
|
||||
</muxc:InfoBar.ActionButton>
|
||||
</muxc:InfoBar>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<!-- No items createria -->
|
||||
<StackPanel
|
||||
x:Name="NoItemsPanel"
|
||||
Grid.Row="0"
|
||||
Grid.RowSpan="3"
|
||||
VerticalAlignment="Center"
|
||||
Opacity="0.4"
|
||||
Spacing="10">
|
||||
|
||||
<Viewbox Width="48" Visibility="{x:Bind ViewModel.IsEmpty, Mode=OneWay}">
|
||||
<Path Data="M252,960C227,960 203.083,954.917 180.25,944.75C157.417,934.583 137.417,920.917 120.25,903.75C103.083,886.583 89.4167,866.583 79.25,843.75C69.0833,820.917 64,797 64,772L64,252C64,227 69.0833,203.083 79.25,180.25C89.4167,157.417 103.083,137.417 120.25,120.25C137.417,103.083 157.417,89.4167 180.25,79.25C203.083,69.0834 227,64.0001 252,64L772,64C797,64.0001 820.917,69.0834 843.75,79.25C866.583,89.4167 886.583,103.083 903.75,120.25C920.917,137.417 934.583,157.417 944.75,180.25C954.917,203.083 960,227 960,252L960,772C960,797 954.917,820.917 944.75,843.75C934.583,866.583 920.917,886.583 903.75,903.75C886.583,920.917 866.583,934.583 843.75,944.75C820.917,954.917 797,960 772,960ZM770.5,896C787.167,896 803.083,892.583 818.25,885.75C833.417,878.917 846.75,869.75 858.25,858.25C869.75,846.75 878.917,833.417 885.75,818.25C892.583,803.083 896,787.167 896,770.5L896,253.5C896,236.833 892.583,220.917 885.75,205.75C878.917,190.583 869.75,177.25 858.25,165.75C846.75,154.25 833.417,145.083 818.25,138.25C803.083,131.417 787.167,128 770.5,128L253.5,128C236.833,128 220.917,131.417 205.75,138.25C190.583,145.083 177.25,154.25 165.75,165.75C154.25,177.25 145.083,190.583 138.25,205.75C131.417,220.917 128,236.833 128,253.5L128,770.5C128,787.167 131.417,803.083 138.25,818.25C145.083,833.417 154.25,846.75 165.75,858.25C177.25,869.75 190.583,878.917 205.75,885.75C220.917,892.583 236.833,896 253.5,896ZM559,604.5C541.667,616.833 523.417,625.833 504.25,631.5C485.083,637.167 465,640 444,640C418,640 393.583,634.833 370.75,624.5C347.917,614.167 328,600.167 311,582.5C294,564.833 280.583,544.417 270.75,521.25C260.917,498.083 256,473.667 256,448C256,421.333 261,396.333 271,373C281,349.667 294.667,329.333 312,312C329.333,294.667 349.667,281 373,271C396.333,261 421.333,256 448,256C473.667,256 498.083,260.917 521.25,270.75C544.417,280.583 564.833,294 582.5,311C600.167,328 614.167,347.917 624.5,370.75C634.833,393.583 640,418 640,444C640,465 637.167,485.083 631.5,504.25C625.833,523.417 616.833,541.667 604.5,559L758.5,713.5C764.833,719.833 768,727.333 768,736C768,744.667 764.833,752.167 758.5,758.5C752.167,764.833 744.667,768 736,768C727.333,768 719.833,764.833 713.5,758.5ZM576,448L576,445.5C576,428.167 572.5,411.917 565.5,396.75C558.5,381.583 549.167,368.333 537.5,357C525.833,345.667 512.25,336.667 496.75,330C481.25,323.333 465,320 448,320C430.333,320 413.75,323.417 398.25,330.25C382.75,337.083 369.25,346.25 357.75,357.75C346.25,369.25 337.083,382.833 330.25,398.5C323.417,414.167 320,430.667 320,448C320,465.667 323.417,482.25 330.25,497.75C337.083,513.25 346.25,526.75 357.75,538.25C369.25,549.75 382.75,558.917 398.25,565.75C413.75,572.583 430.333,576 448,576C465.333,576 481.833,572.583 497.5,565.75C513.167,558.917 526.75,549.75 538.25,538.25C549.75,526.75 558.917,513.25 565.75,497.75C572.583,482.25 576,465.667 576,448Z" Fill="{ThemeResource InformationBrush}" />
|
||||
</Viewbox>
|
||||
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
FontSize="20"
|
||||
FontWeight="ExtraLight"
|
||||
Foreground="{ThemeResource InformationBrush}"
|
||||
Text="{x:Bind domain:Translator.NoMessageCrieteria}"
|
||||
Visibility="{x:Bind ViewModel.IsCriteriaFailed, Mode=OneWay}" />
|
||||
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
FontSize="20"
|
||||
FontWeight="ExtraLight"
|
||||
Foreground="{ThemeResource InformationBrush}"
|
||||
Text="{x:Bind domain:Translator.NoMessageEmptyFolder}"
|
||||
Visibility="{x:Bind ViewModel.IsFolderEmpty, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Mail Items -->
|
||||
<muxc:RefreshContainer
|
||||
Grid.Row="2"
|
||||
RefreshRequested="PullToRefreshRequested"
|
||||
Visibility="{x:Bind ViewModel.IsPerformingSearch, Converter={StaticResource ReverseBooleanToVisibilityConverter}, Mode=OneWay}">
|
||||
<SemanticZoom x:Name="SemanticZoomContainer" CanChangeViews="{x:Bind ViewModel.PreferencesService.IsSemanticZoomEnabled, Mode=OneWay}">
|
||||
<SemanticZoom.ZoomedInView>
|
||||
<listview:WinoListView
|
||||
x:Name="MailListView"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
ui:ListViewExtensions.ItemContainerStretchDirection="Horizontal"
|
||||
LoadMoreCommand="{x:Bind ViewModel.LoadMoreItemsCommand}"
|
||||
ItemContainerStyleSelector="{StaticResource WinoContainerSelector}"
|
||||
ItemTemplateSelector="{StaticResource MailItemDisplaySelector}"
|
||||
ItemsSource="{x:Bind MailCollectionViewSource.View, Mode=OneWay}"
|
||||
ItemDeletedCommand="{x:Bind ViewModel.MailOperationCommand}"
|
||||
ProcessKeyboardAccelerators="ProcessMailItemKeyboardAccelerator"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Auto">
|
||||
<ListView.ItemContainerTransitions>
|
||||
<TransitionCollection>
|
||||
<AddDeleteThemeTransition />
|
||||
</TransitionCollection>
|
||||
</ListView.ItemContainerTransitions>
|
||||
<ListView.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<ItemsStackPanel AreStickyGroupHeadersEnabled="True" />
|
||||
</ItemsPanelTemplate>
|
||||
</ListView.ItemsPanel>
|
||||
<ListView.Resources>
|
||||
<ResourceDictionary>
|
||||
<Style BasedOn="{StaticResource MailListHeaderStyle}" TargetType="ListViewHeaderItem" />
|
||||
</ResourceDictionary>
|
||||
</ListView.Resources>
|
||||
<ListView.GroupStyle>
|
||||
<GroupStyle HeaderTemplate="{StaticResource MailGroupHeaderDefaultTemplate}" HidesIfEmpty="True" />
|
||||
</ListView.GroupStyle>
|
||||
</listview:WinoListView>
|
||||
</SemanticZoom.ZoomedInView>
|
||||
<SemanticZoom.ZoomedOutView>
|
||||
<ListView
|
||||
x:Name="ZoomOutList"
|
||||
x:Load="{x:Bind helpers:XamlHelpers.ReverseBoolConverter(SemanticZoomContainer.IsZoomedInViewActive), Mode=OneWay}"
|
||||
ItemsSource="{x:Bind MailCollectionViewSource.View.CollectionGroups}">
|
||||
<ListView.Resources>
|
||||
<Style TargetType="ListViewItem">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||
<Setter Property="VerticalContentAlignment" Value="Stretch" />
|
||||
<Setter Property="Margin" Value="0,12" />
|
||||
<Setter Property="Padding" Value="0" />
|
||||
</Style>
|
||||
</ListView.Resources>
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="ICollectionViewGroup">
|
||||
<Grid
|
||||
Margin="4,0"
|
||||
Background="{ThemeResource MailListHeaderBackgroundColor}"
|
||||
CornerRadius="4">
|
||||
<TextBlock
|
||||
Margin="12,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontWeight="SemiBold"
|
||||
Text="{x:Bind helpers:XamlHelpers.GetMailGroupDateString(Group)}" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</SemanticZoom.ZoomedOutView>
|
||||
</SemanticZoom>
|
||||
</muxc:RefreshContainer>
|
||||
|
||||
<muxc:ProgressRing
|
||||
x:Name="LoadingProgressIndicator"
|
||||
Grid.Row="2"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
x:Load="{x:Bind ViewModel.IsInitializingFolder, Mode=OneWay}"
|
||||
Canvas.ZIndex="999" />
|
||||
|
||||
<!-- Search Mode Panel -->
|
||||
<Grid
|
||||
x:Name="SearchProgressPanel"
|
||||
Grid.Row="2"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
x:Load="{x:Bind ViewModel.IsInSearchMode, Mode=OneWay}">
|
||||
|
||||
<!-- Searching -->
|
||||
<StackPanel Spacing="6" Visibility="{x:Bind ViewModel.IsPerformingSearch, Mode=OneWay}">
|
||||
<TextBlock HorizontalTextAlignment="Center">
|
||||
<Run Text="{x:Bind domain:Translator.SearchingIn}" /><Run FontWeight="SemiBold" Text="{x:Bind ViewModel.ActiveFolder.FolderName, Mode=OneWay}" />
|
||||
</TextBlock>
|
||||
<muxc:ProgressRing />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- Update Info Bar -->
|
||||
<controls:WinoInfoBar
|
||||
Title="{x:Bind ViewModel.BarTitle, Mode=OneWay}"
|
||||
Grid.Row="2"
|
||||
Margin="6,0,6,6"
|
||||
VerticalAlignment="Bottom"
|
||||
AnimationType="SlideFromBottomToTop"
|
||||
DismissInterval="2"
|
||||
IsClosable="False"
|
||||
IsOpen="{x:Bind ViewModel.IsBarOpen, Mode=TwoWay}"
|
||||
Message="{x:Bind ViewModel.BarMessage, Mode=OneWay}"
|
||||
Severity="{x:Bind helpers:XamlHelpers.InfoBarSeverityConverter(ViewModel.BarSeverity), Mode=OneWay}" />
|
||||
|
||||
</Grid>
|
||||
|
||||
<!-- Mail Rendering Frame -->
|
||||
<Frame
|
||||
x:Name="RenderingFrame"
|
||||
Grid.Column="1"
|
||||
IsNavigationStackEnabled="False" />
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="AdaptiveStates" CurrentStateChanged="AdaptivenessChanged">
|
||||
<VisualState x:Name="NormalState">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="{x:Bind helpers:XamlHelpers.MailListAdaptivityConverter(ViewModel.StatePersistanceService.MailListPaneLength), Mode=OneWay}" />
|
||||
</VisualState.StateTriggers>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="NarrowState">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ReaderGrid.MaxWidth" Value="10000" />
|
||||
</VisualState.Setters>
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="0" />
|
||||
</VisualState.StateTriggers>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
|
||||
<!-- Pane open adjustments -->
|
||||
<VisualStateGroup x:Name="PaneStates">
|
||||
<VisualState x:Name="PaneOpened">
|
||||
<VisualState.Setters>
|
||||
<!--<Setter Target="RootGrid.Margin" Value="-6,0,0,0" />-->
|
||||
<Setter Target="ReaderGrid.Margin" Value="-4,0,0,0" />
|
||||
</VisualState.Setters>
|
||||
<VisualState.StateTriggers>
|
||||
<StateTrigger IsActive="{x:Bind PreferencesService.IsNavigationPaneOpened, Mode=OneWay}" />
|
||||
</VisualState.StateTriggers>
|
||||
</VisualState>
|
||||
<VisualState x:Name="PaneClosed" />
|
||||
</VisualStateGroup>
|
||||
|
||||
<!-- Mail reader states -->
|
||||
<VisualStateGroup x:Name="ReaderStates">
|
||||
<VisualState x:Name="ReaderActive">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ReaderGrid.CornerRadius" Value="0" />
|
||||
</VisualState.Setters>
|
||||
<VisualState.StateTriggers>
|
||||
<StateTrigger IsActive="{x:Bind StatePersistanceService.IsReadingMail, Mode=OneWay}" />
|
||||
</VisualState.StateTriggers>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Grid>
|
||||
</abstract:MailListPageAbstract>
|
||||
|
||||
536
Wino.Mail/Views/MailListPage.xaml.cs
Normal file
536
Wino.Mail/Views/MailListPage.xaml.cs
Normal file
@@ -0,0 +1,536 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.AppCenter.Analytics;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using MoreLinq;
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Media.Animation;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using Wino.Controls;
|
||||
using Wino.Controls.Advanced;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.MailItem;
|
||||
using Wino.Core.Domain.Models.Menus;
|
||||
using Wino.Core.Domain.Models.Navigation;
|
||||
using Wino.Core.Messages.Mails;
|
||||
using Wino.Core.Messages.Shell;
|
||||
using Wino.Mail.ViewModels.Data;
|
||||
using Wino.Mail.ViewModels.Messages;
|
||||
using Wino.MenuFlyouts.Context;
|
||||
using Wino.Views.Abstract;
|
||||
|
||||
namespace Wino.Views
|
||||
{
|
||||
public sealed partial class MailListPage : MailListPageAbstract,
|
||||
IRecipient<ResetSingleMailItemSelectionEvent>,
|
||||
IRecipient<ClearMailSelectionsRequested>,
|
||||
IRecipient<ActiveMailItemChangedEvent>,
|
||||
IRecipient<ActiveMailFolderChangedEvent>,
|
||||
IRecipient<SelectMailItemContainerEvent>,
|
||||
IRecipient<ShellStateUpdated>
|
||||
{
|
||||
private const string NarrowVisualStateKey = "NarrowState";
|
||||
private const string AdaptivenessStatesKey = "AdaptiveStates";
|
||||
|
||||
private IStatePersistanceService StatePersistanceService { get; } = App.Current.Services.GetService<IStatePersistanceService>();
|
||||
private IPreferencesService PreferencesService { get; } = App.Current.Services.GetService<IPreferencesService>();
|
||||
|
||||
public MailListPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
// Bindings.Update();
|
||||
|
||||
// Delegate to ViewModel.
|
||||
if (e.Parameter is NavigateMailFolderEventArgs folderNavigationArgs)
|
||||
{
|
||||
WeakReferenceMessenger.Default.Send(new ActiveMailFolderChangedEvent(folderNavigationArgs.BaseFolderMenuItem, folderNavigationArgs.FolderInitLoadAwaitTask));
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedFrom(e);
|
||||
|
||||
// Force to go to empty page to dispose compose or rendering.
|
||||
if (!(RenderingFrame.Content is IdlePage))
|
||||
{
|
||||
RenderingFrame.Navigate(typeof(IdlePage), null);
|
||||
}
|
||||
|
||||
this.Bindings.StopTracking();
|
||||
|
||||
GC.Collect();
|
||||
}
|
||||
|
||||
private void UpdateSelectAllButtonStatus()
|
||||
{
|
||||
// Check all checkbox if all is selected.
|
||||
// Unhook events to prevent selection overriding.
|
||||
|
||||
SelectAllCheckbox.Checked -= SelectAllCheckboxChecked;
|
||||
SelectAllCheckbox.Unchecked -= SelectAllCheckboxUnchecked;
|
||||
|
||||
SelectAllCheckbox.IsChecked = MailListView.Items.Count > 0 && MailListView.SelectedItems.Count == MailListView.Items.Count;
|
||||
|
||||
SelectAllCheckbox.Checked += SelectAllCheckboxChecked;
|
||||
SelectAllCheckbox.Unchecked += SelectAllCheckboxUnchecked;
|
||||
}
|
||||
|
||||
private void SelectionModeToggleChecked(object sender, Windows.UI.Xaml.RoutedEventArgs e)
|
||||
{
|
||||
ChangeSelectionMode(ListViewSelectionMode.Multiple);
|
||||
}
|
||||
|
||||
private void FolderPivotChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
foreach (var addedItem in e.AddedItems)
|
||||
{
|
||||
if (addedItem is FolderPivotViewModel pivotItem)
|
||||
{
|
||||
pivotItem.IsSelected = true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var removedItem in e.RemovedItems)
|
||||
{
|
||||
if (removedItem is FolderPivotViewModel pivotItem)
|
||||
{
|
||||
pivotItem.IsSelected = false;
|
||||
}
|
||||
}
|
||||
|
||||
SelectAllCheckbox.IsChecked = false;
|
||||
SelectionModeToggle.IsChecked = false;
|
||||
|
||||
MailListView.ClearSelections();
|
||||
|
||||
UpdateSelectAllButtonStatus();
|
||||
ViewModel.SelectedPivotChangedCommand.Execute(null);
|
||||
}
|
||||
|
||||
private void ChangeSelectionMode(ListViewSelectionMode mode)
|
||||
{
|
||||
MailListView.ChangeSelectionMode(mode);
|
||||
|
||||
if (ViewModel?.PivotFolders != null)
|
||||
{
|
||||
ViewModel.PivotFolders.ForEach(a => a.IsExtendedMode = mode == ListViewSelectionMode.Extended);
|
||||
}
|
||||
}
|
||||
|
||||
private void SelectionModeToggleUnchecked(object sender, Windows.UI.Xaml.RoutedEventArgs e)
|
||||
{
|
||||
ChangeSelectionMode(ListViewSelectionMode.Extended);
|
||||
}
|
||||
|
||||
private void SelectAllCheckboxChecked(object sender, Windows.UI.Xaml.RoutedEventArgs e)
|
||||
{
|
||||
MailListView.SelectAllWino();
|
||||
}
|
||||
|
||||
private void SelectAllCheckboxUnchecked(object sender, Windows.UI.Xaml.RoutedEventArgs e)
|
||||
{
|
||||
MailListView.ClearSelections();
|
||||
}
|
||||
|
||||
#region Mostly UI
|
||||
|
||||
private void UpdateAdaptiveness()
|
||||
{
|
||||
ReaderGrid.Visibility = !(StatePersistanceService.IsReadingMail && StatePersistanceService.IsReaderNarrowed) ? Visibility.Visible : Visibility.Collapsed;
|
||||
RenderingFrame.Visibility = StatePersistanceService.IsReadingMail ? Visibility.Visible : (StatePersistanceService.IsReaderNarrowed ? Visibility.Collapsed : Visibility.Visible);
|
||||
|
||||
if (RenderingFrame.Visibility == Visibility.Collapsed)
|
||||
{
|
||||
Grid.SetColumn(ReaderGrid, 0);
|
||||
Grid.SetColumnSpan(ReaderGrid, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
Grid.SetColumn(ReaderGrid, 0);
|
||||
Grid.SetColumnSpan(ReaderGrid, 1);
|
||||
}
|
||||
|
||||
if (ReaderGrid.Visibility == Visibility.Collapsed)
|
||||
{
|
||||
Grid.SetColumn(RenderingFrame, 0);
|
||||
Grid.SetColumnSpan(RenderingFrame, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
Grid.SetColumn(RenderingFrame, 1);
|
||||
Grid.SetColumnSpan(RenderingFrame, 1);
|
||||
}
|
||||
}
|
||||
|
||||
private void AdaptivenessChanged(object sender, VisualStateChangedEventArgs e)
|
||||
{
|
||||
StatePersistanceService.IsReaderNarrowed = e.NewState.Name == "NarrowState";
|
||||
|
||||
UpdateAdaptiveness();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
void IRecipient<ResetSingleMailItemSelectionEvent>.Receive(ResetSingleMailItemSelectionEvent message)
|
||||
{
|
||||
// Single item in thread selected.
|
||||
// Force main list view to unselect all items, except for the one provided.
|
||||
|
||||
MailListView.ClearSelections(message.SelectedViewModel);
|
||||
}
|
||||
|
||||
private async void MailItemContextRequested(UIElement sender, ContextRequestedEventArgs args)
|
||||
{
|
||||
// Context is requested from a single mail point, but we might have multiple selected items.
|
||||
// This menu should be calculated based on all selected items by providers.
|
||||
|
||||
if (sender is MailItemDisplayInformationControl control && args.TryGetPosition(sender, out Point p))
|
||||
{
|
||||
await FocusManager.TryFocusAsync(control, FocusState.Keyboard);
|
||||
|
||||
if (control.DataContext is IMailItem clickedMailItemContext)
|
||||
{
|
||||
var targetItems = ViewModel.GetTargetMailItemViewModels(clickedMailItemContext);
|
||||
var availableActions = ViewModel.GetAvailableMailActions(targetItems);
|
||||
|
||||
if (!availableActions?.Any() ?? false) return;
|
||||
var t = targetItems.ElementAt(0);
|
||||
|
||||
ViewModel.ChangeCustomFocusedState(targetItems, true);
|
||||
|
||||
var clickedOperation = await GetMailOperationFromFlyoutAsync(availableActions, control, p.X, p.Y);
|
||||
|
||||
ViewModel.ChangeCustomFocusedState(targetItems, false);
|
||||
|
||||
if (clickedOperation == null) return;
|
||||
|
||||
var prepRequest = new MailOperationPreperationRequest(clickedOperation.Operation, targetItems.Select(a => a.MailCopy));
|
||||
|
||||
await ViewModel.ExecuteMailOperationAsync(prepRequest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<MailOperationMenuItem> GetMailOperationFromFlyoutAsync(IEnumerable<MailOperationMenuItem> availableActions,
|
||||
UIElement showAtElement,
|
||||
double x,
|
||||
double y)
|
||||
{
|
||||
var source = new TaskCompletionSource<MailOperationMenuItem>();
|
||||
|
||||
var flyout = new MailOperationFlyout(availableActions, source);
|
||||
|
||||
flyout.ShowAt(showAtElement, new FlyoutShowOptions()
|
||||
{
|
||||
ShowMode = FlyoutShowMode.Standard,
|
||||
Position = new Point(x + 30, y - 20)
|
||||
});
|
||||
|
||||
return await source.Task;
|
||||
}
|
||||
|
||||
void IRecipient<ClearMailSelectionsRequested>.Receive(ClearMailSelectionsRequested message)
|
||||
{
|
||||
MailListView.ClearSelections(null, preserveThreadExpanding: true);
|
||||
}
|
||||
|
||||
void IRecipient<ActiveMailItemChangedEvent>.Receive(ActiveMailItemChangedEvent message)
|
||||
{
|
||||
// No active mail item. Go to empty page.
|
||||
if (message.SelectedMailItemViewModel == null)
|
||||
{
|
||||
WeakReferenceMessenger.Default.Send(new CancelRenderingContentRequested());
|
||||
|
||||
ViewModel.NavigationService.Navigate(WinoPage.IdlePage, ViewModel.SelectedItemCount, NavigationReferenceFrame.RenderingFrame);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Navigate to composing page.
|
||||
if (message.SelectedMailItemViewModel.IsDraft)
|
||||
{
|
||||
NavigationTransitionType composerPageTransition = NavigationTransitionType.None;
|
||||
|
||||
// Dispose active rendering if there is any and go to composer.
|
||||
if (IsRenderingPageActive())
|
||||
{
|
||||
// Prepare WebView2 animation from Rendering to Composing page.
|
||||
PrepareRenderingPageWebViewTransition();
|
||||
|
||||
// Dispose existing HTML content from rendering page webview.
|
||||
WeakReferenceMessenger.Default.Send(new CancelRenderingContentRequested());
|
||||
}
|
||||
else if (IsComposingPageActive())
|
||||
{
|
||||
// Composer is already active. Prepare composer WebView2 animation.
|
||||
PrepareComposePageWebViewTransition();
|
||||
}
|
||||
else
|
||||
composerPageTransition = NavigationTransitionType.DrillIn;
|
||||
|
||||
ViewModel.NavigationService.NavigateCompose(message.SelectedMailItemViewModel, composerPageTransition);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Find the MIME and go to rendering page.
|
||||
|
||||
if (message.SelectedMailItemViewModel == null) return;
|
||||
|
||||
|
||||
if (IsComposingPageActive())
|
||||
{
|
||||
PrepareComposePageWebViewTransition();
|
||||
}
|
||||
|
||||
ViewModel.NavigationService.NavigateRendering(message.SelectedMailItemViewModel);
|
||||
}
|
||||
}
|
||||
|
||||
UpdateAdaptiveness();
|
||||
}
|
||||
|
||||
private bool IsRenderingPageActive() => RenderingFrame.Content is MailRenderingPage;
|
||||
private bool IsComposingPageActive() => RenderingFrame.Content is ComposePage;
|
||||
|
||||
private void PrepareComposePageWebViewTransition()
|
||||
{
|
||||
var webView = GetComposerPageWebView();
|
||||
|
||||
if (webView != null)
|
||||
{
|
||||
var animation = ConnectedAnimationService.GetForCurrentView().PrepareToAnimate("WebViewConnectedAnimation", webView);
|
||||
animation.Configuration = new BasicConnectedAnimationConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
private void PrepareRenderingPageWebViewTransition()
|
||||
{
|
||||
var webView = GetRenderingPageWebView();
|
||||
|
||||
if (webView != null)
|
||||
{
|
||||
var animation = ConnectedAnimationService.GetForCurrentView().PrepareToAnimate("WebViewConnectedAnimation", webView);
|
||||
animation.Configuration = new BasicConnectedAnimationConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
#region Connected Animation Helpers
|
||||
|
||||
private WebView2 GetRenderingPageWebView()
|
||||
{
|
||||
if (RenderingFrame.Content is MailRenderingPage renderingPage)
|
||||
return renderingPage.GetWebView();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private WebView2 GetComposerPageWebView()
|
||||
{
|
||||
if (RenderingFrame.Content is ComposePage composePage)
|
||||
return composePage.GetWebView();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void Receive(ActiveMailFolderChangedEvent message)
|
||||
{
|
||||
if (!(RenderingFrame.Content is IdlePage))
|
||||
{
|
||||
RenderingFrame.Navigate(typeof(IdlePage), null);
|
||||
}
|
||||
|
||||
UpdateAdaptiveness();
|
||||
}
|
||||
|
||||
public async void Receive(SelectMailItemContainerEvent message)
|
||||
{
|
||||
if (message.SelectedMailViewModel == null) return;
|
||||
|
||||
await ViewModel.ExecuteUIThread(async () =>
|
||||
{
|
||||
MailListView.ClearSelections(message.SelectedMailViewModel, true);
|
||||
|
||||
int retriedSelectionCount = 0;
|
||||
trySelection:
|
||||
|
||||
bool isSelected = MailListView.SelectMailItemContainer(message.SelectedMailViewModel);
|
||||
|
||||
if (!isSelected)
|
||||
{
|
||||
for (int i = retriedSelectionCount; i < 5;)
|
||||
{
|
||||
// Retry with delay until the container is realized. Max 1 second.
|
||||
await Task.Delay(200);
|
||||
|
||||
retriedSelectionCount++;
|
||||
|
||||
goto trySelection;
|
||||
}
|
||||
}
|
||||
|
||||
// Automatically scroll to the selected item.
|
||||
// This is useful when creating draft.
|
||||
if (isSelected && message.ScrollToItem)
|
||||
{
|
||||
var collectionContainer = ViewModel.MailCollection.GetMailItemContainer(message.SelectedMailViewModel.UniqueId);
|
||||
|
||||
// Scroll to thread if available.
|
||||
if (collectionContainer.ThreadViewModel != null)
|
||||
{
|
||||
MailListView.ScrollIntoView(collectionContainer.ThreadViewModel, ScrollIntoViewAlignment.Default);
|
||||
}
|
||||
else if (collectionContainer.ItemViewModel != null)
|
||||
{
|
||||
MailListView.ScrollIntoView(collectionContainer.ItemViewModel, ScrollIntoViewAlignment.Default);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!isSelected)
|
||||
Analytics.TrackEvent("Mail Selection Failed");
|
||||
});
|
||||
}
|
||||
|
||||
public void Receive(ShellStateUpdated message)
|
||||
{
|
||||
UpdateAdaptiveness();
|
||||
}
|
||||
|
||||
private void SearchBoxFocused(object sender, RoutedEventArgs e)
|
||||
{
|
||||
SearchBar.PlaceholderText = string.Empty;
|
||||
}
|
||||
|
||||
private void SearchBarUnfocused(object sender, RoutedEventArgs e)
|
||||
{
|
||||
SearchBar.PlaceholderText = "search";
|
||||
}
|
||||
|
||||
private void MailListPageLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// App might open with narrowed state.
|
||||
// VSM will not trigger in this case.
|
||||
// Set values to force updating adaptiveness.
|
||||
|
||||
var groups = VisualStateManager.GetVisualStateGroups(RootGrid);
|
||||
var adaptiveState = groups.FirstOrDefault(a => a.Name == AdaptivenessStatesKey);
|
||||
|
||||
if (adaptiveState == null) return;
|
||||
|
||||
// This should force UpdateAdaptiveness call.
|
||||
StatePersistanceService.IsReaderNarrowed = adaptiveState.CurrentState.Name == NarrowVisualStateKey;
|
||||
}
|
||||
|
||||
private void ProcessMailItemKeyboardAccelerator(UIElement sender, ProcessKeyboardAcceleratorEventArgs args)
|
||||
{
|
||||
if (args.Key == Windows.System.VirtualKey.Delete)
|
||||
{
|
||||
args.Handled = true;
|
||||
|
||||
ViewModel?.MailOperationCommand?.Execute((int)MailOperation.SoftDelete);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Thread header is mail info display control and it can be dragged spearately out of ListView.
|
||||
/// We need to prepare a drag package for it from the items inside.
|
||||
/// </summary>
|
||||
private void ThreadHeaderDragStart(UIElement sender, DragStartingEventArgs args)
|
||||
{
|
||||
if (sender is MailItemDisplayInformationControl control
|
||||
&& control.ConnectedExpander?.Content is WinoListView contentListView)
|
||||
{
|
||||
var allItems = contentListView.Items.Where(a => a is IMailItem);
|
||||
|
||||
// Highlight all items.
|
||||
allItems.Cast<MailItemViewModel>().ForEach(a => a.IsCustomFocused = true);
|
||||
|
||||
// Set native drag arg properties.
|
||||
args.AllowedOperations = Windows.ApplicationModel.DataTransfer.DataPackageOperation.Move;
|
||||
|
||||
var dragPackage = new MailDragPackage(allItems.Cast<IMailItem>());
|
||||
|
||||
args.Data.Properties.Add(nameof(MailDragPackage), dragPackage);
|
||||
args.DragUI.SetContentFromDataPackage();
|
||||
|
||||
control.ConnectedExpander.IsExpanded = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void ThreadHeaderDragFinished(UIElement sender, DropCompletedEventArgs args)
|
||||
{
|
||||
if (sender is MailItemDisplayInformationControl control && control.ConnectedExpander != null && control.ConnectedExpander.Content is WinoListView contentListView)
|
||||
{
|
||||
contentListView.Items.Where(a => a is MailItemViewModel).Cast<MailItemViewModel>().ForEach(a => a.IsCustomFocused = false);
|
||||
}
|
||||
}
|
||||
|
||||
private async void LeftSwipeItemInvoked(Microsoft.UI.Xaml.Controls.SwipeItem sender, Microsoft.UI.Xaml.Controls.SwipeItemInvokedEventArgs args)
|
||||
{
|
||||
// Delete item for now.
|
||||
|
||||
var swipeControl = args.SwipeControl;
|
||||
|
||||
swipeControl.Close();
|
||||
|
||||
if (swipeControl.Tag is MailItemViewModel mailItemViewModel)
|
||||
{
|
||||
var package = new MailOperationPreperationRequest(MailOperation.SoftDelete, mailItemViewModel.MailCopy);
|
||||
await ViewModel.ExecuteMailOperationAsync(package);
|
||||
}
|
||||
else if (swipeControl.Tag is ThreadMailItemViewModel threadMailItemViewModel)
|
||||
{
|
||||
var package = new MailOperationPreperationRequest(MailOperation.SoftDelete, threadMailItemViewModel.GetMailCopies());
|
||||
await ViewModel.ExecuteMailOperationAsync(package);
|
||||
}
|
||||
}
|
||||
|
||||
private async void RightSwipeItemInvoked(Microsoft.UI.Xaml.Controls.SwipeItem sender, Microsoft.UI.Xaml.Controls.SwipeItemInvokedEventArgs args)
|
||||
{
|
||||
// Toggle status only for now.
|
||||
|
||||
var swipeControl = args.SwipeControl;
|
||||
|
||||
swipeControl.Close();
|
||||
|
||||
if (swipeControl.Tag is MailItemViewModel mailItemViewModel)
|
||||
{
|
||||
var operation = mailItemViewModel.IsRead ? MailOperation.MarkAsUnread : MailOperation.MarkAsRead;
|
||||
var package = new MailOperationPreperationRequest(operation, mailItemViewModel.MailCopy);
|
||||
|
||||
await ViewModel.ExecuteMailOperationAsync(package);
|
||||
}
|
||||
else if (swipeControl.Tag is ThreadMailItemViewModel threadMailItemViewModel)
|
||||
{
|
||||
bool isAllRead = threadMailItemViewModel.ThreadItems.All(a => a.IsRead);
|
||||
|
||||
var operation = isAllRead ? MailOperation.MarkAsUnread : MailOperation.MarkAsRead;
|
||||
var package = new MailOperationPreperationRequest(operation, threadMailItemViewModel.GetMailCopies());
|
||||
|
||||
await ViewModel.ExecuteMailOperationAsync(package);
|
||||
}
|
||||
}
|
||||
|
||||
private void PullToRefreshRequested(Microsoft.UI.Xaml.Controls.RefreshContainer sender, Microsoft.UI.Xaml.Controls.RefreshRequestedEventArgs args)
|
||||
{
|
||||
ViewModel.SyncFolderCommand?.Execute(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
359
Wino.Mail/Views/MailRenderingPage.xaml
Normal file
359
Wino.Mail/Views/MailRenderingPage.xaml
Normal file
File diff suppressed because one or more lines are too long
287
Wino.Mail/Views/MailRenderingPage.xaml.cs
Normal file
287
Wino.Mail/Views/MailRenderingPage.xaml.cs
Normal file
@@ -0,0 +1,287 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.AppCenter.Crashes;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.Web.WebView2.Core;
|
||||
using Newtonsoft.Json;
|
||||
using Windows.System;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media.Animation;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using Wino.Core.Domain;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Messages.Mails;
|
||||
using Wino.Core.Messages.Shell;
|
||||
using Wino.Mail.ViewModels.Data;
|
||||
using Wino.Views.Abstract;
|
||||
|
||||
namespace Wino.Views
|
||||
{
|
||||
public sealed partial class MailRenderingPage : MailRenderingPageAbstract,
|
||||
IRecipient<HtmlRenderingRequested>,
|
||||
IRecipient<CancelRenderingContentRequested>,
|
||||
IRecipient<NavigationPaneModeChanged>,
|
||||
IRecipient<ApplicationThemeChanged>,
|
||||
IRecipient<SaveAsPDFRequested>
|
||||
{
|
||||
private readonly IFontService _fontService = App.Current.Services.GetService<IFontService>();
|
||||
private readonly IDialogService _dialogService = App.Current.Services.GetService<IDialogService>();
|
||||
|
||||
private bool isRenderingInProgress = false;
|
||||
private TaskCompletionSource<bool> DOMLoadedTask = new TaskCompletionSource<bool>();
|
||||
|
||||
public WebView2 GetWebView() => Chromium;
|
||||
|
||||
public MailRenderingPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
Environment.SetEnvironmentVariable("WEBVIEW2_DEFAULT_BACKGROUND_COLOR", "00FFFFFF");
|
||||
|
||||
NavigationCacheMode = NavigationCacheMode.Enabled;
|
||||
}
|
||||
|
||||
public override async void OnEditorThemeChanged()
|
||||
{
|
||||
base.OnEditorThemeChanged();
|
||||
|
||||
await UpdateEditorThemeAsync();
|
||||
}
|
||||
|
||||
private async Task<string> InvokeScriptSafeAsync(string function)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await Chromium.ExecuteScriptAsync(function);
|
||||
}
|
||||
catch (Exception) { }
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public async Task<string> ExecuteScriptFunctionAsync(string functionName, params object[] parameters)
|
||||
{
|
||||
string script = functionName + "(";
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
script += JsonConvert.SerializeObject(parameters[i]);
|
||||
if (i < parameters.Length - 1)
|
||||
{
|
||||
script += ", ";
|
||||
}
|
||||
}
|
||||
script += ");";
|
||||
|
||||
return await Chromium.ExecuteScriptAsync(script);
|
||||
}
|
||||
|
||||
private async Task RenderInternalAsync(string htmlBody)
|
||||
{
|
||||
isRenderingInProgress = true;
|
||||
|
||||
await DOMLoadedTask.Task;
|
||||
|
||||
await UpdateEditorThemeAsync();
|
||||
await UpdateReaderFontPropertiesAsync();
|
||||
|
||||
if (string.IsNullOrEmpty(htmlBody))
|
||||
{
|
||||
await ExecuteScriptFunctionAsync("RenderHTML", " ");
|
||||
}
|
||||
else
|
||||
{
|
||||
await ExecuteScriptFunctionAsync("RenderHTML", htmlBody);
|
||||
}
|
||||
|
||||
isRenderingInProgress = false;
|
||||
}
|
||||
|
||||
private async void WindowRequested(CoreWebView2 sender, CoreWebView2NewWindowRequestedEventArgs args)
|
||||
{
|
||||
args.Handled = true;
|
||||
|
||||
try
|
||||
{
|
||||
await Launcher.LaunchUriAsync(new Uri(args.Uri));
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
|
||||
private void DOMContentLoaded(CoreWebView2 sender, CoreWebView2DOMContentLoadedEventArgs args) => DOMLoadedTask.TrySetResult(true);
|
||||
|
||||
async void IRecipient<HtmlRenderingRequested>.Receive(HtmlRenderingRequested message)
|
||||
{
|
||||
if (message == null || string.IsNullOrEmpty(message.HtmlBody))
|
||||
{
|
||||
await RenderInternalAsync(string.Empty);
|
||||
return;
|
||||
}
|
||||
|
||||
await Chromium.EnsureCoreWebView2Async();
|
||||
|
||||
await RenderInternalAsync(message.HtmlBody);
|
||||
}
|
||||
|
||||
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
||||
{
|
||||
WeakReferenceMessenger.Default.Send(new CancelRenderingContentRequested());
|
||||
|
||||
base.OnNavigatedFrom(e);
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
var anim = ConnectedAnimationService.GetForCurrentView().GetAnimation("WebViewConnectedAnimation");
|
||||
anim?.TryStart(Chromium);
|
||||
|
||||
Chromium.CoreWebView2Initialized -= CoreWebViewInitialized;
|
||||
Chromium.CoreWebView2Initialized += CoreWebViewInitialized;
|
||||
|
||||
_ = Chromium.EnsureCoreWebView2Async();
|
||||
|
||||
// We don't have shell initialized here. It's only standalone EML viewing.
|
||||
// Shift command bar from top to adjust the design.
|
||||
|
||||
if (ViewModel.StatePersistanceService.ShouldShiftMailRenderingDesign)
|
||||
RendererGridFrame.Margin = new Thickness(0, 24, 0, 0);
|
||||
else
|
||||
RendererGridFrame.Margin = new Thickness(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
private async void CoreWebViewInitialized(WebView2 sender, CoreWebView2InitializedEventArgs args)
|
||||
{
|
||||
if (Chromium.CoreWebView2 == null) return;
|
||||
|
||||
var editorBundlePath = (await ViewModel.NativeAppService.GetQuillEditorBundlePathAsync()).Replace("full.html", string.Empty);
|
||||
|
||||
Chromium.CoreWebView2.SetVirtualHostNameToFolderMapping("app.reader", editorBundlePath, CoreWebView2HostResourceAccessKind.Allow);
|
||||
|
||||
Chromium.CoreWebView2.DOMContentLoaded -= DOMContentLoaded;
|
||||
Chromium.CoreWebView2.DOMContentLoaded += DOMContentLoaded;
|
||||
|
||||
Chromium.CoreWebView2.NewWindowRequested -= WindowRequested;
|
||||
Chromium.CoreWebView2.NewWindowRequested += WindowRequested;
|
||||
|
||||
Chromium.Source = new Uri("https://app.reader/reader.html");
|
||||
}
|
||||
|
||||
|
||||
async void IRecipient<CancelRenderingContentRequested>.Receive(CancelRenderingContentRequested message)
|
||||
{
|
||||
await Chromium.EnsureCoreWebView2Async();
|
||||
|
||||
if (!isRenderingInProgress)
|
||||
{
|
||||
await RenderInternalAsync(string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
void IRecipient<NavigationPaneModeChanged>.Receive(NavigationPaneModeChanged message)
|
||||
{
|
||||
if (message.NewMode == MenuPaneMode.Hidden)
|
||||
RendererBar.Margin = new Thickness(48, 6, 6, 6);
|
||||
else
|
||||
RendererBar.Margin = new Thickness(16, 6, 6, 6);
|
||||
}
|
||||
|
||||
private async void WebViewNavigationStarting(WebView2 sender, CoreWebView2NavigationStartingEventArgs args)
|
||||
{
|
||||
// This is our reader.
|
||||
if (args.Uri == "https://app.reader/reader.html")
|
||||
return;
|
||||
|
||||
// Cancel all external navigations since it's navigating to different address inside the WebView2.
|
||||
args.Cancel = !args.Uri.StartsWith("data:text/html");
|
||||
|
||||
// TODO: Check external link navigation setting is enabled.
|
||||
// Open all external urls in launcher.
|
||||
|
||||
if (args.Cancel && Uri.TryCreate(args.Uri, UriKind.Absolute, out Uri newUri))
|
||||
{
|
||||
await Launcher.LaunchUriAsync(newUri);
|
||||
}
|
||||
}
|
||||
|
||||
private void AttachmentClicked(object sender, ItemClickEventArgs e)
|
||||
{
|
||||
if (e.ClickedItem is MailAttachmentViewModel attachmentViewModel)
|
||||
{
|
||||
ViewModel.OpenAttachmentCommand.Execute(attachmentViewModel);
|
||||
}
|
||||
}
|
||||
|
||||
private void BarDynamicOverflowChanging(CommandBar sender, DynamicOverflowItemsChangingEventArgs args)
|
||||
{
|
||||
if (args.Action == CommandBarDynamicOverflowAction.AddingToOverflow)
|
||||
sender.OverflowButtonVisibility = CommandBarOverflowButtonVisibility.Visible;
|
||||
else
|
||||
sender.OverflowButtonVisibility = CommandBarOverflowButtonVisibility.Collapsed;
|
||||
}
|
||||
|
||||
private async Task UpdateEditorThemeAsync()
|
||||
{
|
||||
await DOMLoadedTask.Task;
|
||||
|
||||
if (ViewModel.IsDarkWebviewRenderer)
|
||||
{
|
||||
Chromium.CoreWebView2.Profile.PreferredColorScheme = CoreWebView2PreferredColorScheme.Dark;
|
||||
|
||||
await InvokeScriptSafeAsync("ChangePrefferedTheme('dark')");
|
||||
await InvokeScriptSafeAsync("DarkReader.enable();");
|
||||
}
|
||||
else
|
||||
{
|
||||
Chromium.CoreWebView2.Profile.PreferredColorScheme = CoreWebView2PreferredColorScheme.Light;
|
||||
|
||||
await InvokeScriptSafeAsync("ChangePrefferedTheme('light')");
|
||||
await InvokeScriptSafeAsync("DarkReader.disable();");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UpdateReaderFontPropertiesAsync()
|
||||
{
|
||||
await ExecuteScriptFunctionAsync("ChangeFontSize", _fontService.GetCurrentReaderFontSize());
|
||||
|
||||
// Prepare font family name with fallback to sans-serif by default.
|
||||
var fontName = _fontService.GetCurrentReaderFont()?.FontFamilyName ?? "Arial";
|
||||
|
||||
// If font family name is not supported by the browser, fallback to sans-serif.
|
||||
fontName += ", sans-serif";
|
||||
|
||||
// var fontName = "Starborn";
|
||||
|
||||
await ExecuteScriptFunctionAsync("ChangeFontFamily", fontName);
|
||||
}
|
||||
|
||||
void IRecipient<ApplicationThemeChanged>.Receive(ApplicationThemeChanged message)
|
||||
{
|
||||
ViewModel.IsDarkWebviewRenderer = message.IsUnderlyingThemeDark;
|
||||
}
|
||||
|
||||
public async void Receive(SaveAsPDFRequested message)
|
||||
{
|
||||
try
|
||||
{
|
||||
bool isSaved = await Chromium.CoreWebView2.PrintToPdfAsync(message.FileSavePath, null);
|
||||
|
||||
if (isSaved)
|
||||
{
|
||||
_dialogService.InfoBarMessage(Translator.Info_PDFSaveSuccessTitle,
|
||||
string.Format(Translator.Info_PDFSaveSuccessMessage, message.FileSavePath),
|
||||
InfoBarMessageType.Success);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_dialogService.InfoBarMessage(Translator.Info_PDFSaveFailedTitle, ex.Message, InfoBarMessageType.Error);
|
||||
Crashes.TrackError(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
47
Wino.Mail/Views/NewAccountManagementPage.xaml
Normal file
47
Wino.Mail/Views/NewAccountManagementPage.xaml
Normal file
@@ -0,0 +1,47 @@
|
||||
<abstract:NewAccountManagementPageAbstract
|
||||
x:Class="Wino.Views.NewAccountManagementPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:abstract="using:Wino.Views.Abstract"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:winuiControls="using:Microsoft.UI.Xaml.Controls"
|
||||
xmlns:helpers="using:Wino.Helpers"
|
||||
xmlns:viewModelData="using:Wino.Mail.ViewModels.Data"
|
||||
Style="{StaticResource PageStyle}"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid
|
||||
MaxWidth="900"
|
||||
Padding="20"
|
||||
HorizontalAlignment="Stretch"
|
||||
RowSpacing="20">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<winuiControls:BreadcrumbBar
|
||||
x:Name="Breadcrumb"
|
||||
ItemsSource="{x:Bind PageHistory, Mode=OneWay}"
|
||||
ItemClicked="BreadItemClicked">
|
||||
<winuiControls:BreadcrumbBar.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<winuiControls:BreadcrumbBarItem Margin="0,0,8,0">
|
||||
<winuiControls:BreadcrumbBarItem.ContentTemplate>
|
||||
<DataTemplate x:DataType="viewModelData:BreadcrumbNavigationItemViewModel">
|
||||
<TextBlock
|
||||
Margin="0,0,8,10"
|
||||
Text="{Binding Title, Mode=OneWay}"
|
||||
FontWeight="{x:Bind helpers:XamlHelpers.GetFontWeightBySyncState(IsActive), Mode=OneWay}"
|
||||
Style="{StaticResource TitleTextBlockStyle}" />
|
||||
</DataTemplate>
|
||||
</winuiControls:BreadcrumbBarItem.ContentTemplate>
|
||||
</winuiControls:BreadcrumbBarItem>
|
||||
</DataTemplate>
|
||||
</winuiControls:BreadcrumbBar.ItemTemplate>
|
||||
</winuiControls:BreadcrumbBar>
|
||||
|
||||
<Frame x:Name="AccountPagesFrame" Grid.Row="1" />
|
||||
</Grid>
|
||||
</abstract:NewAccountManagementPageAbstract>
|
||||
111
Wino.Mail/Views/NewAccountManagementPage.xaml.cs
Normal file
111
Wino.Mail/Views/NewAccountManagementPage.xaml.cs
Normal file
@@ -0,0 +1,111 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using MoreLinq;
|
||||
using Windows.UI.Xaml.Media.Animation;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Messages.Navigation;
|
||||
using Wino.Core.Requests;
|
||||
using Wino.Mail.ViewModels.Data;
|
||||
using Wino.Views.Abstract;
|
||||
using Wino.Views.Account;
|
||||
using Wino.Views.Settings;
|
||||
|
||||
namespace Wino.Views
|
||||
{
|
||||
public sealed partial class NewAccountManagementPage : NewAccountManagementPageAbstract,
|
||||
IRecipient<BackBreadcrumNavigationRequested>,
|
||||
IRecipient<BreadcrumbNavigationRequested>,
|
||||
IRecipient<MergedInboxRenamed>
|
||||
{
|
||||
public ObservableCollection<BreadcrumbNavigationItemViewModel> PageHistory { get; set; } = new ObservableCollection<BreadcrumbNavigationItemViewModel>();
|
||||
|
||||
public NewAccountManagementPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
var initialRequest = new BreadcrumbNavigationRequested("Manage Accounts", Core.Domain.Enums.WinoPage.AccountManagementPage);
|
||||
PageHistory.Add(new BreadcrumbNavigationItemViewModel(initialRequest, true));
|
||||
|
||||
AccountPagesFrame.Navigate(typeof(AccountManagementPage), null, new SuppressNavigationTransitionInfo());
|
||||
}
|
||||
|
||||
private Type GetPageNavigationType(WinoPage page)
|
||||
{
|
||||
return page switch
|
||||
{
|
||||
WinoPage.SignatureManagementPage => typeof(SignatureManagementPage),
|
||||
WinoPage.AccountDetailsPage => typeof(AccountDetailsPage),
|
||||
WinoPage.MergedAccountDetailsPage => typeof(MergedAccountDetailsPage),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
void IRecipient<BreadcrumbNavigationRequested>.Receive(BreadcrumbNavigationRequested message)
|
||||
{
|
||||
var pageType = GetPageNavigationType(message.PageType);
|
||||
|
||||
if (pageType == null) return;
|
||||
|
||||
AccountPagesFrame.Navigate(pageType, message.Parameter, new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromRight });
|
||||
|
||||
PageHistory.ForEach(a => a.IsActive = false);
|
||||
|
||||
PageHistory.Add(new BreadcrumbNavigationItemViewModel(message, true));
|
||||
}
|
||||
|
||||
private void GoBackFrame()
|
||||
{
|
||||
if (AccountPagesFrame.CanGoBack)
|
||||
{
|
||||
PageHistory.RemoveAt(PageHistory.Count - 1);
|
||||
|
||||
AccountPagesFrame.GoBack(new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromRight });
|
||||
}
|
||||
}
|
||||
|
||||
private void BreadItemClicked(Microsoft.UI.Xaml.Controls.BreadcrumbBar sender, Microsoft.UI.Xaml.Controls.BreadcrumbBarItemClickedEventArgs args)
|
||||
{
|
||||
var clickedPageHistory = PageHistory[args.Index];
|
||||
|
||||
while (PageHistory.FirstOrDefault(a => a.IsActive) != clickedPageHistory)
|
||||
{
|
||||
AccountPagesFrame.GoBack(new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromRight });
|
||||
PageHistory.RemoveAt(PageHistory.Count - 1);
|
||||
PageHistory[PageHistory.Count - 1].IsActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(BackBreadcrumNavigationRequested message)
|
||||
{
|
||||
GoBackFrame();
|
||||
}
|
||||
|
||||
public void Receive(AccountUpdatedMessage message)
|
||||
{
|
||||
// TODO: Find better way to retrieve page history from the stack for the account.
|
||||
var activePage = PageHistory.LastOrDefault();
|
||||
|
||||
if (activePage == null) return;
|
||||
|
||||
activePage.Title = message.Account.Name;
|
||||
}
|
||||
|
||||
public void Receive(MergedInboxRenamed message)
|
||||
{
|
||||
// TODO: Find better way to retrieve page history from the stack for the merged account.
|
||||
var activePage = PageHistory.LastOrDefault();
|
||||
|
||||
if (activePage == null) return;
|
||||
|
||||
activePage.Title = message.NewName;
|
||||
}
|
||||
}
|
||||
}
|
||||
127
Wino.Mail/Views/Settings/AboutPage.xaml
Normal file
127
Wino.Mail/Views/Settings/AboutPage.xaml
Normal file
File diff suppressed because one or more lines are too long
12
Wino.Mail/Views/Settings/AboutPage.xaml.cs
Normal file
12
Wino.Mail/Views/Settings/AboutPage.xaml.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Wino.Views.Abstract;
|
||||
|
||||
namespace Wino.Views.Settings
|
||||
{
|
||||
public sealed partial class AboutPage : AboutPageAbstract
|
||||
{
|
||||
public AboutPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
105
Wino.Mail/Views/Settings/MessageListPage.xaml
Normal file
105
Wino.Mail/Views/Settings/MessageListPage.xaml
Normal file
File diff suppressed because one or more lines are too long
12
Wino.Mail/Views/Settings/MessageListPage.xaml.cs
Normal file
12
Wino.Mail/Views/Settings/MessageListPage.xaml.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Wino.Views.Abstract;
|
||||
|
||||
namespace Wino.Views.Settings
|
||||
{
|
||||
public sealed partial class MessageListPage : MessageListPageAbstract
|
||||
{
|
||||
public MessageListPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
306
Wino.Mail/Views/Settings/PersonalizationPage.xaml
Normal file
306
Wino.Mail/Views/Settings/PersonalizationPage.xaml
Normal file
File diff suppressed because one or more lines are too long
12
Wino.Mail/Views/Settings/PersonalizationPage.xaml.cs
Normal file
12
Wino.Mail/Views/Settings/PersonalizationPage.xaml.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Wino.Views.Abstract;
|
||||
|
||||
namespace Wino.Views.Settings
|
||||
{
|
||||
public sealed partial class PersonalizationPage : PersonalizationPageAbstract
|
||||
{
|
||||
public PersonalizationPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
115
Wino.Mail/Views/Settings/ReadingPanePage.xaml
Normal file
115
Wino.Mail/Views/Settings/ReadingPanePage.xaml
Normal file
File diff suppressed because one or more lines are too long
12
Wino.Mail/Views/Settings/ReadingPanePage.xaml.cs
Normal file
12
Wino.Mail/Views/Settings/ReadingPanePage.xaml.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Wino.Views.Abstract;
|
||||
|
||||
namespace Wino.Views.Settings
|
||||
{
|
||||
public sealed partial class ReadingPanePage : ReadingPanePageAbstract
|
||||
{
|
||||
public ReadingPanePage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
97
Wino.Mail/Views/Settings/SettingOptionsPage.xaml
Normal file
97
Wino.Mail/Views/Settings/SettingOptionsPage.xaml
Normal file
File diff suppressed because one or more lines are too long
19
Wino.Mail/Views/Settings/SettingOptionsPage.xaml.cs
Normal file
19
Wino.Mail/Views/Settings/SettingOptionsPage.xaml.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using Wino.Views.Abstract;
|
||||
|
||||
namespace Wino.Views.Settings
|
||||
{
|
||||
public sealed partial class SettingOptionsPage : SettingOptionsPageAbstract
|
||||
{
|
||||
public SettingOptionsPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public override void OnLanguageChanged()
|
||||
{
|
||||
base.OnLanguageChanged();
|
||||
|
||||
Bindings.Update();
|
||||
}
|
||||
}
|
||||
}
|
||||
284
Wino.Mail/Views/Settings/SignatureManagementPage.xaml
Normal file
284
Wino.Mail/Views/Settings/SignatureManagementPage.xaml
Normal file
@@ -0,0 +1,284 @@
|
||||
<abstract:SignatureManagementPageAbstract
|
||||
x:Class="Wino.Views.Settings.SignatureManagementPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:abstract="using:Wino.Views.Abstract"
|
||||
xmlns:helpers="using:Wino.Helpers"
|
||||
xmlns:domain="using:Wino.Core.Domain"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
xmlns:controls="using:Wino.Controls"
|
||||
xmlns:controls1="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:reader="using:Wino.Core.Domain.Models.Reader"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid RowSpacing="12">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Wino Pivot -->
|
||||
<controls:WinoPivotControl
|
||||
IsEnabled="{x:Bind ViewModel.IsSignatureEnabled, Mode=OneWay}"
|
||||
ItemsSource="{x:Bind ViewModel.ToolbarSections}"
|
||||
SelectedItem="{x:Bind ViewModel.SelectedToolbarSection, Mode=TwoWay}"
|
||||
SelectorPipeColor="{ThemeResource NavigationViewSelectionIndicatorForeground}">
|
||||
<controls:WinoPivotControl.DataTemplate>
|
||||
<DataTemplate x:DataType="reader:EditorToolbarSection">
|
||||
<TextBlock Text="{x:Bind Title}" />
|
||||
</DataTemplate>
|
||||
</controls:WinoPivotControl.DataTemplate>
|
||||
</controls:WinoPivotControl>
|
||||
|
||||
<StackPanel
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Spacing="12"
|
||||
Orientation="Horizontal">
|
||||
<Button Content="{x:Bind domain:Translator.Buttons_Save}" Command="{x:Bind ViewModel.SaveSignatureCommand}" />
|
||||
<ToggleButton Content="{x:Bind domain:Translator.SettingsEnableSignature}" IsChecked="{x:Bind ViewModel.IsSignatureEnabled, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- Editor Options -->
|
||||
<ScrollViewer
|
||||
Grid.Row="1"
|
||||
VerticalAlignment="Top"
|
||||
IsEnabled="{x:Bind ViewModel.IsSignatureEnabled, Mode=OneWay}"
|
||||
VerticalScrollMode="Disabled"
|
||||
HorizontalScrollBarVisibility="Hidden"
|
||||
HorizontalScrollMode="Enabled">
|
||||
<Grid Padding="8,0">
|
||||
<!-- Format Panel -->
|
||||
<Grid x:Name="FormatPanel" Visibility="{x:Bind helpers:XamlHelpers.IsFormatSection(ViewModel.SelectedToolbarSection), Mode=OneWay}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="12">
|
||||
<ToggleButton
|
||||
x:Name="BoldButton"
|
||||
Height="32"
|
||||
Click="BoldButtonClicked">
|
||||
<ToggleButton.Content>
|
||||
<Viewbox Width="16">
|
||||
<PathIcon Data="{StaticResource BoldPathIcon}" />
|
||||
</Viewbox>
|
||||
</ToggleButton.Content>
|
||||
</ToggleButton>
|
||||
<ToggleButton
|
||||
x:Name="ItalicButton"
|
||||
Height="32"
|
||||
Click="ItalicButtonClicked">
|
||||
<ToggleButton.Content>
|
||||
<Viewbox Width="16">
|
||||
<PathIcon Data="{StaticResource ItalicPathIcon}" />
|
||||
</Viewbox>
|
||||
</ToggleButton.Content>
|
||||
</ToggleButton>
|
||||
<ToggleButton
|
||||
x:Name="UnderlineButton"
|
||||
Height="32"
|
||||
Click="UnderlineButtonClicked">
|
||||
<ToggleButton.Content>
|
||||
<Viewbox Width="16">
|
||||
<PathIcon Data="{StaticResource UnderlinePathIcon}" />
|
||||
</Viewbox>
|
||||
</ToggleButton.Content>
|
||||
</ToggleButton>
|
||||
<ToggleButton
|
||||
x:Name="StrokeButton"
|
||||
Height="32"
|
||||
Click="StrokeButtonClicked">
|
||||
<ToggleButton.Content>
|
||||
<Viewbox Width="16">
|
||||
<PathIcon Data="{StaticResource StrikePathIcon}" />
|
||||
</Viewbox>
|
||||
</ToggleButton.Content>
|
||||
</ToggleButton>
|
||||
|
||||
<AppBarSeparator />
|
||||
|
||||
<ToggleButton
|
||||
x:Name="BulletListButton"
|
||||
Height="32"
|
||||
Click="BulletListButtonClicked">
|
||||
<ToggleButton.Content>
|
||||
<Viewbox Width="16">
|
||||
<PathIcon Data="{StaticResource BulletedListPathIcon}" />
|
||||
</Viewbox>
|
||||
</ToggleButton.Content>
|
||||
</ToggleButton>
|
||||
|
||||
<ToggleButton
|
||||
x:Name="OrderedListButton"
|
||||
Height="32"
|
||||
Click="OrderedListButtonClicked">
|
||||
<ToggleButton.Content>
|
||||
<Viewbox Width="16">
|
||||
<PathIcon Data="{StaticResource OrderedListPathIcon}" />
|
||||
</Viewbox>
|
||||
</ToggleButton.Content>
|
||||
</ToggleButton>
|
||||
|
||||
<Button
|
||||
x:Name="DecreaseIndentButton"
|
||||
Height="32"
|
||||
Click="DecreaseIndentClicked">
|
||||
<Button.Content>
|
||||
<Viewbox Width="16">
|
||||
<PathIcon Data="{StaticResource DecreaseIndentPathIcon}" />
|
||||
</Viewbox>
|
||||
</Button.Content>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
x:Name="IncreaseIndentButton"
|
||||
Height="32"
|
||||
Click="IncreaseIndentClicked">
|
||||
<Button.Content>
|
||||
<Viewbox Width="16">
|
||||
<PathIcon Data="{StaticResource IncreaseIndentPathIcon}" />
|
||||
</Viewbox>
|
||||
</Button.Content>
|
||||
</Button>
|
||||
|
||||
<AppBarSeparator />
|
||||
|
||||
<ToggleButton
|
||||
x:Name="DirectionButton"
|
||||
Height="32"
|
||||
Click="DirectionButtonClicked">
|
||||
<ToggleButton.Content>
|
||||
<Viewbox Width="16">
|
||||
<PathIcon Data="{StaticResource ParagraphPathIcon}" />
|
||||
</Viewbox>
|
||||
</ToggleButton.Content>
|
||||
</ToggleButton>
|
||||
|
||||
<ComboBox
|
||||
x:Name="AlignmentListView"
|
||||
Width="120"
|
||||
Height="32"
|
||||
VerticalAlignment="Center"
|
||||
SelectionChanged="AlignmentChanged">
|
||||
<ComboBoxItem Tag="left">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<Viewbox Width="16">
|
||||
<PathIcon Data="{StaticResource AlignLeftPathIcon}" />
|
||||
</Viewbox>
|
||||
<TextBlock VerticalAlignment="Center" Text="{x:Bind domain:Translator.Left}" />
|
||||
</StackPanel>
|
||||
</ComboBoxItem>
|
||||
|
||||
<ComboBoxItem Tag="center">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<Viewbox Width="16">
|
||||
<PathIcon Data="{StaticResource AlignCenterPathIcon}" />
|
||||
</Viewbox>
|
||||
<TextBlock VerticalAlignment="Center" Text="{x:Bind domain:Translator.Center}" />
|
||||
</StackPanel>
|
||||
</ComboBoxItem>
|
||||
|
||||
<ComboBoxItem Tag="right">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<Viewbox Width="16">
|
||||
<PathIcon Data="{StaticResource AlignRightPathIcon}" />
|
||||
</Viewbox>
|
||||
<TextBlock VerticalAlignment="Center" Text="{x:Bind domain:Translator.Right}" />
|
||||
</StackPanel>
|
||||
</ComboBoxItem>
|
||||
|
||||
<ComboBoxItem Tag="justify">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<Viewbox Width="16">
|
||||
<PathIcon Data="{StaticResource AlignJustifyPathIcon}" />
|
||||
</Viewbox>
|
||||
<TextBlock VerticalAlignment="Center" Text="{x:Bind domain:Translator.Justify}" />
|
||||
</StackPanel>
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- Insert Panel -->
|
||||
<Grid x:Name="InsertPanel" Visibility="{x:Bind helpers:XamlHelpers.IsInsertSection(ViewModel.SelectedToolbarSection), Mode=OneWay}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="12">
|
||||
<Button
|
||||
x:Name="AddImageButton"
|
||||
Height="32"
|
||||
Click="AddImageClicked">
|
||||
<Button.Content>
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<Viewbox Width="16" VerticalAlignment="Center">
|
||||
<PathIcon Data="{StaticResource AddPhotoPathIcon}" />
|
||||
</Viewbox>
|
||||
<TextBlock Text="{x:Bind domain:Translator.Photos}" />
|
||||
</StackPanel>
|
||||
</Button.Content>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
x:Name="EmojiButton"
|
||||
Height="32"
|
||||
Click="EmojiButtonClicked">
|
||||
<Button.Content>
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<Viewbox Width="16" VerticalAlignment="Center">
|
||||
<PathIcon Data="{StaticResource EmojiPathIcon}" />
|
||||
</Viewbox>
|
||||
<TextBlock Text="{x:Bind domain:Translator.Emoji}" />
|
||||
</StackPanel>
|
||||
</Button.Content>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
x:Name="LinkButton"
|
||||
Height="32"
|
||||
Click="LinkButtonClicked">
|
||||
<Button.Flyout>
|
||||
<Flyout x:Name="HyperlinkFlyout">
|
||||
<StackPanel Width="250" Spacing="6">
|
||||
<TextBox x:Name="HyperlinkTextBox" Header="Text" />
|
||||
<TextBox
|
||||
x:Name="LinkUrlTextBox"
|
||||
Header="Link"
|
||||
PlaceholderText="https://" />
|
||||
<Button
|
||||
x:Name="AddHyperlinkButton"
|
||||
Margin="0,6"
|
||||
HorizontalAlignment="Stretch"
|
||||
Click="HyperlinkAddClicked"
|
||||
Content="{x:Bind domain:Translator.AddHyperlink}" />
|
||||
</StackPanel>
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
<Button.Content>
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<Viewbox Width="16" VerticalAlignment="Center">
|
||||
<PathIcon Data="{StaticResource AddLinkPathIcon}" />
|
||||
</Viewbox>
|
||||
<TextBlock Text="Link" />
|
||||
</StackPanel>
|
||||
</Button.Content>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
|
||||
<muxc:WebView2
|
||||
x:Name="Chromium"
|
||||
Grid.Row="2"
|
||||
Margin="0,0,0,250"
|
||||
BorderBrush="LightGray"
|
||||
BorderThickness="1"
|
||||
Visibility="{x:Bind ViewModel.IsSignatureEnabled, Mode=OneWay}" />
|
||||
</Grid>
|
||||
</abstract:SignatureManagementPageAbstract>
|
||||
319
Wino.Mail/Views/Settings/SignatureManagementPage.xaml.cs
Normal file
319
Wino.Mail/Views/Settings/SignatureManagementPage.xaml.cs
Normal file
@@ -0,0 +1,319 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Web.WebView2.Core;
|
||||
using Newtonsoft.Json;
|
||||
using Windows.UI.ViewManagement.Core;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Messages.Mails;
|
||||
using Wino.Views.Abstract;
|
||||
|
||||
namespace Wino.Views.Settings
|
||||
{
|
||||
public sealed partial class SignatureManagementPage : SignatureManagementPageAbstract, IRecipient<HtmlRenderingRequested>
|
||||
{
|
||||
private TaskCompletionSource<bool> DOMLoadedTask = new TaskCompletionSource<bool>();
|
||||
|
||||
public bool IsComposerDarkMode
|
||||
{
|
||||
get { return (bool)GetValue(IsComposerDarkModeProperty); }
|
||||
set { SetValue(IsComposerDarkModeProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IsComposerDarkModeProperty = DependencyProperty.Register(nameof(IsComposerDarkMode), typeof(bool), typeof(SignatureManagementPage), new PropertyMetadata(false, OnIsComposerDarkModeChanged));
|
||||
|
||||
public SignatureManagementPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
// Environment.SetEnvironmentVariable("WEBVIEW2_DEFAULT_BACKGROUND_COLOR", "00FFFFFF");
|
||||
}
|
||||
|
||||
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedFrom(e);
|
||||
|
||||
Chromium.CoreWebView2Initialized -= ChromiumInitialized;
|
||||
|
||||
if (Chromium.CoreWebView2 != null)
|
||||
{
|
||||
Chromium.CoreWebView2.DOMContentLoaded -= DOMLoaded;
|
||||
Chromium.CoreWebView2.WebMessageReceived -= ScriptMessageRecieved;
|
||||
}
|
||||
}
|
||||
|
||||
protected override async void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
Chromium.CoreWebView2Initialized -= ChromiumInitialized;
|
||||
Chromium.CoreWebView2Initialized += ChromiumInitialized;
|
||||
|
||||
await Chromium.EnsureCoreWebView2Async();
|
||||
|
||||
ViewModel.GetHTMLBodyFunction = new Func<Task<string>>(async () =>
|
||||
{
|
||||
var quillContent = await InvokeScriptSafeAsync("GetHTMLContent();");
|
||||
|
||||
return JsonConvert.DeserializeObject<string>(quillContent);
|
||||
});
|
||||
|
||||
ViewModel.GetTextBodyFunction = new Func<Task<string>>(() => InvokeScriptSafeAsync("GetTextContent();"));
|
||||
|
||||
var underlyingThemeService = App.Current.Services.GetService<IUnderlyingThemeService>();
|
||||
|
||||
IsComposerDarkMode = underlyingThemeService.IsUnderlyingThemeDark();
|
||||
}
|
||||
|
||||
private async void HyperlinkAddClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await InvokeScriptSafeAsync($"addHyperlink('{LinkUrlTextBox.Text}')");
|
||||
|
||||
LinkUrlTextBox.Text = string.Empty;
|
||||
HyperlinkFlyout.Hide();
|
||||
}
|
||||
|
||||
private async void BoldButtonClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await InvokeScriptSafeAsync("document.getElementById('boldButton').click();");
|
||||
}
|
||||
|
||||
private async void ItalicButtonClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await InvokeScriptSafeAsync("document.getElementById('italicButton').click();");
|
||||
}
|
||||
|
||||
private async void UnderlineButtonClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await InvokeScriptSafeAsync("document.getElementById('underlineButton').click();");
|
||||
}
|
||||
|
||||
private async void StrokeButtonClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await InvokeScriptSafeAsync("document.getElementById('strikeButton').click();");
|
||||
}
|
||||
|
||||
private async void BulletListButtonClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await InvokeScriptSafeAsync("document.getElementById('bulletListButton').click();");
|
||||
}
|
||||
|
||||
private async void OrderedListButtonClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await InvokeScriptSafeAsync("document.getElementById('orderedListButton').click();");
|
||||
}
|
||||
|
||||
private async void IncreaseIndentClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await InvokeScriptSafeAsync("document.getElementById('increaseIndentButton').click();");
|
||||
}
|
||||
|
||||
private async void DecreaseIndentClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await InvokeScriptSafeAsync("document.getElementById('decreaseIndentButton').click();");
|
||||
}
|
||||
|
||||
private async void DirectionButtonClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await InvokeScriptSafeAsync("document.getElementById('directionButton').click();");
|
||||
}
|
||||
|
||||
private async void AlignmentChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
var selectedItem = AlignmentListView.SelectedItem as ComboBoxItem;
|
||||
var alignment = selectedItem.Tag.ToString();
|
||||
|
||||
switch (alignment)
|
||||
{
|
||||
case "left":
|
||||
await InvokeScriptSafeAsync("document.getElementById('ql-align-left').click();");
|
||||
break;
|
||||
case "center":
|
||||
await InvokeScriptSafeAsync("document.getElementById('ql-align-center').click();");
|
||||
break;
|
||||
case "right":
|
||||
await InvokeScriptSafeAsync("document.getElementById('ql-align-right').click();");
|
||||
break;
|
||||
case "justify":
|
||||
await InvokeScriptSafeAsync("document.getElementById('ql-align-justify').click();");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<string> ExecuteScriptFunctionAsync(string functionName, params object[] parameters)
|
||||
{
|
||||
string script = functionName + "(";
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
script += JsonConvert.SerializeObject(parameters[i]);
|
||||
if (i < parameters.Length - 1)
|
||||
{
|
||||
script += ", ";
|
||||
}
|
||||
}
|
||||
script += ");";
|
||||
|
||||
return await Chromium.ExecuteScriptAsync(script);
|
||||
}
|
||||
|
||||
private async Task<string> InvokeScriptSafeAsync(string function)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await Chromium.ExecuteScriptAsync(function);
|
||||
}
|
||||
catch (Exception) { }
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private async void AddImageClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await InvokeScriptSafeAsync("document.getElementById('addImageButton').click();");
|
||||
}
|
||||
|
||||
private async Task FocusEditorAsync()
|
||||
{
|
||||
await InvokeScriptSafeAsync("quill.focus();");
|
||||
|
||||
Chromium.Focus(FocusState.Keyboard);
|
||||
Chromium.Focus(FocusState.Programmatic);
|
||||
}
|
||||
|
||||
private async void EmojiButtonClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
CoreInputView.GetForCurrentView().TryShow(CoreInputViewKind.Emoji);
|
||||
|
||||
await FocusEditorAsync();
|
||||
}
|
||||
|
||||
private async Task<string> TryGetSelectedTextAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
return await Chromium.ExecuteScriptAsync("getSelectedText();");
|
||||
}
|
||||
catch (Exception) { }
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private async void LinkButtonClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Get selected text from Quill.
|
||||
|
||||
HyperlinkTextBox.Text = await TryGetSelectedTextAsync();
|
||||
}
|
||||
|
||||
private async Task UpdateEditorThemeAsync()
|
||||
{
|
||||
await DOMLoadedTask.Task;
|
||||
|
||||
if (IsComposerDarkMode)
|
||||
{
|
||||
await InvokeScriptSafeAsync("DarkReader.enable();");
|
||||
}
|
||||
else
|
||||
{
|
||||
await InvokeScriptSafeAsync("DarkReader.disable();");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RenderInternalAsync(string htmlBody)
|
||||
{
|
||||
await DOMLoadedTask.Task;
|
||||
|
||||
await UpdateEditorThemeAsync();
|
||||
|
||||
if (string.IsNullOrEmpty(htmlBody))
|
||||
{
|
||||
await ExecuteScriptFunctionAsync("RenderHTML", " ");
|
||||
}
|
||||
else
|
||||
{
|
||||
await ExecuteScriptFunctionAsync("RenderHTML", htmlBody);
|
||||
|
||||
await FocusEditorAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private async void ChromiumInitialized(Microsoft.UI.Xaml.Controls.WebView2 sender, Microsoft.UI.Xaml.Controls.CoreWebView2InitializedEventArgs args)
|
||||
{
|
||||
var editorBundlePath = (await ViewModel.NativeAppService.GetQuillEditorBundlePathAsync()).Replace("full.html", string.Empty);
|
||||
|
||||
Chromium.CoreWebView2.SetVirtualHostNameToFolderMapping("app.reader", editorBundlePath, CoreWebView2HostResourceAccessKind.Allow);
|
||||
Chromium.Source = new Uri("https://app.reader/full.html");
|
||||
|
||||
Chromium.CoreWebView2.DOMContentLoaded -= DOMLoaded;
|
||||
Chromium.CoreWebView2.DOMContentLoaded += DOMLoaded;
|
||||
|
||||
Chromium.CoreWebView2.WebMessageReceived -= ScriptMessageRecieved;
|
||||
Chromium.CoreWebView2.WebMessageReceived += ScriptMessageRecieved;
|
||||
}
|
||||
|
||||
private static async void OnIsComposerDarkModeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
if (obj is ComposePage page)
|
||||
{
|
||||
await page.UpdateEditorThemeAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private void ScriptMessageRecieved(CoreWebView2 sender, CoreWebView2WebMessageReceivedEventArgs args)
|
||||
{
|
||||
var change = JsonConvert.DeserializeObject<string>(args.WebMessageAsJson);
|
||||
|
||||
bool isEnabled = change.EndsWith("ql-active");
|
||||
|
||||
if (change.StartsWith("ql-bold"))
|
||||
BoldButton.IsChecked = isEnabled;
|
||||
else if (change.StartsWith("ql-italic"))
|
||||
ItalicButton.IsChecked = isEnabled;
|
||||
else if (change.StartsWith("ql-underline"))
|
||||
UnderlineButton.IsChecked = isEnabled;
|
||||
else if (change.StartsWith("ql-strike"))
|
||||
StrokeButton.IsChecked = isEnabled;
|
||||
else if (change.StartsWith("orderedListButton"))
|
||||
OrderedListButton.IsChecked = isEnabled;
|
||||
else if (change.StartsWith("bulletListButton"))
|
||||
BulletListButton.IsChecked = isEnabled;
|
||||
else if (change.StartsWith("ql-direction"))
|
||||
DirectionButton.IsChecked = isEnabled;
|
||||
else if (change.StartsWith("ql-align-left"))
|
||||
{
|
||||
AlignmentListView.SelectionChanged -= AlignmentChanged;
|
||||
AlignmentListView.SelectedIndex = 0;
|
||||
AlignmentListView.SelectionChanged += AlignmentChanged;
|
||||
}
|
||||
else if (change.StartsWith("ql-align-center"))
|
||||
{
|
||||
AlignmentListView.SelectionChanged -= AlignmentChanged;
|
||||
AlignmentListView.SelectedIndex = 1;
|
||||
AlignmentListView.SelectionChanged += AlignmentChanged;
|
||||
}
|
||||
else if (change.StartsWith("ql-align-right"))
|
||||
{
|
||||
AlignmentListView.SelectionChanged -= AlignmentChanged;
|
||||
AlignmentListView.SelectedIndex = 2;
|
||||
AlignmentListView.SelectionChanged += AlignmentChanged;
|
||||
}
|
||||
else if (change.StartsWith("ql-align-justify"))
|
||||
{
|
||||
AlignmentListView.SelectionChanged -= AlignmentChanged;
|
||||
AlignmentListView.SelectedIndex = 3;
|
||||
AlignmentListView.SelectionChanged += AlignmentChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private void DOMLoaded(CoreWebView2 sender, CoreWebView2DOMContentLoadedEventArgs args) => DOMLoadedTask.TrySetResult(true);
|
||||
|
||||
public async void Receive(HtmlRenderingRequested message)
|
||||
{
|
||||
await RenderInternalAsync(message.HtmlBody);
|
||||
}
|
||||
}
|
||||
}
|
||||
47
Wino.Mail/Views/SettingsPage.xaml
Normal file
47
Wino.Mail/Views/SettingsPage.xaml
Normal file
@@ -0,0 +1,47 @@
|
||||
<abstract:SettingsPageAbstract
|
||||
x:Class="Wino.Views.SettingsPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:abstract="using:Wino.Views.Abstract"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:winuiControls="using:Microsoft.UI.Xaml.Controls"
|
||||
xmlns:helpers="using:Wino.Helpers"
|
||||
xmlns:viewModelData="using:Wino.Mail.ViewModels.Data"
|
||||
Style="{StaticResource PageStyle}"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid
|
||||
MaxWidth="900"
|
||||
Padding="20"
|
||||
HorizontalAlignment="Stretch"
|
||||
RowSpacing="20">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<winuiControls:BreadcrumbBar
|
||||
x:Name="Breadcrumb"
|
||||
ItemsSource="{x:Bind PageHistory, Mode=OneWay}"
|
||||
ItemClicked="BreadItemClicked">
|
||||
<winuiControls:BreadcrumbBar.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<winuiControls:BreadcrumbBarItem Margin="0,0,8,0">
|
||||
<winuiControls:BreadcrumbBarItem.ContentTemplate>
|
||||
<DataTemplate x:DataType="viewModelData:BreadcrumbNavigationItemViewModel">
|
||||
<TextBlock
|
||||
Margin="0,0,8,10"
|
||||
Text="{Binding Title}"
|
||||
FontWeight="{x:Bind helpers:XamlHelpers.GetFontWeightBySyncState(IsActive), Mode=OneWay}"
|
||||
Style="{StaticResource TitleTextBlockStyle}" />
|
||||
</DataTemplate>
|
||||
</winuiControls:BreadcrumbBarItem.ContentTemplate>
|
||||
</winuiControls:BreadcrumbBarItem>
|
||||
</DataTemplate>
|
||||
</winuiControls:BreadcrumbBar.ItemTemplate>
|
||||
</winuiControls:BreadcrumbBar>
|
||||
|
||||
<Frame x:Name="SettingsFrame" Grid.Row="1" />
|
||||
</Grid>
|
||||
</abstract:SettingsPageAbstract>
|
||||
92
Wino.Mail/Views/SettingsPage.xaml.cs
Normal file
92
Wino.Mail/Views/SettingsPage.xaml.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using MoreLinq;
|
||||
using Windows.UI.Xaml.Media.Animation;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using Wino.Core.Domain;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Messages.Navigation;
|
||||
using Wino.Mail.ViewModels.Data;
|
||||
using Wino.Views.Abstract;
|
||||
using Wino.Views.Settings;
|
||||
|
||||
namespace Wino.Views
|
||||
{
|
||||
public sealed partial class SettingsPage : SettingsPageAbstract, IRecipient<BreadcrumbNavigationRequested>
|
||||
{
|
||||
public ObservableCollection<BreadcrumbNavigationItemViewModel> PageHistory { get; set; } = new ObservableCollection<BreadcrumbNavigationItemViewModel>();
|
||||
|
||||
public SettingsPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
SettingsFrame.Navigate(typeof(SettingOptionsPage), null, new SuppressNavigationTransitionInfo());
|
||||
|
||||
var initialRequest = new BreadcrumbNavigationRequested(Translator.MenuSettings, WinoPage.SettingOptionsPage);
|
||||
PageHistory.Add(new BreadcrumbNavigationItemViewModel(initialRequest, true));
|
||||
}
|
||||
|
||||
public override void OnLanguageChanged()
|
||||
{
|
||||
base.OnLanguageChanged();
|
||||
|
||||
// Update Settings header in breadcrumb.
|
||||
|
||||
var settingsHeader = PageHistory.FirstOrDefault();
|
||||
|
||||
if (settingsHeader == null) return;
|
||||
|
||||
settingsHeader.Title = Translator.MenuSettings;
|
||||
}
|
||||
|
||||
private Type GetNavigationPageType(WinoPage page)
|
||||
{
|
||||
switch (page)
|
||||
{
|
||||
case WinoPage.AboutPage:
|
||||
return typeof(AboutPage);
|
||||
case WinoPage.PersonalizationPage:
|
||||
return typeof(PersonalizationPage);
|
||||
case WinoPage.MessageListPage:
|
||||
return typeof(MessageListPage);
|
||||
case WinoPage.ReadingPanePage:
|
||||
return typeof(ReadingPanePage);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
void IRecipient<BreadcrumbNavigationRequested>.Receive(BreadcrumbNavigationRequested message)
|
||||
{
|
||||
var pageType = GetNavigationPageType(message.PageType);
|
||||
|
||||
if (pageType == null) return;
|
||||
|
||||
SettingsFrame.Navigate(pageType, message.Parameter, new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromRight });
|
||||
|
||||
PageHistory.ForEach(a => a.IsActive = false);
|
||||
|
||||
PageHistory.Add(new BreadcrumbNavigationItemViewModel(message, true));
|
||||
}
|
||||
|
||||
private void BreadItemClicked(Microsoft.UI.Xaml.Controls.BreadcrumbBar sender, Microsoft.UI.Xaml.Controls.BreadcrumbBarItemClickedEventArgs args)
|
||||
{
|
||||
var clickedPageHistory = PageHistory[args.Index];
|
||||
var activeIndex = PageHistory.IndexOf(PageHistory.FirstOrDefault(a => a.IsActive));
|
||||
|
||||
while (PageHistory.FirstOrDefault(a => a.IsActive) != clickedPageHistory)
|
||||
{
|
||||
SettingsFrame.GoBack(new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromRight });
|
||||
PageHistory.RemoveAt(PageHistory.Count - 1);
|
||||
PageHistory[PageHistory.Count - 1].IsActive = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
28
Wino.Mail/Views/WelcomePage.xaml
Normal file
28
Wino.Mail/Views/WelcomePage.xaml
Normal file
@@ -0,0 +1,28 @@
|
||||
<abstract:WelcomePageAbstract
|
||||
xmlns:abstract="using:Wino.Views.Abstract"
|
||||
x:Class="Wino.Views.WelcomePage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:Wino.Views"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid Padding="24">
|
||||
<ScrollViewer>
|
||||
<controls:MarkdownTextBlock
|
||||
x:Name="MarkdownControl"
|
||||
LinkClicked="HyperlinkClicked"
|
||||
Background="Transparent"
|
||||
ImageStretch="UniformToFill"
|
||||
Text="{x:Bind ViewModel.CurrentVersionNotes, Mode=OneWay}"
|
||||
ListBulletSpacing="10"
|
||||
Header1FontSize="30"
|
||||
CharacterSpacing="12"
|
||||
FontSize="16"
|
||||
Header2FontSize="22" />
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
|
||||
</abstract:WelcomePageAbstract>
|
||||
19
Wino.Mail/Views/WelcomePage.xaml.cs
Normal file
19
Wino.Mail/Views/WelcomePage.xaml.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using Windows.System;
|
||||
using Wino.Views.Abstract;
|
||||
|
||||
namespace Wino.Views
|
||||
{
|
||||
public sealed partial class WelcomePage : WelcomePageAbstract
|
||||
{
|
||||
public WelcomePage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private async void HyperlinkClicked(object sender, Microsoft.Toolkit.Uwp.UI.Controls.LinkClickedEventArgs e)
|
||||
{
|
||||
await Launcher.LaunchUriAsync(new System.Uri(e.Link));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user