Ability to set composer default font (#287)

* Added ability to set Composer font

* Added missing translations and refactoring

* Remove unused methods

* Small fixes
This commit is contained in:
Tiktack
2024-07-18 20:04:11 +02:00
committed by GitHub
parent 76375c9471
commit cf2f0ec936
30 changed files with 424 additions and 316 deletions

View File

@@ -1,15 +0,0 @@
namespace Wino.Core.Domain.Enums
{
public enum ReaderFont
{
Arial,
TimesNewRoman,
Verdana,
Tahoma,
CourierNew,
Georgia,
TrebuchetMS,
Calibri,
Helvetica
}
}

View File

@@ -19,7 +19,7 @@
PersonalizationPage, PersonalizationPage,
MessageListPage, MessageListPage,
MailListPage, MailListPage,
ReadingPanePage, ReadComposePanePage,
LanguageTimePage, LanguageTimePage,
SettingOptionsPage, SettingOptionsPage,
} }

View File

@@ -1,16 +1,16 @@
using System.Collections.Generic; using System.Collections.Generic;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Models.Reader;
namespace Wino.Core.Domain.Interfaces namespace Wino.Core.Domain.Interfaces
{ {
/// <summary>
/// Service to access available fonts.
/// </summary>
public interface IFontService public interface IFontService
{ {
List<ReaderFontModel> GetReaderFonts(); /// <summary>
ReaderFontModel GetCurrentReaderFont(); /// Get available fonts. Default + installed system fonts.
int GetCurrentReaderFontSize(); /// Fonts initialized only once. To refresh fonts, restart the application.
/// </summary>
void ChangeReaderFont(ReaderFont font); List<string> GetFonts();
void ChangeReaderFontSize(int size);
} }
} }

View File

@@ -122,15 +122,25 @@ namespace Wino.Core.Domain.Interfaces
AppLanguage CurrentLanguage { get; set; } AppLanguage CurrentLanguage { get; set; }
/// <summary> /// <summary>
/// Setting: Display font for the mail reader. Not composer. /// Setting: Display font for the mail reader.
/// </summary> /// </summary>
ReaderFont ReaderFont { get; set; } string ReaderFont { get; set; }
/// <summary> /// <summary>
/// Setting: Font size for the mail reader. Not composer. /// Setting: Font size for the mail reader.
/// </summary> /// </summary>
int ReaderFontSize { get; set; } int ReaderFontSize { get; set; }
/// <summary>
/// Setting: Display font for the mail composer.
/// </summary>
string ComposerFont { get; set; }
/// <summary>
/// Setting: Font size for the mail composer.
/// </summary>
int ComposerFontSize { get; set; }
/// <summary> /// <summary>
/// Setting: Whether the navigation pane is opened on the last session or not. /// Setting: Whether the navigation pane is opened on the last session or not.
/// </summary> /// </summary>

View File

@@ -1,6 +0,0 @@
using Wino.Core.Domain.Enums;
namespace Wino.Core.Domain.Models.Reader
{
public record ReaderFontModel(ReaderFont Font, string FontFamilyName);
}

View File

@@ -444,8 +444,10 @@
"SettingsPersonalization_Title": "Personalization", "SettingsPersonalization_Title": "Personalization",
"SettingsPrivacyPolicy_Description": "Review privacy policy.", "SettingsPrivacyPolicy_Description": "Review privacy policy.",
"SettingsPrivacyPolicy_Title": "Privacy Policy", "SettingsPrivacyPolicy_Title": "Privacy Policy",
"SettingsReadingPane_Description": "Mail rendering options.", "SettingsReader_Title": "Reader",
"SettingsReadingPane_Title": "Reading Pane", "SettingsComposer_Title": "Composer",
"SettingsReadComposePane_Description": "Fonts, external content.",
"SettingsReadComposePane_Title": "Reader & Composer",
"SettingsReaderFont_Title": "Default Reader Font", "SettingsReaderFont_Title": "Default Reader Font",
"SettingsReaderFontFamily_Description": "Change the default font family and font size for reading mails.", "SettingsReaderFontFamily_Description": "Change the default font family and font size for reading mails.",
"SettingsFontFamily_Title": "Font Family", "SettingsFontFamily_Title": "Font Family",

View File

@@ -2244,14 +2244,24 @@ namespace Wino.Core.Domain
public static string SettingsPrivacyPolicy_Title => Resources.GetTranslatedString(@"SettingsPrivacyPolicy_Title"); public static string SettingsPrivacyPolicy_Title => Resources.GetTranslatedString(@"SettingsPrivacyPolicy_Title");
/// <summary> /// <summary>
/// Mail rendering options. /// Reader
/// </summary> /// </summary>
public static string SettingsReadingPane_Description => Resources.GetTranslatedString(@"SettingsReadingPane_Description"); public static string SettingsReader_Title => Resources.GetTranslatedString(@"SettingsReader_Title");
/// <summary> /// <summary>
/// Reading Pane /// Composer
/// </summary> /// </summary>
public static string SettingsReadingPane_Title => Resources.GetTranslatedString(@"SettingsReadingPane_Title"); public static string SettingsComposer_Title => Resources.GetTranslatedString(@"SettingsComposer_Title");
/// <summary>
/// Fonts, external content.
/// </summary>
public static string SettingsReadComposePane_Description => Resources.GetTranslatedString(@"SettingsReadComposePane_Description");
/// <summary>
/// Reader & Composer
/// </summary>
public static string SettingsReadComposePane_Title => Resources.GetTranslatedString(@"SettingsReadComposePane_Title");
/// <summary> /// <summary>
/// Default Reader Font /// Default Reader Font

View File

@@ -1,50 +1,26 @@
using System.Collections.Generic; using System;
using Serilog; using System.Collections.Generic;
using Wino.Core.Domain.Enums; using System.Linq;
using SkiaSharp;
using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Reader;
namespace Wino.Core.Services namespace Wino.Core.Services;
{
public class FontService : IFontService
{
private readonly IPreferencesService _preferencesService;
private ILogger _logger = Log.ForContext<FontService>();
private readonly List<ReaderFontModel> _availableFonts = public class FontService() : IFontService
[
new ReaderFontModel(ReaderFont.Arial, "Arial"),
new ReaderFontModel(ReaderFont.Calibri, "Calibri"),
new ReaderFontModel(ReaderFont.TimesNewRoman, "Times New Roman"),
new ReaderFontModel(ReaderFont.TrebuchetMS, "Trebuchet MS"),
new ReaderFontModel(ReaderFont.Tahoma, "Tahoma"),
new ReaderFontModel(ReaderFont.Verdana, "Verdana"),
new ReaderFontModel(ReaderFont.Georgia, "Georgia"),
new ReaderFontModel(ReaderFont.CourierNew, "Courier New")
];
public FontService(IPreferencesService preferencesService)
{ {
_preferencesService = preferencesService; private static readonly Lazy<List<string>> _availableFonts = new(InitializeFonts);
private static readonly List<string> _defaultFonts = ["Arial", "Calibri", "Trebuchet MS", "Tahoma", "Verdana", "Courier New", "Georgia", "Times New Roman"];
private static List<string> InitializeFonts()
{
// TODO: Skia used to get system fonts. This is a temporary solution to support UWP and WinUI together.
// After migration to WinUI, this code should be replaced with lightweight solution.
var fontFamilies = SKFontManager.Default.FontFamilies;
List<string> combinedFonts = [.. fontFamilies, .. _defaultFonts];
return [.. combinedFonts.Distinct().OrderBy(x => x)];
} }
public List<ReaderFontModel> GetReaderFonts() => _availableFonts; public List<string> GetFonts() => _availableFonts.Value;
public void ChangeReaderFont(ReaderFont font)
{
_preferencesService.ReaderFont = font;
_logger.Information("Default reader font is changed to {Font}", font);
}
public void ChangeReaderFontSize(int size)
{
_preferencesService.ReaderFontSize = size;
_logger.Information("Default reader font size is changed to {Size}", size);
}
public ReaderFontModel GetCurrentReaderFont() => _availableFonts.Find(f => f.Font == _preferencesService.ReaderFont);
public int GetCurrentReaderFontSize() => _preferencesService.ReaderFontSize;
}
} }

View File

@@ -28,6 +28,7 @@ namespace Wino.Core.Services
private readonly ISignatureService _signatureService; private readonly ISignatureService _signatureService;
private readonly IThreadingStrategyProvider _threadingStrategyProvider; private readonly IThreadingStrategyProvider _threadingStrategyProvider;
private readonly IMimeFileService _mimeFileService; private readonly IMimeFileService _mimeFileService;
private readonly IPreferencesService _preferencesService;
private readonly ILogger _logger = Log.ForContext<MailService>(); private readonly ILogger _logger = Log.ForContext<MailService>();
@@ -38,7 +39,8 @@ namespace Wino.Core.Services
IAccountService accountService, IAccountService accountService,
ISignatureService signatureService, ISignatureService signatureService,
IThreadingStrategyProvider threadingStrategyProvider, IThreadingStrategyProvider threadingStrategyProvider,
IMimeFileService mimeFileService) : base(databaseService) IMimeFileService mimeFileService,
IPreferencesService preferencesService) : base(databaseService)
{ {
_folderService = folderService; _folderService = folderService;
_contactService = contactService; _contactService = contactService;
@@ -46,6 +48,7 @@ namespace Wino.Core.Services
_signatureService = signatureService; _signatureService = signatureService;
_threadingStrategyProvider = threadingStrategyProvider; _threadingStrategyProvider = threadingStrategyProvider;
_mimeFileService = mimeFileService; _mimeFileService = mimeFileService;
_preferencesService = preferencesService;
} }
public async Task<MailCopy> CreateDraftAsync(MailAccount composerAccount, public async Task<MailCopy> CreateDraftAsync(MailAccount composerAccount,
@@ -651,6 +654,9 @@ namespace Wino.Core.Services
message.From.Add(new MailboxAddress(account.SenderName, account.Address)); message.From.Add(new MailboxAddress(account.SenderName, account.Address));
// It contains empty blocks with inlined font, to make sure when users starts typing,it will follow selected font.
var gapHtml = CreateHtmlGap();
// Manage "To" // Manage "To"
if (reason == DraftCreationReason.Reply || reason == DraftCreationReason.ReplyAll) if (reason == DraftCreationReason.Reply || reason == DraftCreationReason.ReplyAll)
{ {
@@ -682,8 +688,7 @@ namespace Wino.Core.Services
{ {
message.InReplyTo = referenceMessage.MessageId; message.InReplyTo = referenceMessage.MessageId;
foreach (var id in referenceMessage.References) message.References.AddRange(referenceMessage.References);
message.References.Add(id);
message.References.Add(referenceMessage.MessageId); message.References.Add(referenceMessage.MessageId);
} }
@@ -711,14 +716,18 @@ namespace Wino.Core.Services
if (string.IsNullOrWhiteSpace(builder.HtmlBody)) if (string.IsNullOrWhiteSpace(builder.HtmlBody))
{ {
builder.HtmlBody = $"<br><br><br>{signature.HtmlBody}"; builder.HtmlBody = $"{gapHtml}{signature.HtmlBody}";
} }
else else
{ {
builder.HtmlBody = $"<br><br><br>{signature.HtmlBody}" + builder.HtmlBody; builder.HtmlBody = $"{gapHtml}{signature.HtmlBody}{gapHtml}{builder.HtmlBody}";
} }
} }
} }
else
{
builder.HtmlBody = $"{gapHtml}{builder.HtmlBody}";
}
// Manage Subject // Manage Subject
if (reason == DraftCreationReason.Forward && !referenceMessage.Subject.StartsWith("FW: ", StringComparison.OrdinalIgnoreCase)) if (reason == DraftCreationReason.Forward && !referenceMessage.Subject.StartsWith("FW: ", StringComparison.OrdinalIgnoreCase))
@@ -794,7 +803,7 @@ namespace Wino.Core.Services
{ {
var htmlMimeInfo = string.Empty; var htmlMimeInfo = string.Empty;
// Separation Line // Separation Line
htmlMimeInfo += "<br><br><hr style='display:inline-block;width:100%' tabindex='-1'>"; htmlMimeInfo += "<hr style='display:inline-block;width:100%' tabindex='-1'>";
var visitor = _mimeFileService.CreateHTMLPreviewVisitor(referenceMessage, string.Empty); var visitor = _mimeFileService.CreateHTMLPreviewVisitor(referenceMessage, string.Empty);
visitor.Visit(referenceMessage); visitor.Visit(referenceMessage);
@@ -816,6 +825,12 @@ namespace Wino.Core.Services
return htmlMimeInfo; return htmlMimeInfo;
} }
string CreateHtmlGap()
{
var template = $"""<div style="font-family: '{_preferencesService.ComposerFont}', Arial, sans-serif; font-size: {_preferencesService.ComposerFontSize}px"><br></div>""";
return string.Concat(Enumerable.Repeat(template, 5));
}
static string ParticipantsToHtml(InternetAddressList internetAddresses) => static string ParticipantsToHtml(InternetAddressList internetAddresses) =>
string.Join("; ", internetAddresses.Mailboxes string.Join("; ", internetAddresses.Mailboxes
.Select(x => $"{x.Name ?? Translator.UnknownSender} &lt;<a href=\"mailto:{x.Address ?? Translator.UnknownAddress}\">{x.Address ?? Translator.UnknownAddress}</a>&gt;")); .Select(x => $"{x.Name ?? Translator.UnknownSender} &lt;<a href=\"mailto:{x.Address ?? Translator.UnknownAddress}\">{x.Address ?? Translator.UnknownAddress}</a>&gt;"));

View File

@@ -30,6 +30,7 @@
<PackageReference Include="Serilog.Exceptions" Version="8.4.0" /> <PackageReference Include="Serilog.Exceptions" Version="8.4.0" />
<PackageReference Include="Serilog.Sinks.Debug" Version="2.0.0" /> <PackageReference Include="Serilog.Sinks.Debug" Version="2.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" /> <PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="SkiaSharp" Version="2.88.8" />
<PackageReference Include="SqlKata" Version="2.4.0" /> <PackageReference Include="SqlKata" Version="2.4.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" /> <PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" />
<PackageReference Include="Xamarin.Essentials" Version="1.8.1" /> <PackageReference Include="Xamarin.Essentials" Version="1.8.1" />

View File

@@ -108,35 +108,38 @@ namespace Wino.Mail.ViewModels
private readonly IMailService _mailService; private readonly IMailService _mailService;
private readonly ILaunchProtocolService _launchProtocolService; private readonly ILaunchProtocolService _launchProtocolService;
private readonly IMimeFileService _mimeFileService; private readonly IMimeFileService _mimeFileService;
private readonly IStatePersistanceService _statePersistanceService;
private readonly IFolderService _folderService; private readonly IFolderService _folderService;
private readonly IAccountService _accountService; private readonly IAccountService _accountService;
private readonly IWinoRequestDelegator _worker; private readonly IWinoRequestDelegator _worker;
public readonly IFontService FontService;
public readonly IPreferencesService PreferencesService;
public readonly IContactService ContactService; public readonly IContactService ContactService;
public ComposePageViewModel(IDialogService dialogService, public ComposePageViewModel(IDialogService dialogService,
IMailService mailService, IMailService mailService,
ILaunchProtocolService launchProtocolService, ILaunchProtocolService launchProtocolService,
IMimeFileService mimeFileService, IMimeFileService mimeFileService,
IStatePersistanceService statePersistanceService,
INativeAppService nativeAppService, INativeAppService nativeAppService,
IFolderService folderService, IFolderService folderService,
IAccountService accountService, IAccountService accountService,
IWinoRequestDelegator worker, IWinoRequestDelegator worker,
IContactService contactService) : base(dialogService) IContactService contactService,
IFontService fontService,
IPreferencesService preferencesService) : base(dialogService)
{ {
NativeAppService = nativeAppService; NativeAppService = nativeAppService;
_folderService = folderService; _folderService = folderService;
ContactService = contactService; ContactService = contactService;
FontService = fontService;
_mailService = mailService; _mailService = mailService;
_launchProtocolService = launchProtocolService; _launchProtocolService = launchProtocolService;
_mimeFileService = mimeFileService; _mimeFileService = mimeFileService;
_statePersistanceService = statePersistanceService;
_accountService = accountService; _accountService = accountService;
_worker = worker; _worker = worker;
SelectedToolbarSection = ToolbarSections[0]; SelectedToolbarSection = ToolbarSections[0];
PreferencesService = preferencesService;
} }
[RelayCommand] [RelayCommand]

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using Wino.Core.Domain; using Wino.Core.Domain;
using Wino.Core.Domain.Enums; using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Interfaces;
@@ -9,23 +10,40 @@ namespace Wino.Mail.ViewModels
{ {
public IPreferencesService PreferencesService { get; } public IPreferencesService PreferencesService { get; }
private List<MailOperation> availableHoverActions = new List<MailOperation> private int selectedMarkAsOptionIndex;
public int SelectedMarkAsOptionIndex
{ {
get => selectedMarkAsOptionIndex;
set
{
if (SetProperty(ref selectedMarkAsOptionIndex, value))
{
if (value >= 0)
{
PreferencesService.MarkAsPreference = (MailMarkAsOption)Enum.GetValues(typeof(MailMarkAsOption)).GetValue(value);
}
}
}
}
private readonly List<MailOperation> availableHoverActions =
[
MailOperation.Archive, MailOperation.Archive,
MailOperation.SoftDelete, MailOperation.SoftDelete,
MailOperation.SetFlag, MailOperation.SetFlag,
MailOperation.MarkAsRead, MailOperation.MarkAsRead,
MailOperation.MoveToJunk MailOperation.MoveToJunk
}; ];
public List<string> AvailableHoverActionsTranslations { get; set; } = new List<string>() public List<string> AvailableHoverActionsTranslations { get; set; } =
{ [
Translator.HoverActionOption_Archive, Translator.HoverActionOption_Archive,
Translator.HoverActionOption_Delete, Translator.HoverActionOption_Delete,
Translator.HoverActionOption_ToggleFlag, Translator.HoverActionOption_ToggleFlag,
Translator.HoverActionOption_ToggleRead, Translator.HoverActionOption_ToggleRead,
Translator.HoverActionOption_MoveJunk Translator.HoverActionOption_MoveJunk
}; ];
#region Properties #region Properties
@@ -82,6 +100,8 @@ namespace Wino.Mail.ViewModels
leftHoverActionIndex = availableHoverActions.IndexOf(PreferencesService.LeftHoverAction); leftHoverActionIndex = availableHoverActions.IndexOf(PreferencesService.LeftHoverAction);
centerHoverActionIndex = availableHoverActions.IndexOf(PreferencesService.CenterHoverAction); centerHoverActionIndex = availableHoverActions.IndexOf(PreferencesService.CenterHoverAction);
rightHoverActionIndex = availableHoverActions.IndexOf(PreferencesService.RightHoverAction); rightHoverActionIndex = availableHoverActions.IndexOf(PreferencesService.RightHoverAction);
SelectedMarkAsOptionIndex = Array.IndexOf(Enum.GetValues(typeof(MailMarkAsOption)), PreferencesService.MarkAsPreference);
} }
} }
} }

View File

@@ -0,0 +1,73 @@
using System.Collections.Generic;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using Wino.Core.Domain.Interfaces;
namespace Wino.Mail.ViewModels
{
public partial class ReadComposePanePageViewModel : BaseViewModel,
IRecipient<PropertyChangedMessage<string>>,
IRecipient<PropertyChangedMessage<int>>
{
private readonly IFontService _fontService;
public IPreferencesService PreferencesService { get; set; }
public List<string> AvailableFonts => _fontService.GetFonts();
[ObservableProperty]
[NotifyPropertyChangedRecipients]
string currentReaderFont;
[ObservableProperty]
[NotifyPropertyChangedRecipients]
int currentReaderFontSize;
[ObservableProperty]
[NotifyPropertyChangedRecipients]
string currentComposerFont;
[ObservableProperty]
[NotifyPropertyChangedRecipients]
int currentComposerFontSize;
public ReadComposePanePageViewModel(IDialogService dialogService,
IFontService fontService,
IPreferencesService preferencesService) : base(dialogService)
{
_fontService = fontService;
PreferencesService = preferencesService;
CurrentReaderFont = fontService.GetCurrentReaderFont();
CurrentReaderFontSize = fontService.GetCurrentReaderFontSize();
CurrentComposerFont = fontService.GetCurrentComposerFont();
CurrentComposerFontSize = fontService.GetCurrentComposerFontSize();
}
public void Receive(PropertyChangedMessage<string> message)
{
if (message.PropertyName == nameof(CurrentReaderFont) && message.OldValue != message.NewValue)
{
_fontService.SetReaderFont(message.NewValue);
}
if (message.PropertyName == nameof(CurrentComposerFont) && message.OldValue != message.NewValue)
{
_fontService.SetComposerFont(message.NewValue);
}
}
public void Receive(PropertyChangedMessage<int> message)
{
if (message.PropertyName == nameof(CurrentReaderFontSize))
{
_fontService.SetReaderFontSize(CurrentReaderFontSize);
}
else if (message.PropertyName == nameof(CurrentComposerFontSize))
{
_fontService.SetComposerFontSize(CurrentComposerFontSize);
}
}
}
}

View File

@@ -1,77 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Reader;
namespace Wino.Mail.ViewModels
{
public partial class ReadingPanePageViewModel : BaseViewModel,
IRecipient<PropertyChangedMessage<ReaderFontModel>>,
IRecipient<PropertyChangedMessage<int>>
{
public IPreferencesService PreferencesService { get; set; }
private int selectedMarkAsOptionIndex;
private readonly IFontService _fontService;
public int SelectedMarkAsOptionIndex
{
get => selectedMarkAsOptionIndex;
set
{
if (SetProperty(ref selectedMarkAsOptionIndex, value))
{
if (value >= 0)
{
PreferencesService.MarkAsPreference = (MailMarkAsOption)Enum.GetValues(typeof(MailMarkAsOption)).GetValue(value);
}
}
}
}
public List<ReaderFontModel> ReaderFonts => _fontService.GetReaderFonts();
[ObservableProperty]
[NotifyPropertyChangedRecipients]
ReaderFontModel currentReaderFont;
[ObservableProperty]
[NotifyPropertyChangedRecipients]
int currentReaderFontSize;
public ReadingPanePageViewModel(IDialogService dialogService,
IFontService fontService,
IPreferencesService preferencesService) : base(dialogService)
{
_fontService = fontService;
PreferencesService = preferencesService;
SelectedMarkAsOptionIndex = Array.IndexOf(Enum.GetValues(typeof(MailMarkAsOption)), PreferencesService.MarkAsPreference);
CurrentReaderFont = fontService.GetCurrentReaderFont();
CurrentReaderFontSize = fontService.GetCurrentReaderFontSize();
}
public void Receive(PropertyChangedMessage<ReaderFontModel> message)
{
if (message.OldValue != message.NewValue)
{
_fontService.ChangeReaderFont(message.NewValue.Font);
Debug.WriteLine("Changed reader font.");
}
}
public void Receive(PropertyChangedMessage<int> message)
{
if (message.PropertyName == nameof(CurrentReaderFontSize))
{
_fontService.ChangeReaderFontSize(CurrentReaderFontSize);
}
}
}
}

View File

@@ -9,18 +9,11 @@ using Wino.Core.Messages.Navigation;
namespace Wino.Mail.ViewModels namespace Wino.Mail.ViewModels
{ {
public partial class SettingOptionsPageViewModel : BaseViewModel public partial class SettingOptionsPageViewModel(IDialogService dialogService) : BaseViewModel(dialogService)
{ {
public SettingOptionsPageViewModel(IDialogService dialogService) : base(dialogService) { }
[RelayCommand] [RelayCommand]
private void GoAccountSettings() => Messenger.Send<NavigateSettingsRequested>(); private void GoAccountSettings() => Messenger.Send<NavigateSettingsRequested>();
public override void OnNavigatedTo(NavigationMode mode, object parameters)
{
base.OnNavigatedTo(mode, parameters);
}
[RelayCommand] [RelayCommand]
public void NavigateSubDetail(object type) public void NavigateSubDetail(object type)
{ {
@@ -31,7 +24,7 @@ namespace Wino.Mail.ViewModels
WinoPage.PersonalizationPage => Translator.SettingsPersonalization_Title, WinoPage.PersonalizationPage => Translator.SettingsPersonalization_Title,
WinoPage.AboutPage => Translator.SettingsAbout_Title, WinoPage.AboutPage => Translator.SettingsAbout_Title,
WinoPage.MessageListPage => Translator.SettingsMessageList_Title, WinoPage.MessageListPage => Translator.SettingsMessageList_Title,
WinoPage.ReadingPanePage => Translator.SettingsReadingPane_Title, WinoPage.ReadComposePanePage => Translator.SettingsReadComposePane_Title,
WinoPage.LanguageTimePage => Translator.SettingsLanguageTime_Title, WinoPage.LanguageTimePage => Translator.SettingsLanguageTime_Title,
_ => throw new NotImplementedException() _ => throw new NotImplementedException()
}; };

View File

@@ -135,7 +135,7 @@ namespace Wino
services.AddTransient(typeof(AccountDetailsPageViewModel)); services.AddTransient(typeof(AccountDetailsPageViewModel));
services.AddTransient(typeof(SignatureManagementPageViewModel)); services.AddTransient(typeof(SignatureManagementPageViewModel));
services.AddTransient(typeof(MessageListPageViewModel)); services.AddTransient(typeof(MessageListPageViewModel));
services.AddTransient(typeof(ReadingPanePageViewModel)); services.AddTransient(typeof(ReadComposePanePageViewModel));
services.AddTransient(typeof(MergedAccountDetailsPageViewModel)); services.AddTransient(typeof(MergedAccountDetailsPageViewModel));
services.AddTransient(typeof(LanguageTimePageViewModel)); services.AddTransient(typeof(LanguageTimePageViewModel));
} }

View File

@@ -19,7 +19,11 @@ namespace Wino.Dialogs
{ {
private Func<Task<string>> _getHTMLBodyFunction; private Func<Task<string>> _getHTMLBodyFunction;
private readonly TaskCompletionSource<bool> _domLoadedTask = new TaskCompletionSource<bool>(); private readonly TaskCompletionSource<bool> _domLoadedTask = new TaskCompletionSource<bool>();
private readonly INativeAppService _nativeAppService = App.Current.Services.GetService<INativeAppService>(); private readonly INativeAppService _nativeAppService = App.Current.Services.GetService<INativeAppService>();
private readonly IFontService _fontService = App.Current.Services.GetService<IFontService>();
private readonly IPreferencesService _preferencesService = App.Current.Services.GetService<IPreferencesService>();
public AccountSignature Result; public AccountSignature Result;
public bool IsComposerDarkMode public bool IsComposerDarkMode
@@ -36,7 +40,7 @@ namespace Wino.Dialogs
SignatureNameTextBox.Header = Translator.SignatureEditorDialog_SignatureName_TitleNew; SignatureNameTextBox.Header = Translator.SignatureEditorDialog_SignatureName_TitleNew;
Environment.SetEnvironmentVariable("WEBVIEW2_DEFAULT_BACKGROUND_COLOR", "00FFFFFF"); Environment.SetEnvironmentVariable("WEBVIEW2_DEFAULT_BACKGROUND_COLOR", "00FFFFFF");
Environment.SetEnvironmentVariable("WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS", "--enable-features=OverlayScrollbar,msOverlayScrollbarWinStyle,msOverlayScrollbarWinStyleAnimation"); Environment.SetEnvironmentVariable("WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS", "--enable-features=OverlayScrollbar,msOverlayScrollbarWinStyle,msOverlayScrollbarWinStyleAnimation,FontAccess");
// TODO: Should be added additional logic to enable/disable primary button when webview content changed. // TODO: Should be added additional logic to enable/disable primary button when webview content changed.
IsPrimaryButtonEnabled = true; IsPrimaryButtonEnabled = true;
@@ -275,6 +279,7 @@ namespace Wino.Dialogs
await _domLoadedTask.Task; await _domLoadedTask.Task;
await UpdateEditorThemeAsync(); await UpdateEditorThemeAsync();
await InitializeEditorAsync();
if (string.IsNullOrEmpty(htmlBody)) if (string.IsNullOrEmpty(htmlBody))
{ {
@@ -288,6 +293,16 @@ namespace Wino.Dialogs
} }
} }
private async Task<string> InitializeEditorAsync()
{
var fonts = _fontService.GetFonts();
var composerFont = _preferencesService.ComposerFont;
int composerFontSize = _preferencesService.ComposerFontSize;
var readerFont = _preferencesService.ReaderFont;
int readerFontSize = _preferencesService.ReaderFontSize;
return await ExecuteScriptFunctionAsync("initializeJodit", fonts, composerFont, composerFontSize, readerFont, readerFontSize);
}
private async void ChromiumInitialized(Microsoft.UI.Xaml.Controls.WebView2 sender, Microsoft.UI.Xaml.Controls.CoreWebView2InitializedEventArgs args) private async void ChromiumInitialized(Microsoft.UI.Xaml.Controls.WebView2 sender, Microsoft.UI.Xaml.Controls.CoreWebView2InitializedEventArgs args)
{ {
var editorBundlePath = (await _nativeAppService.GetEditorBundlePathAsync()).Replace("editor.html", string.Empty); var editorBundlePath = (await _nativeAppService.GetEditorBundlePathAsync()).Replace("editor.html", string.Empty);

View File

@@ -1,4 +1,4 @@
const editor = Jodit.make("#editor", { const joditConfig = {
"useSearch": false, "useSearch": false,
"toolbar": true, "toolbar": true,
"buttons": "bold,italic,underline,strikethrough,brush,ul,ol,font,fontsize,paragraph,image,link,indent,outdent,align,lineHeight,table", "buttons": "bold,italic,underline,strikethrough,brush,ul,ol,font,fontsize,paragraph,image,link,indent,outdent,align,lineHeight,table",
@@ -6,7 +6,6 @@ const editor = Jodit.make("#editor", {
"toolbarAdaptive": false, "toolbarAdaptive": false,
"toolbarInlineForSelection": false, "toolbarInlineForSelection": false,
"showCharsCounter": false, "showCharsCounter": false,
style: { font: "14px Arial" },
"showWordsCounter": false, "showWordsCounter": false,
"showXPathInStatusbar": false, "showXPathInStatusbar": false,
"disablePlugins": "add-new-line", "disablePlugins": "add-new-line",
@@ -16,7 +15,31 @@ const editor = Jodit.make("#editor", {
}, },
"enter": "DIV", "enter": "DIV",
"minHeight": 200 "minHeight": 200
}
// This method should be called first all the time.
function initializeJodit(fonts, defaultComposerFont, defaultComposerFontSize, defaultReaderFont, defaultReaderFontSize) {
const fontsWithFallabckObject = fonts.reduce((acc, font) => { acc[`'${font}',Arial,sans-serif`] = font; return acc; }, {});
const mergedConfig = {
...joditConfig,
controls: {
font: {
list: Jodit.atom(fontsWithFallabckObject)
}
},
style: { font: `${defaultReaderFontSize}px ${defaultReaderFont}` },
}
Jodit.plugins.add('inlineFonts', jodit => {
jodit.events.on('afterEnter', e => {
const current = jodit.selection.current().parentNode;
current.style.fontFamily = `'${defaultComposerFont}',Arial,sans-serif`;
current.style.fontSize = `${defaultComposerFontSize}px`;
}); });
});
// Don't add const/let/var here, it should be global
editor = Jodit.make("#editor", mergedConfig);
// Handle the image input change event // Handle the image input change event
imageInput.addEventListener('change', () => { imageInput.addEventListener('change', () => {
@@ -31,6 +54,7 @@ imageInput.addEventListener('change', () => {
} }
}); });
// Listeners for button events
const disabledButtons = ["indent", "outdent"]; const disabledButtons = ["indent", "outdent"];
const ariaPressedButtons = ["bold", "italic", "underline", "strikethrough", "ul", "ol"]; const ariaPressedButtons = ["bold", "italic", "underline", "strikethrough", "ul", "ol"];
@@ -66,10 +90,9 @@ function pressedChanged(buttonContainer) {
function disabledButtonChanged(buttonContainer) { function disabledButtonChanged(buttonContainer) {
const ref = buttonContainer.getAttribute('ref'); const ref = buttonContainer.getAttribute('ref');
const value = buttonContainer.firstChild.getAttribute('disabled'); const value = buttonContainer.firstChild.getAttribute('disabled');
console.log(buttonContainer, ref, value);
window.chrome.webview.postMessage({ type: ref, value: value }); window.chrome.webview.postMessage({ type: ref, value: value });
} }
}
function RenderHTML(htmlString) { function RenderHTML(htmlString) {
editor.s.insertHTML(htmlString); editor.s.insertHTML(htmlString);

View File

@@ -166,9 +166,9 @@ namespace Wino.Services
set => SaveProperty(propertyName: nameof(CurrentLanguage), value); set => SaveProperty(propertyName: nameof(CurrentLanguage), value);
} }
public ReaderFont ReaderFont public string ReaderFont
{ {
get => _configurationService.Get(nameof(ReaderFont), ReaderFont.Calibri); get => _configurationService.Get(nameof(ReaderFont), "Calibri");
set => SaveProperty(propertyName: nameof(ReaderFont), value); set => SaveProperty(propertyName: nameof(ReaderFont), value);
} }
@@ -178,6 +178,18 @@ namespace Wino.Services
set => SaveProperty(propertyName: nameof(ReaderFontSize), value); set => SaveProperty(propertyName: nameof(ReaderFontSize), value);
} }
public string ComposerFont
{
get => _configurationService.Get(nameof(ComposerFont), "Calibri");
set => SaveProperty(propertyName: nameof(ComposerFont), value);
}
public int ComposerFontSize
{
get => _configurationService.Get(nameof(ComposerFontSize), 14);
set => SaveProperty(propertyName: nameof(ComposerFontSize), value);
}
public bool IsNavigationPaneOpened public bool IsNavigationPaneOpened
{ {
get => _configurationService.Get(nameof(IsNavigationPaneOpened), true); get => _configurationService.Get(nameof(IsNavigationPaneOpened), true);

View File

@@ -72,8 +72,8 @@ namespace Wino.Services
return typeof(PersonalizationPage); return typeof(PersonalizationPage);
case WinoPage.MessageListPage: case WinoPage.MessageListPage:
return typeof(MessageListPage); return typeof(MessageListPage);
case WinoPage.ReadingPanePage: case WinoPage.ReadComposePanePage:
return typeof(ReadingPanePage); return typeof(ReadComposePanePage);
case WinoPage.MailRenderingPage: case WinoPage.MailRenderingPage:
return typeof(MailRenderingPage); return typeof(MailRenderingPage);
case WinoPage.ComposePage: case WinoPage.ComposePage:

View File

@@ -0,0 +1,6 @@
using Wino.Mail.ViewModels;
namespace Wino.Views.Abstract
{
public abstract class ReadComposePanePageAbstract : BasePage<ReadComposePanePageViewModel> { }
}

View File

@@ -1,6 +0,0 @@
using Wino.Mail.ViewModels;
namespace Wino.Views.Abstract
{
public abstract class ReadingPanePageAbstract : BasePage<ReadingPanePageViewModel> { }
}

View File

@@ -59,7 +59,7 @@ namespace Wino.Views
InitializeComponent(); InitializeComponent();
Environment.SetEnvironmentVariable("WEBVIEW2_DEFAULT_BACKGROUND_COLOR", "00FFFFFF"); Environment.SetEnvironmentVariable("WEBVIEW2_DEFAULT_BACKGROUND_COLOR", "00FFFFFF");
Environment.SetEnvironmentVariable("WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS", "--enable-features=OverlayScrollbar,msOverlayScrollbarWinStyle,msOverlayScrollbarWinStyleAnimation"); Environment.SetEnvironmentVariable("WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS", "--enable-features=OverlayScrollbar,msOverlayScrollbarWinStyle,msOverlayScrollbarWinStyleAnimation,FontAccess");
} }
private static async void OnIsComposerDarkModeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) private static async void OnIsComposerDarkModeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
@@ -389,6 +389,7 @@ namespace Wino.Views
await DOMLoadedTask.Task; await DOMLoadedTask.Task;
await UpdateEditorThemeAsync(); await UpdateEditorThemeAsync();
await InitializeEditorAsync();
if (string.IsNullOrEmpty(htmlBody)) if (string.IsNullOrEmpty(htmlBody))
{ {
@@ -400,6 +401,16 @@ namespace Wino.Views
} }
} }
private async Task<string> InitializeEditorAsync()
{
var fonts = ViewModel.FontService.GetFonts();
var composerFont = ViewModel.PreferencesService.ComposerFont;
int composerFontSize = ViewModel.PreferencesService.ComposerFontSize;
var readerFont = ViewModel.PreferencesService.ReaderFont;
int readerFontSize = ViewModel.PreferencesService.ReaderFontSize;
return await ExecuteScriptFunctionAsync("initializeJodit", fonts, composerFont, composerFontSize, readerFont, readerFontSize);
}
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e) protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{ {
base.OnNavigatingFrom(e); base.OnNavigatingFrom(e);

View File

@@ -17,6 +17,7 @@ using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Interfaces;
using Wino.Core.Messages.Mails; using Wino.Core.Messages.Mails;
using Wino.Core.Messages.Shell; using Wino.Core.Messages.Shell;
using Wino.Core.Services;
using Wino.Mail.ViewModels.Data; using Wino.Mail.ViewModels.Data;
using Wino.Views.Abstract; using Wino.Views.Abstract;
@@ -273,13 +274,11 @@ namespace Wino.Views
await ExecuteScriptFunctionAsync("ChangeFontSize", _fontService.GetCurrentReaderFontSize()); await ExecuteScriptFunctionAsync("ChangeFontSize", _fontService.GetCurrentReaderFontSize());
// Prepare font family name with fallback to sans-serif by default. // Prepare font family name with fallback to sans-serif by default.
var fontName = _fontService.GetCurrentReaderFont()?.FontFamilyName ?? "Arial"; var fontName = _fontService.GetCurrentReaderFont();
// If font family name is not supported by the browser, fallback to sans-serif. // If font family name is not supported by the browser, fallback to sans-serif.
fontName += ", sans-serif"; fontName += ", sans-serif";
// var fontName = "Starborn";
await ExecuteScriptFunctionAsync("ChangeFontFamily", fontName); await ExecuteScriptFunctionAsync("ChangeFontFamily", fontName);
} }

File diff suppressed because one or more lines are too long

View File

@@ -2,9 +2,9 @@
namespace Wino.Views.Settings namespace Wino.Views.Settings
{ {
public sealed partial class ReadingPanePage : ReadingPanePageAbstract public sealed partial class ReadComposePanePage : ReadComposePanePageAbstract
{ {
public ReadingPanePage() public ReadComposePanePage()
{ {
InitializeComponent(); InitializeComponent();
} }

View File

@@ -68,10 +68,10 @@
<controls:SettingsCard <controls:SettingsCard
Command="{x:Bind ViewModel.NavigateSubDetailCommand}" Command="{x:Bind ViewModel.NavigateSubDetailCommand}"
CommandParameter="{x:Bind enums:WinoPage.ReadingPanePage}" CommandParameter="{x:Bind enums:WinoPage.ReadComposePanePage}"
IsClickEnabled="True" IsClickEnabled="True"
Header="{x:Bind domain:Translator.SettingsReadingPane_Title}" Header="{x:Bind domain:Translator.SettingsReadComposePane_Title}"
Description="{x:Bind domain:Translator.SettingsReadingPane_Description}"> Description="{x:Bind domain:Translator.SettingsReadComposePane_Description}">
<controls:SettingsCard.HeaderIcon> <controls:SettingsCard.HeaderIcon>
<PathIcon <PathIcon
Data="F1 M 20 2.5 L 20 3.75 L 8.75 3.75 L 8.75 2.5 Z M 17.5 15 L 0 15 L 0 13.75 L 17.5 13.75 Z M 2.5 10 L 20 10 L 20 11.25 L 2.5 11.25 Z M 2.5 17.5 L 20 17.5 L 20 18.75 L 2.5 18.75 Z M 3.125 7.5 C 2.695312 7.5 2.291667 7.416992 1.914062 7.250977 C 1.536458 7.084961 1.206055 6.860352 0.922852 6.577148 C 0.639648 6.293945 0.415039 5.963542 0.249023 5.585938 C 0.083008 5.208334 0 4.804688 0 4.375 C 0 3.945312 0.083008 3.541668 0.249023 3.164062 C 0.415039 2.786459 0.639648 2.456055 0.922852 2.172852 C 1.206055 1.889648 1.536458 1.665039 1.914062 1.499023 C 2.291667 1.333008 2.695312 1.25 3.125 1.25 C 3.554688 1.25 3.958333 1.333008 4.335938 1.499023 C 4.713542 1.665039 5.043945 1.889648 5.327148 2.172852 C 5.610352 2.456055 5.834961 2.786459 6.000977 3.164062 C 6.166992 3.541668 6.25 3.945312 6.25 4.375 L 5 4.375 C 5 4.114584 4.951172 3.870443 4.853516 3.642578 C 4.755859 3.414715 4.622396 3.216146 4.453125 3.046875 C 4.283854 2.877605 4.085286 2.744141 3.857422 2.646484 C 3.629557 2.548828 3.385417 2.5 3.125 2.5 C 2.864583 2.5 2.620443 2.548828 2.392578 2.646484 C 2.164713 2.744141 1.966146 2.877605 1.796875 3.046875 C 1.627604 3.216146 1.494141 3.414715 1.396484 3.642578 C 1.298828 3.870443 1.25 4.114584 1.25 4.375 C 1.25 4.635418 1.298828 4.879559 1.396484 5.107422 C 1.494141 5.335287 1.627604 5.533854 1.796875 5.703125 C 1.966146 5.872396 2.164713 6.005859 2.392578 6.103516 C 2.620443 6.201172 2.864583 6.25 3.125 6.25 L 17.5 6.25 L 17.5 7.5 Z " Data="F1 M 20 2.5 L 20 3.75 L 8.75 3.75 L 8.75 2.5 Z M 17.5 15 L 0 15 L 0 13.75 L 17.5 13.75 Z M 2.5 10 L 20 10 L 20 11.25 L 2.5 11.25 Z M 2.5 17.5 L 20 17.5 L 20 18.75 L 2.5 18.75 Z M 3.125 7.5 C 2.695312 7.5 2.291667 7.416992 1.914062 7.250977 C 1.536458 7.084961 1.206055 6.860352 0.922852 6.577148 C 0.639648 6.293945 0.415039 5.963542 0.249023 5.585938 C 0.083008 5.208334 0 4.804688 0 4.375 C 0 3.945312 0.083008 3.541668 0.249023 3.164062 C 0.415039 2.786459 0.639648 2.456055 0.922852 2.172852 C 1.206055 1.889648 1.536458 1.665039 1.914062 1.499023 C 2.291667 1.333008 2.695312 1.25 3.125 1.25 C 3.554688 1.25 3.958333 1.333008 4.335938 1.499023 C 4.713542 1.665039 5.043945 1.889648 5.327148 2.172852 C 5.610352 2.456055 5.834961 2.786459 6.000977 3.164062 C 6.166992 3.541668 6.25 3.945312 6.25 4.375 L 5 4.375 C 5 4.114584 4.951172 3.870443 4.853516 3.642578 C 4.755859 3.414715 4.622396 3.216146 4.453125 3.046875 C 4.283854 2.877605 4.085286 2.744141 3.857422 2.646484 C 3.629557 2.548828 3.385417 2.5 3.125 2.5 C 2.864583 2.5 2.620443 2.548828 2.392578 2.646484 C 2.164713 2.744141 1.966146 2.877605 1.796875 3.046875 C 1.627604 3.216146 1.494141 3.414715 1.396484 3.642578 C 1.298828 3.870443 1.25 4.114584 1.25 4.375 C 1.25 4.635418 1.298828 4.879559 1.396484 5.107422 C 1.494141 5.335287 1.627604 5.533854 1.796875 5.703125 C 1.966146 5.872396 2.164713 6.005859 2.392578 6.103516 C 2.620443 6.201172 2.864583 6.25 3.125 6.25 L 17.5 6.25 L 17.5 7.5 Z "

View File

@@ -51,7 +51,7 @@ namespace Wino.Views
WinoPage.AboutPage => typeof(AboutPage), WinoPage.AboutPage => typeof(AboutPage),
WinoPage.PersonalizationPage => typeof(PersonalizationPage), WinoPage.PersonalizationPage => typeof(PersonalizationPage),
WinoPage.MessageListPage => typeof(MessageListPage), WinoPage.MessageListPage => typeof(MessageListPage),
WinoPage.ReadingPanePage => typeof(ReadingPanePage), WinoPage.ReadComposePanePage => typeof(ReadComposePanePage),
WinoPage.LanguageTimePage => typeof(LanguageTimePage), WinoPage.LanguageTimePage => typeof(LanguageTimePage),
_ => null, _ => null,
}; };

View File

@@ -356,7 +356,7 @@
<Compile Include="Views\Abstract\MessageListPageAbstract.cs" /> <Compile Include="Views\Abstract\MessageListPageAbstract.cs" />
<Compile Include="Views\Abstract\NewAccountManagementPageAbstract.cs" /> <Compile Include="Views\Abstract\NewAccountManagementPageAbstract.cs" />
<Compile Include="Views\Abstract\PersonalizationPageAbstract.cs" /> <Compile Include="Views\Abstract\PersonalizationPageAbstract.cs" />
<Compile Include="Views\Abstract\ReadingPanePageAbstract.cs" /> <Compile Include="Views\Abstract\ReadComposePanePageAbstract.cs" />
<Compile Include="Views\Abstract\SettingOptionsPageAbstract.cs" /> <Compile Include="Views\Abstract\SettingOptionsPageAbstract.cs" />
<Compile Include="Views\Abstract\SettingsPageAbstract.cs" /> <Compile Include="Views\Abstract\SettingsPageAbstract.cs" />
<Compile Include="Views\Abstract\SettingsPageBase.cs" /> <Compile Include="Views\Abstract\SettingsPageBase.cs" />
@@ -419,8 +419,8 @@
<Compile Include="AppShell.xaml.cs"> <Compile Include="AppShell.xaml.cs">
<DependentUpon>AppShell.xaml</DependentUpon> <DependentUpon>AppShell.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Views\Settings\ReadingPanePage.xaml.cs"> <Compile Include="Views\Settings\ReadComposePanePage.xaml.cs">
<DependentUpon>ReadingPanePage.xaml</DependentUpon> <DependentUpon>ReadComposePanePage.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Views\Settings\SettingOptionsPage.xaml.cs"> <Compile Include="Views\Settings\SettingOptionsPage.xaml.cs">
<DependentUpon>SettingOptionsPage.xaml</DependentUpon> <DependentUpon>SettingOptionsPage.xaml</DependentUpon>
@@ -647,7 +647,7 @@
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType> <SubType>Designer</SubType>
</Page> </Page>
<Page Include="Views\Settings\ReadingPanePage.xaml"> <Page Include="Views\Settings\ReadComposePanePage.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>