Embedded images replaced with cid linked resources. (#313)

* Added logic to replace embedded images with linked resources

* Added alt text for images and replaced NewtonSoft with Text.Json

* Fix draft mime preparation

* Fix crashes for signatures without images.

---------

Co-authored-by: Burak Kaan Köse <bkaankose@outlook.com>
This commit is contained in:
Tiktack
2024-08-11 23:58:54 +02:00
committed by GitHub
parent 983bc21448
commit 5912adff93
23 changed files with 148 additions and 253 deletions

View File

@@ -2,6 +2,7 @@
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Toolkit.Uwp.Helpers;
using Windows.ApplicationModel.Activation;
using Windows.Storage;
using Windows.UI.Xaml;
@@ -9,7 +10,6 @@ using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Animation;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Services;
using Wino.Helpers;
using Wino.Views;
namespace Wino.Activation

View File

@@ -1,9 +1,9 @@
using System;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
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;
@@ -78,7 +78,7 @@ namespace Wino.Dialogs
{
var editorContent = await InvokeScriptSafeAsync("GetHTMLContent();");
return JsonConvert.DeserializeObject<string>(editorContent);
return JsonSerializer.Deserialize<string>(editorContent);
});
var underlyingThemeService = App.Current.Services.GetService<IUnderlyingThemeService>();
@@ -193,7 +193,7 @@ namespace Wino.Dialogs
string script = functionName + "(";
for (int i = 0; i < parameters.Length; i++)
{
script += JsonConvert.SerializeObject(parameters[i]);
script += JsonSerializer.Serialize(parameters[i]);
if (i < parameters.Length - 1)
{
script += ", ";
@@ -327,7 +327,7 @@ namespace Wino.Dialogs
private void ScriptMessageReceived(CoreWebView2 sender, CoreWebView2WebMessageReceivedEventArgs args)
{
var change = JsonConvert.DeserializeObject<WebViewMessage>(args.WebMessageAsJson);
var change = JsonSerializer.Deserialize<WebViewMessage>(args.WebMessageAsJson);
if (change.Type == "bold")
{

View File

@@ -1,25 +0,0 @@
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace Wino.Helpers
{
public static class JsonHelpers
{
public static async Task<T> ToObjectAsync<T>(string value)
{
return await Task.Run<T>(() =>
{
return JsonConvert.DeserializeObject<T>(value);
});
}
public static async Task<string> StringifyAsync(object value)
{
return await Task.Run<string>(() =>
{
return JsonConvert.SerializeObject(value);
});
}
}
}

View File

@@ -1,119 +0,0 @@
using System;
using System.IO;
using System.Threading.Tasks;
using Windows.Storage;
using Windows.Storage.Streams;
namespace Wino.Helpers
{
// Use these extension methods to store and retrieve local and roaming app data
// More details regarding storing and retrieving app data at https://docs.microsoft.com/windows/uwp/app-settings/store-and-retrieve-app-data
public static class SettingsStorageExtensions
{
private const string FileExtension = ".json";
public static bool IsRoamingStorageAvailable(this ApplicationData appData)
{
return appData.RoamingStorageQuota == 0;
}
public static async Task SaveAsync<T>(this StorageFolder folder, string name, T content)
{
var file = await folder.CreateFileAsync(GetFileName(name), CreationCollisionOption.ReplaceExisting);
var fileContent = await JsonHelpers.StringifyAsync(content);
await FileIO.WriteTextAsync(file, fileContent);
}
public static async Task<T> ReadAsync<T>(this StorageFolder folder, string name)
{
if (!File.Exists(Path.Combine(folder.Path, GetFileName(name))))
{
return default;
}
var file = await folder.GetFileAsync($"{name}.json");
var fileContent = await FileIO.ReadTextAsync(file);
return await JsonHelpers.ToObjectAsync<T>(fileContent);
}
public static async Task SaveAsync<T>(this ApplicationDataContainer settings, string key, T value)
{
settings.SaveString(key, await JsonHelpers.StringifyAsync(value));
}
public static void SaveString(this ApplicationDataContainer settings, string key, string value)
{
settings.Values[key] = value;
}
public static async Task<T> ReadAsync<T>(this ApplicationDataContainer settings, string key)
{
object obj = null;
if (settings.Values.TryGetValue(key, out obj))
{
return await JsonHelpers.ToObjectAsync<T>((string)obj);
}
return default;
}
public static async Task<StorageFile> SaveFileAsync(this StorageFolder folder, byte[] content, string fileName, CreationCollisionOption options = CreationCollisionOption.ReplaceExisting)
{
if (content == null)
{
throw new ArgumentNullException(nameof(content));
}
if (string.IsNullOrEmpty(fileName))
{
throw new ArgumentException("File name is null or empty. Specify a valid file name", nameof(fileName));
}
var storageFile = await folder.CreateFileAsync(fileName, options);
await FileIO.WriteBytesAsync(storageFile, content);
return storageFile;
}
public static async Task<byte[]> ReadFileAsync(this StorageFolder folder, string fileName)
{
var item = await folder.TryGetItemAsync(fileName).AsTask().ConfigureAwait(false);
if ((item != null) && item.IsOfType(StorageItemTypes.File))
{
var storageFile = await folder.GetFileAsync(fileName);
byte[] content = await storageFile.ReadBytesAsync();
return content;
}
return null;
}
public static async Task<byte[]> ReadBytesAsync(this StorageFile file)
{
if (file != null)
{
using (IRandomAccessStream stream = await file.OpenReadAsync())
{
using (var reader = new DataReader(stream.GetInputStreamAt(0)))
{
await reader.LoadAsync((uint)stream.Size);
var bytes = new byte[stream.Size];
reader.ReadBytes(bytes);
return bytes;
}
}
}
return null;
}
private static string GetFileName(string name)
{
return string.Concat(name, FileExtension);
}
}
}

View File

@@ -48,7 +48,7 @@ function initializeJodit(fonts, defaultComposerFont, defaultComposerFontSize, de
const reader = new FileReader();
reader.onload = function (event) {
const base64Image = event.target.result;
insertImages([base64Image]);
insertImages([{ data: base64Image, name: file.name }]);
};
reader.readAsDataURL(file);
}
@@ -121,8 +121,8 @@ function toggleToolbar(enable) {
}
}
function insertImages(images) {
images.forEach(image => {
editor.selection.insertHTML(`<img src="${image}" alt="Embedded Image">`);
function insertImages(imagesInfo) {
imagesInfo.forEach(imageInfo => {
editor.selection.insertHTML(`<img src="${imageInfo.data}" alt="${imageInfo.name}">`);
});
};

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive.Linq;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Messaging;
@@ -13,7 +14,6 @@ using Microsoft.Toolkit.Uwp.Helpers;
using Microsoft.UI.Xaml.Controls;
using Microsoft.Web.WebView2.Core;
using MimeKit;
using Newtonsoft.Json;
using Windows.ApplicationModel.DataTransfer;
using Windows.Foundation;
using Windows.Storage;
@@ -167,7 +167,7 @@ namespace Wino.Views
foreach (var file in files)
{
if (ValidateImageFile(file))
if (IsValidImageFile(file))
{
isValid = true;
}
@@ -200,15 +200,21 @@ namespace Wino.Views
var storageItems = await e.DataView.GetStorageItemsAsync();
var files = storageItems.OfType<StorageFile>();
var imageDataURLs = new List<string>();
var imagesInformation = new List<ImageInfo>();
foreach (var file in files)
{
if (ValidateImageFile(file))
imageDataURLs.Add(await GetDataURL(file));
if (IsValidImageFile(file))
{
imagesInformation.Add(new ImageInfo
{
Data = await GetDataURL(file),
Name = file.Name
});
}
}
await InvokeScriptSafeAsync($"insertImages({JsonConvert.SerializeObject(imageDataURLs)});");
await InvokeScriptSafeAsync($"insertImages({JsonSerializer.Serialize(imagesInformation)});");
}
}
// State should be reset even when an exception occurs, otherwise the UI will be stuck in a dragging state.
@@ -240,7 +246,7 @@ namespace Wino.Views
}
}
private bool ValidateImageFile(StorageFile file)
private bool IsValidImageFile(StorageFile file)
{
string[] allowedTypes = new string[] { ".jpg", ".jpeg", ".png" };
var fileType = file.FileType.ToLower();
@@ -321,7 +327,7 @@ namespace Wino.Views
string script = functionName + "(";
for (int i = 0; i < parameters.Length; i++)
{
script += JsonConvert.SerializeObject(parameters[i]);
script += JsonSerializer.Serialize(parameters[i]);
if (i < parameters.Length - 1)
{
script += ", ";
@@ -463,7 +469,7 @@ namespace Wino.Views
{
var editorContent = await InvokeScriptSafeAsync("GetHTMLContent();");
return JsonConvert.DeserializeObject<string>(editorContent);
return JsonSerializer.Deserialize<string>(editorContent);
});
var underlyingThemeService = App.Current.Services.GetService<IUnderlyingThemeService>();
@@ -487,7 +493,7 @@ namespace Wino.Views
private void ScriptMessageReceived(CoreWebView2 sender, CoreWebView2WebMessageReceivedEventArgs args)
{
var change = JsonConvert.DeserializeObject<WebViewMessage>(args.WebMessageAsJson);
var change = JsonSerializer.Deserialize<WebViewMessage>(args.WebMessageAsJson);
if (change.Type == "bold")
{

View File

@@ -1,12 +1,12 @@
using System;
using System.Linq;
using System.Text.Json;
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;
@@ -70,7 +70,7 @@ namespace Wino.Views
string script = functionName + "(";
for (int i = 0; i < parameters.Length; i++)
{
script += JsonConvert.SerializeObject(parameters[i]);
script += JsonSerializer.Serialize(parameters[i]);
if (i < parameters.Length - 1)
{
script += ", ";

View File

@@ -298,14 +298,12 @@
<Compile Include="Dialogs\AccountCreationDialog.xaml.cs">
<DependentUpon>AccountCreationDialog.xaml</DependentUpon>
</Compile>
<Compile Include="Helpers\JsonHelpers.cs" />
<Compile Include="Extensions\AnimationExtensions.cs" />
<Compile Include="Extensions\CompositionExtensions.Implicit.cs" />
<Compile Include="Extensions\CompositionExtensions.Size.cs" />
<Compile Include="Extensions\CompositionEnums.cs" />
<Compile Include="Extensions\EnumerableExtensions.cs" />
<Compile Include="Extensions\UtilExtensions.cs" />
<Compile Include="Helpers\SettingsStorageExtensions.cs" />
<Compile Include="MenuFlyouts\FilterMenuFlyout.cs" />
<Compile Include="Controls\ImagePreviewControl.cs" />
<Compile Include="Controls\MailItemDisplayInformationControl.xaml.cs">