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:
@@ -1,22 +1,22 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Wino.Core.Domain.Models.AutoDiscovery
|
||||
{
|
||||
public class AutoDiscoveryProviderSetting
|
||||
{
|
||||
[JsonProperty("protocol")]
|
||||
[JsonPropertyName("protocol")]
|
||||
public string Protocol { get; set; }
|
||||
|
||||
[JsonProperty("address")]
|
||||
[JsonPropertyName("address")]
|
||||
public string Address { get; set; }
|
||||
|
||||
[JsonProperty("port")]
|
||||
[JsonPropertyName("port")]
|
||||
public int Port { get; set; }
|
||||
|
||||
[JsonProperty("secure")]
|
||||
[JsonPropertyName("secure")]
|
||||
public string Secure { get; set; }
|
||||
|
||||
[JsonProperty("username")]
|
||||
[JsonPropertyName("username")]
|
||||
public string Username { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Wino.Core.Domain.Entities;
|
||||
|
||||
namespace Wino.Core.Domain.Models.AutoDiscovery
|
||||
{
|
||||
public class AutoDiscoverySettings
|
||||
{
|
||||
[JsonProperty("domain")]
|
||||
[JsonPropertyName("domain")]
|
||||
public string Domain { get; set; }
|
||||
|
||||
[JsonProperty("password")]
|
||||
[JsonPropertyName("password")]
|
||||
public string Password { get; set; }
|
||||
|
||||
[JsonProperty("settings")]
|
||||
[JsonPropertyName("settings")]
|
||||
public List<AutoDiscoveryProviderSetting> Settings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
||||
12
Wino.Core.Domain/Models/Reader/ImageInfo.cs
Normal file
12
Wino.Core.Domain/Models/Reader/ImageInfo.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Reader;
|
||||
|
||||
public class ImageInfo
|
||||
{
|
||||
[JsonPropertyName("data")]
|
||||
public string Data { get; set; }
|
||||
|
||||
[JsonPropertyName("name")]
|
||||
public string Name { get; set; }
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Reader
|
||||
{
|
||||
@@ -7,10 +7,10 @@ namespace Wino.Core.Domain.Models.Reader
|
||||
/// </summary>
|
||||
public class WebViewMessage
|
||||
{
|
||||
[JsonProperty("type")]
|
||||
[JsonPropertyName("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
[JsonProperty("value")]
|
||||
[JsonPropertyName("value")]
|
||||
public string Value { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,6 @@
|
||||
</PackageReference>
|
||||
<PackageReference Include="MimeKit" Version="4.7.1" />
|
||||
<PackageReference Include="MailKit" Version="4.7.1.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="sqlite-net-pcl" Version="1.8.116" />
|
||||
<PackageReference Include="System.Text.Json" Version="8.0.4" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -4,10 +4,10 @@ using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.Toolkit.Uwp.Helpers;
|
||||
using Newtonsoft.Json;
|
||||
using Windows.Storage;
|
||||
using Windows.UI;
|
||||
using Windows.UI.ViewManagement;
|
||||
@@ -406,7 +406,7 @@ namespace Wino.Services
|
||||
// Save metadata.
|
||||
var metadataFile = await themeFolder.CreateFileAsync($"{newTheme.Id}.json", CreationCollisionOption.ReplaceExisting);
|
||||
|
||||
var serialized = JsonConvert.SerializeObject(newTheme);
|
||||
var serialized = JsonSerializer.Serialize(newTheme);
|
||||
await FileIO.WriteTextAsync(metadataFile, serialized);
|
||||
|
||||
return newTheme;
|
||||
@@ -438,7 +438,7 @@ namespace Wino.Services
|
||||
{
|
||||
var fileContent = await FileIO.ReadTextAsync(file);
|
||||
|
||||
return JsonConvert.DeserializeObject<CustomThemeMetadata>(fileContent);
|
||||
return JsonSerializer.Deserialize<CustomThemeMetadata>(fileContent);
|
||||
}
|
||||
|
||||
public string GetSystemAccentColorHex()
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Wino.Core.Domain;
|
||||
using Wino.Core.Domain.Entities;
|
||||
using Wino.Core.Domain.Enums;
|
||||
@@ -58,14 +58,14 @@ namespace Wino.Core.Authenticators
|
||||
if (!response.IsSuccessStatusCode)
|
||||
throw new GoogleAuthenticationException(Translator.Exception_GoogleAuthorizationCodeExchangeFailed);
|
||||
|
||||
var parsed = JObject.Parse(responseString);
|
||||
var parsed = JsonNode.Parse(responseString).AsObject();
|
||||
|
||||
if (parsed.ContainsKey("error"))
|
||||
throw new GoogleAuthenticationException(parsed["error"]["message"].Value<string>());
|
||||
throw new GoogleAuthenticationException(parsed["error"]["message"].GetValue<string>());
|
||||
|
||||
var accessToken = parsed["access_token"].Value<string>();
|
||||
var refreshToken = parsed["refresh_token"].Value<string>();
|
||||
var expiresIn = parsed["expires_in"].Value<long>();
|
||||
var accessToken = parsed["access_token"].GetValue<string>();
|
||||
var refreshToken = parsed["refresh_token"].GetValue<string>();
|
||||
var expiresIn = parsed["expires_in"].GetValue<long>();
|
||||
|
||||
var expirationDate = DateTime.UtcNow.AddSeconds(expiresIn);
|
||||
|
||||
@@ -76,12 +76,12 @@ namespace Wino.Core.Authenticators
|
||||
var userinfoResponse = await client.GetAsync(UserInfoEndpoint);
|
||||
string userinfoResponseContent = await userinfoResponse.Content.ReadAsStringAsync();
|
||||
|
||||
var parsedUserInfo = JObject.Parse(userinfoResponseContent);
|
||||
var parsedUserInfo = JsonNode.Parse(userinfoResponseContent).AsObject();
|
||||
|
||||
if (parsedUserInfo.ContainsKey("error"))
|
||||
throw new GoogleAuthenticationException(parsedUserInfo["error"]["message"].Value<string>());
|
||||
throw new GoogleAuthenticationException(parsedUserInfo["error"]["message"].GetValue<string>());
|
||||
|
||||
var username = parsedUserInfo["emailAddress"].Value<string>();
|
||||
var username = parsedUserInfo["emailAddress"].GetValue<string>();
|
||||
|
||||
return new TokenInformation()
|
||||
{
|
||||
@@ -166,13 +166,13 @@ namespace Wino.Core.Authenticators
|
||||
|
||||
string responseString = await response.Content.ReadAsStringAsync();
|
||||
|
||||
var parsed = JObject.Parse(responseString);
|
||||
var parsed = JsonNode.Parse(responseString).AsObject();
|
||||
|
||||
// TODO: Error parsing is incorrect.
|
||||
if (parsed.ContainsKey("error"))
|
||||
throw new GoogleAuthenticationException(parsed["error_description"].Value<string>());
|
||||
throw new GoogleAuthenticationException(parsed["error_description"].GetValue<string>());
|
||||
|
||||
var accessToken = parsed["access_token"].Value<string>();
|
||||
var accessToken = parsed["access_token"].GetValue<string>();
|
||||
|
||||
string activeRefreshToken = refresh_token;
|
||||
|
||||
@@ -182,10 +182,10 @@ namespace Wino.Core.Authenticators
|
||||
|
||||
if (parsed.ContainsKey("refresh_token"))
|
||||
{
|
||||
activeRefreshToken = parsed["refresh_token"].Value<string>();
|
||||
activeRefreshToken = parsed["refresh_token"].GetValue<string>();
|
||||
}
|
||||
|
||||
var expiresIn = parsed["expires_in"].Value<long>();
|
||||
var expiresIn = parsed["expires_in"].GetValue<long>();
|
||||
var expirationDate = DateTime.UtcNow.AddSeconds(expiresIn);
|
||||
|
||||
return new TokenInformationBase()
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
using System.IO;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Google.Apis.Gmail.v1.Data;
|
||||
using HtmlAgilityPack;
|
||||
using MimeKit;
|
||||
using MimeKit.IO;
|
||||
using MimeKit.IO.Filters;
|
||||
using MimeKit.Utils;
|
||||
using Wino.Core.Domain;
|
||||
using Wino.Core.Domain.Entities;
|
||||
|
||||
@@ -48,5 +51,71 @@ namespace Wino.Core.Extensions
|
||||
|
||||
return new AddressInformation() { Name = address.Name, Address = address.Address };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets html body replacing base64 images with cid linked resources.
|
||||
/// Updates text body based on html.
|
||||
/// </summary>
|
||||
/// <param name="bodyBuilder">Body builder.</param>
|
||||
/// <param name="htmlContent">Html content that can have embedded images.</param>
|
||||
/// <returns>Body builder with set HtmlBody.</returns>
|
||||
public static BodyBuilder SetHtmlBody(this BodyBuilder bodyBuilder, string htmlContent)
|
||||
{
|
||||
if (string.IsNullOrEmpty(htmlContent)) return bodyBuilder;
|
||||
|
||||
var doc = new HtmlDocument();
|
||||
doc.LoadHtml(htmlContent);
|
||||
|
||||
var imgNodes = doc.DocumentNode.SelectNodes("//img");
|
||||
|
||||
if (imgNodes != null)
|
||||
{
|
||||
foreach (var node in imgNodes)
|
||||
{
|
||||
var src = node.GetAttributeValue("src", string.Empty);
|
||||
|
||||
if (string.IsNullOrEmpty(src)) continue;
|
||||
|
||||
if (!src.StartsWith("data:image"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var parts = src.Substring(11).Split([";base64,"], StringSplitOptions.None);
|
||||
|
||||
string mimeType = parts[0];
|
||||
string base64Content = parts[1];
|
||||
|
||||
var alt = node.GetAttributeValue("alt", $"Embedded_Image.{mimeType}");
|
||||
|
||||
// Convert the base64 content to binary data
|
||||
byte[] imageData = Convert.FromBase64String(base64Content);
|
||||
|
||||
// Create a new linked resource as MimePart
|
||||
var image = new MimePart("image", mimeType)
|
||||
{
|
||||
ContentId = MimeUtils.GenerateMessageId(),
|
||||
Content = new MimeContent(new MemoryStream(imageData)),
|
||||
ContentDisposition = new ContentDisposition(ContentDisposition.Inline),
|
||||
ContentDescription = alt.Replace(" ", "_"),
|
||||
FileName = alt,
|
||||
ContentTransferEncoding = ContentEncoding.Base64
|
||||
};
|
||||
|
||||
bodyBuilder.LinkedResources.Add(image);
|
||||
|
||||
node.SetAttributeValue("src", $"cid:{image.ContentId}");
|
||||
}
|
||||
}
|
||||
|
||||
bodyBuilder.HtmlBody = doc.DocumentNode.InnerHtml;
|
||||
|
||||
if (!string.IsNullOrEmpty(bodyBuilder.HtmlBody))
|
||||
{
|
||||
bodyBuilder.TextBody = HtmlAgilityPackExtensions.GetPreviewText(bodyBuilder.HtmlBody);
|
||||
}
|
||||
|
||||
return bodyBuilder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace Wino.Core.Http
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// We need to generate HttpRequestMessage for batch requests, and sometimes we need to
|
||||
/// serialize content as json. However, some of the fields like 'ODataType' must be ignored
|
||||
/// in order PATCH requests to succeed. Therefore Microsoft account synchronizer uses
|
||||
/// special JsonSerializerSettings for ignoring some of the properties.
|
||||
/// </summary>
|
||||
public class MicrosoftJsonContractResolver : DefaultContractResolver
|
||||
{
|
||||
private readonly HashSet<string> ignoreProps = new HashSet<string>()
|
||||
{
|
||||
"ODataType"
|
||||
};
|
||||
|
||||
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
|
||||
{
|
||||
JsonProperty property = base.CreateProperty(member, memberSerialization);
|
||||
|
||||
if (ignoreProps.Contains(property.PropertyName))
|
||||
{
|
||||
property.ShouldSerialize = _ => false;
|
||||
}
|
||||
|
||||
return property;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,13 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Requests
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Bundle that encapsulates batch request and native request without a response.
|
||||
/// </summary>
|
||||
@@ -43,7 +41,7 @@ namespace Wino.Core.Domain.Models.Requests
|
||||
{
|
||||
var content = await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
|
||||
return JsonConvert.DeserializeObject<TResponse>(content) ?? throw new InvalidOperationException("Invalid Http Response Deserialization");
|
||||
return JsonSerializer.Deserialize<TResponse>(content) ?? throw new InvalidOperationException("Invalid Http Response Deserialization");
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using Serilog;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.AutoDiscovery;
|
||||
@@ -43,7 +43,7 @@ namespace Wino.Core.Services
|
||||
{
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
|
||||
return JsonConvert.DeserializeObject<AutoDiscoverySettings>(content);
|
||||
return JsonSerializer.Deserialize<AutoDiscoverySettings>(content);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -636,10 +636,7 @@ namespace Wino.Core.Services
|
||||
_ => CreateReferencedDraft(builder, message, draftCreationOptions, account, signature),
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(builder.HtmlBody))
|
||||
{
|
||||
builder.TextBody = HtmlAgilityPackExtensions.GetPreviewText(builder.HtmlBody);
|
||||
}
|
||||
builder.SetHtmlBody(builder.HtmlBody);
|
||||
|
||||
message.Body = builder.ToMessageBody();
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Newtonsoft.Json;
|
||||
using Serilog;
|
||||
using Wino.Core.Domain;
|
||||
using Wino.Core.Domain.Enums;
|
||||
@@ -44,7 +44,7 @@ namespace Wino.Core.Services
|
||||
|
||||
var stremValue = await new StreamReader(resourceStream).ReadToEndAsync().ConfigureAwait(false);
|
||||
|
||||
var translationLookups = JsonConvert.DeserializeObject<Dictionary<string, string>>(stremValue);
|
||||
var translationLookups = JsonSerializer.Deserialize<Dictionary<string, string>>(stremValue);
|
||||
|
||||
// Insert new translation key-value pairs.
|
||||
// Overwrite existing values for the same keys.
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
<PackageReference Include="Microsoft.Identity.Client.Extensions.Msal" Version="4.62.0" />
|
||||
<PackageReference Include="MimeKit" Version="4.7.1" />
|
||||
<PackageReference Include="morelinq" Version="4.1.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Nito.AsyncEx.Tasks" Version="5.1.2" />
|
||||
<PackageReference Include="Serilog" Version="3.1.1" />
|
||||
<PackageReference Include="Serilog.Exceptions" Version="8.4.0" />
|
||||
|
||||
@@ -8,6 +8,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using MimeKit;
|
||||
using MimeKit.Utils;
|
||||
using Wino.Core.Domain;
|
||||
using Wino.Core.Domain.Entities;
|
||||
using Wino.Core.Domain.Enums;
|
||||
@@ -237,12 +238,9 @@ namespace Wino.Mail.ViewModels
|
||||
{
|
||||
if (GetHTMLBodyFunction != null)
|
||||
{
|
||||
bodyBuilder.HtmlBody = await GetHTMLBodyFunction();
|
||||
bodyBuilder.SetHtmlBody(await GetHTMLBodyFunction());
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(bodyBuilder.HtmlBody))
|
||||
bodyBuilder.TextBody = HtmlAgilityPackExtensions.GetPreviewText(bodyBuilder.HtmlBody);
|
||||
|
||||
if (bodyBuilder.HtmlBody != null && bodyBuilder.TextBody != null)
|
||||
CurrentMimeMessage.Body = bodyBuilder.ToMessageBody();
|
||||
}
|
||||
@@ -401,7 +399,6 @@ namespace Wino.Mail.ViewModels
|
||||
{
|
||||
DialogService.InfoBarMessage("Busy", "Mail is being processed. Please wait a moment and try again.", InfoBarMessageType.Warning);
|
||||
}
|
||||
|
||||
catch (ComposerMimeNotFoundException)
|
||||
{
|
||||
DialogService.InfoBarMessage(Translator.Info_ComposerMissingMIMETitle, Translator.Info_ComposerMissingMIMEMessage, InfoBarMessageType.Error);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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}">`);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -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")
|
||||
{
|
||||
|
||||
@@ -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 += ", ";
|
||||
|
||||
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user