From 468ebe83198afb7764d53aebf2a3f559766ab0ce Mon Sep 17 00:00:00 2001 From: Guerra24 Date: Sat, 21 Jan 2023 19:01:35 -0600 Subject: [PATCH 01/31] Update WebView2 with support for file picker --- LRReader.Avalonia/LRReader.Avalonia.csproj | 2 +- LRReader.Shared/LRReader.Shared.csproj | 6 +- LRReader.Shared/NullableAttributes.cs | 142 --------------------- LRReader.Shared/Services/Tabs.cs | 2 +- LRReader.UWP/LRReader.UWP.csproj | 9 +- LRReader.UWP/Services/ImageProcessing.cs | 5 +- 6 files changed, 16 insertions(+), 150 deletions(-) delete mode 100644 LRReader.Shared/NullableAttributes.cs diff --git a/LRReader.Avalonia/LRReader.Avalonia.csproj b/LRReader.Avalonia/LRReader.Avalonia.csproj index 8ca8938f..cf870c59 100644 --- a/LRReader.Avalonia/LRReader.Avalonia.csproj +++ b/LRReader.Avalonia/LRReader.Avalonia.csproj @@ -10,7 +10,7 @@ - + diff --git a/LRReader.Shared/LRReader.Shared.csproj b/LRReader.Shared/LRReader.Shared.csproj index 13aaf5c0..a369515e 100644 --- a/LRReader.Shared/LRReader.Shared.csproj +++ b/LRReader.Shared/LRReader.Shared.csproj @@ -14,13 +14,17 @@ - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/LRReader.Shared/NullableAttributes.cs b/LRReader.Shared/NullableAttributes.cs deleted file mode 100644 index 7cd76f8f..00000000 --- a/LRReader.Shared/NullableAttributes.cs +++ /dev/null @@ -1,142 +0,0 @@ -// https://github.com/dotnet/runtime/blob/527f9ae88a0ee216b44d556f9bdc84037fe0ebda/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/NullableAttributes.cs - -#pragma warning disable - -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Diagnostics.CodeAnalysis -{ -#if NETSTANDARD2_0 || NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 || NET45 || NET451 || NET452 || NET46 || NET461 || NET462 || NET47 || NET471 || NET472 || NET48 || WINDOWS_UWP - /// Specifies that null is allowed as an input even if the corresponding type disallows it. - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] - public - sealed class AllowNullAttribute : Attribute - { } - - /// Specifies that null is disallowed as an input even if the corresponding type allows it. - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] - public - sealed class DisallowNullAttribute : Attribute - { } - - /// Specifies that an output may be null even if the corresponding type disallows it. - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] - public - sealed class MaybeNullAttribute : Attribute - { } - - /// Specifies that when a method returns , the parameter may be null even if the corresponding type disallows it. - [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] - public - sealed class MaybeNullWhenAttribute : Attribute - { - /// Initializes the attribute with the specified return value condition. - /// - /// The return value condition. If the method returns this value, the associated parameter may be null. - /// - public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; - - /// Gets the return value condition. - public bool ReturnValue { get; } - } - - /// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it. - [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] - public - sealed class NotNullWhenAttribute : Attribute - { - /// Initializes the attribute with the specified return value condition. - /// - /// The return value condition. If the method returns this value, the associated parameter will not be null. - /// - public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; - - /// Gets the return value condition. - public bool ReturnValue { get; } - } - - /// Applied to a method that will never return under any circumstance. - [AttributeUsage(AttributeTargets.Method, Inherited = false)] - public - sealed class DoesNotReturnAttribute : Attribute - { } - - /// Specifies that the method will not return if the associated Boolean parameter is passed the specified value. - [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] - public - sealed class DoesNotReturnIfAttribute : Attribute - { - /// Initializes the attribute with the specified parameter value. - /// - /// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to - /// the associated parameter matches this value. - /// - public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue; - - /// Gets the condition parameter value. - public bool ParameterValue { get; } - } -#endif - -#if NETSTANDARD2_0 || NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 || NETCOREAPP3_0 || NETCOREAPP3_1 || NET45 || NET451 || NET452 || NET46 || NET461 || NET462 || NET47 || NET471 || NET472 || NET48 || WINDOWS_UWP - /// Specifies that the method or property will ensure that the listed field and property members have not-null values. - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] - public - sealed class MemberNotNullAttribute : Attribute - { - /// Initializes the attribute with a field or property member. - /// - /// The field or property member that is promised to be not-null. - /// - public MemberNotNullAttribute(string member) => Members = new[] { member }; - - /// Initializes the attribute with the list of field and property members. - /// - /// The list of field and property members that are promised to be not-null. - /// - public MemberNotNullAttribute(params string[] members) => Members = members; - - /// Gets field or property member names. - public string[] Members { get; } - } - - /// Specifies that the method or property will ensure that the listed field and property members have not-null values when returning with the specified return value condition. - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] - public - sealed class MemberNotNullWhenAttribute : Attribute - { - /// Initializes the attribute with the specified return value condition and a field or property member. - /// - /// The return value condition. If the method returns this value, the associated parameter will not be null. - /// - /// - /// The field or property member that is promised to be not-null. - /// - public MemberNotNullWhenAttribute(bool returnValue, string member) - { - ReturnValue = returnValue; - Members = new[] { member }; - } - - /// Initializes the attribute with the specified return value condition and list of field and property members. - /// - /// The return value condition. If the method returns this value, the associated parameter will not be null. - /// - /// - /// The list of field and property members that are promised to be not-null. - /// - public MemberNotNullWhenAttribute(bool returnValue, params string[] members) - { - ReturnValue = returnValue; - Members = members; - } - - /// Gets the return value condition. - public bool ReturnValue { get; } - - /// Gets field or property member names. - public string[] Members { get; } - } -#endif -} diff --git a/LRReader.Shared/Services/Tabs.cs b/LRReader.Shared/Services/Tabs.cs index cb1a5569..26171b20 100644 --- a/LRReader.Shared/Services/Tabs.cs +++ b/LRReader.Shared/Services/Tabs.cs @@ -24,7 +24,7 @@ public partial class TabsService : ObservableObject [ObservableProperty] [NotifyPropertyChangedFor("Windowed")] private bool _fullscreen; - public bool Windowed => !_fullscreen; + public bool Windowed => !Fullscreen; private Dictionary Tabs = new Dictionary(); diff --git a/LRReader.UWP/LRReader.UWP.csproj b/LRReader.UWP/LRReader.UWP.csproj index 61029cad..f1658ec5 100644 --- a/LRReader.UWP/LRReader.UWP.csproj +++ b/LRReader.UWP/LRReader.UWP.csproj @@ -412,7 +412,7 @@ - 8.0.0 + 8.1.0 5.0.1 @@ -445,11 +445,16 @@ 2.8.2-prerelease.220830001 - 1.0.1462.37 + 1.0.1518.46 1.1.5 + + 1.3.1 + runtime; build; native; contentfiles; analyzers; buildtransitive + all + diff --git a/LRReader.UWP/Services/ImageProcessing.cs b/LRReader.UWP/Services/ImageProcessing.cs index dbfabca0..a3e9a2e4 100644 --- a/LRReader.UWP/Services/ImageProcessing.cs +++ b/LRReader.UWP/Services/ImageProcessing.cs @@ -91,10 +91,9 @@ public override async Task GetImageSize(byte[]? bytes) return Size.Empty; var size = await base.GetImageSize(bytes); if (size.IsEmpty) - using (var stream = new InMemoryRandomAccessStream()) + using (var ms = new MemoryStream(bytes)) { - await stream.WriteAsync(bytes.AsBuffer()); - stream.Seek(0); + var stream = ms.AsRandomAccessStream(); try { var decoder = await BitmapDecoder.CreateAsync(stream); From 6c9162a310238f0c81cbed8ba8528bbe249b60f8 Mon Sep 17 00:00:00 2001 From: Guerra24 Date: Sat, 18 Mar 2023 19:06:11 -0600 Subject: [PATCH 02/31] Automatic reader and karen integration --- LRReader.Avalonia/LRReader.Avalonia.csproj | 2 +- LRReader.Shared/LRReader.Shared.csproj | 8 +- LRReader.Shared/Models/Dialogs.cs | 1 + LRReader.Shared/Models/Karen.cs | 18 ++ LRReader.Shared/Models/Main/Profile.cs | 10 +- LRReader.Shared/Services/Api.cs | 3 +- LRReader.Shared/Services/Karen.cs | 36 ++++ LRReader.Shared/Services/Services.cs | 2 + LRReader.Shared/Services/Settings.cs | 188 ++++++++++-------- LRReader.Shared/Services/SettingsStorage.cs | 44 ++-- LRReader.Shared/Services/Updates.cs | 13 +- .../ViewModels/ArchivePageViewModel.cs | 3 + .../ViewModels/LoadingPageViewModel.cs | 35 +++- .../ViewModels/SettingsPageViewModel.cs | 114 ++++++++++- .../LRReader.UWP.Installer.csproj | 2 +- LRReader.UWP.Installer/MainWindow.xaml.cs | 2 +- LRReader.UWP/App.xaml.cs | 8 + LRReader.UWP/Init.cs | 2 + LRReader.UWP/LRReader.UWP.csproj | 5 +- LRReader.UWP/Package.appxmanifest | 5 + LRReader.UWP/Services/Karen.cs | 85 ++++++++ LRReader.UWP/Services/SettingsStorage.cs | 13 +- .../Views/Content/Settings/Reader.xaml | 43 ++++ .../Views/Content/Settings/Server.xaml | 64 ++++++ .../Views/Content/Settings/Server.xaml.cs | 7 +- LRReader.UWP/Views/Controls/ArchiveList.xaml | 2 +- LRReader.UWP/Views/Controls/ModernInput.cs | 2 +- .../Views/Dialogs/ServerProfileDialog.xaml | 4 + .../Views/Dialogs/ServerProfileDialog.xaml.cs | 3 + LRReader.UWP/Views/Main/HostTabPage.xaml.cs | 4 +- LRReader.UWP/Views/Tabs/ArchiveTab.xaml | 11 + .../Tabs/Content/ArchiveTabContent.xaml.cs | 54 +++++ .../Tabs/Content/ArchivesTabContent.xaml | 2 +- LRReader.UWP/Views/Tabs/SettingsTab.xaml.cs | 2 +- 34 files changed, 655 insertions(+), 142 deletions(-) create mode 100644 LRReader.Shared/Models/Karen.cs create mode 100644 LRReader.Shared/Services/Karen.cs create mode 100644 LRReader.UWP/Services/Karen.cs diff --git a/LRReader.Avalonia/LRReader.Avalonia.csproj b/LRReader.Avalonia/LRReader.Avalonia.csproj index cf870c59..3956cadc 100644 --- a/LRReader.Avalonia/LRReader.Avalonia.csproj +++ b/LRReader.Avalonia/LRReader.Avalonia.csproj @@ -10,7 +10,7 @@ - + diff --git a/LRReader.Shared/LRReader.Shared.csproj b/LRReader.Shared/LRReader.Shared.csproj index a369515e..7379741d 100644 --- a/LRReader.Shared/LRReader.Shared.csproj +++ b/LRReader.Shared/LRReader.Shared.csproj @@ -17,16 +17,16 @@ - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + diff --git a/LRReader.Shared/Models/Dialogs.cs b/LRReader.Shared/Models/Dialogs.cs index 01926b2b..1db24287 100644 --- a/LRReader.Shared/Models/Dialogs.cs +++ b/LRReader.Shared/Models/Dialogs.cs @@ -26,6 +26,7 @@ public interface ICreateProfileDialog : IDialog string Name { get; set; } string Address { get; set; } string ApiKey { get; set; } + bool Integration { get; set; } } public enum ConflictMode diff --git a/LRReader.Shared/Models/Karen.cs b/LRReader.Shared/Models/Karen.cs new file mode 100644 index 00000000..15233a76 --- /dev/null +++ b/LRReader.Shared/Models/Karen.cs @@ -0,0 +1,18 @@ +namespace LRReader.Shared.Models +{ + public enum PacketType + { + None, InstanceStart, InstanceStop, InstanceStatus, InstanceSetting, InstanceRepair + } + + public enum SettingOperation + { + None, Load, Save + } + public enum SettingType + { + None, ContentFolder, ThumbnailFolder, StartServerAutomatically, StartWithWindows, NetworkPort, + ForceDebugMode, UseWSL2 + } + +} diff --git a/LRReader.Shared/Models/Main/Profile.cs b/LRReader.Shared/Models/Main/Profile.cs index 7f9f56c0..be452047 100644 --- a/LRReader.Shared/Models/Main/Profile.cs +++ b/LRReader.Shared/Models/Main/Profile.cs @@ -17,6 +17,7 @@ public class ServerProfile : ObservableObject public bool AcceptedDisclaimer { get; set; } public List MarkedAsNonDuplicated { get; set; } public int CacheTimestamp { get; set; } + public bool Integration { get; set; } [JsonIgnore] public bool HasApiKey @@ -24,15 +25,22 @@ public bool HasApiKey get => !string.IsNullOrEmpty(ServerApiKey) || !Service.Api.ServerInfo.has_password; } + [JsonIgnore] + public bool IsLocalHost + { + get => ServerAddress.Contains("127.0.0.") || ServerAddress.Contains("localhost"); + } + [JsonIgnore] public string ServerAddressBrowser => ServerAddress.TrimEnd('/'); - public ServerProfile(string name, string address, string key) + public ServerProfile(string name, string address, string key, bool integration) { Name = name; ServerAddress = address; ServerApiKey = key; + Integration = integration; UID = Guid.NewGuid().ToString(); Bookmarks = new List(); MarkedAsNonDuplicated = new List(); diff --git a/LRReader.Shared/Services/Api.cs b/LRReader.Shared/Services/Api.cs index 6c67a701..a25492bb 100644 --- a/LRReader.Shared/Services/Api.cs +++ b/LRReader.Shared/Services/Api.cs @@ -32,8 +32,7 @@ public bool RefreshSettings(ServerProfile profile) if (!Uri.IsWellFormedUriString(profile.ServerAddress, UriKind.Absolute)) return false; var options = new RestClientOptions(profile.ServerAddress) { UserAgent = "LRReader" }; - Client = new RestClient(options); - Client.UseNewtonsoftJson(); + Client = new RestClient(options, configureSerialization: s => s.UseNewtonsoftJson()); if (!string.IsNullOrEmpty(profile.ServerApiKey)) { var base64Key = Convert.ToBase64String(Encoding.UTF8.GetBytes(profile.ServerApiKey)); diff --git a/LRReader.Shared/Services/Karen.cs b/LRReader.Shared/Services/Karen.cs new file mode 100644 index 00000000..9e9b29f6 --- /dev/null +++ b/LRReader.Shared/Services/Karen.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using LRReader.Shared.Models; + +namespace LRReader.Shared.Services +{ + public interface IKarenService + { + public void Connect(object instance); + + public Task?> SendMessage(IDictionary data); + + public Task LoadSetting(SettingType type); + + public Task SaveSetting(SettingType type, object value); + + public void Disconnect(); + + public bool IsConnected { get; } + } + + public class StubKarenService : IKarenService + { + public void Connect(object instance) { } + + public Task?> SendMessage(IDictionary data) => Task.FromResult?>(null); + + public Task LoadSetting(SettingType type) => Task.FromResult(default); + + public Task SaveSetting(SettingType type, object value) => Task.CompletedTask; + + public void Disconnect() { } + + public bool IsConnected => false; + } +} diff --git a/LRReader.Shared/Services/Services.cs b/LRReader.Shared/Services/Services.cs index 83928281..00da9163 100644 --- a/LRReader.Shared/Services/Services.cs +++ b/LRReader.Shared/Services/Services.cs @@ -36,6 +36,7 @@ public static void BuildServices(ConfigureServices? services = null) collection.AddSingleton(); collection.AddSingleton(); collection.AddSingleton(); + collection.AddSingleton(); // Tools collection.AddSingleton(); @@ -91,6 +92,7 @@ public static async Task InitServices() public static EventsService Events => Services.GetRequiredService(); public static ApiService Api => Services.GetRequiredService(); public static TabsService Tabs => Services.GetRequiredService(); + public static IKarenService Karen => Services.GetRequiredService(); public static ILogger Logger() => Services.GetRequiredService>(); } diff --git a/LRReader.Shared/Services/Settings.cs b/LRReader.Shared/Services/Settings.cs index 4ade788b..10fb5e22 100644 --- a/LRReader.Shared/Services/Settings.cs +++ b/LRReader.Shared/Services/Settings.cs @@ -31,7 +31,7 @@ public ServerProfile Profile set { if (value != null) - SettingsStorage.StoreObjectLocal("ProfileUID", value.UID); + SettingsStorage.StoreObjectLocal(value.UID); SetProperty(ref _profile, value); } } @@ -45,212 +45,241 @@ public bool AtLeastOneProfile } public int DefaultZoom { - get => SettingsStorage.GetObjectLocal("DefaultZoom", 100); + get => SettingsStorage.GetObjectLocal(100); set { - SettingsStorage.StoreObjectLocal("DefaultZoom", value); - OnPropertyChanged("DefaultZoom"); + SettingsStorage.StoreObjectLocal(value); + OnPropertyChanged(); } } public bool ReadRTL { - get => SettingsStorage.GetObjectLocal("ReadRTL", false); + get => SettingsStorage.GetObjectLocal(false); set { - SettingsStorage.StoreObjectLocal("ReadRTL", value); + SettingsStorage.StoreObjectLocal(value); Service.Events.RebuildReaderImagesSet(); } } public bool TwoPages { - get => SettingsStorage.GetObjectLocal("TwoPages", false); + get => SettingsStorage.GetObjectLocal(false); set { - SettingsStorage.StoreObjectLocal("TwoPages", value); - OnPropertyChanged("TwoPages"); + SettingsStorage.StoreObjectLocal(value); + OnPropertyChanged(); Service.Events.RebuildReaderImagesSet(); } } public bool BookmarkReminder { - get => SettingsStorage.GetObjectRoamed("BookmarkReminder", true); + get => SettingsStorage.GetObjectRoamed(true); set { - SettingsStorage.StoreObjectRoamed("BookmarkReminder", value); - OnPropertyChanged("BookmarkReminder"); + SettingsStorage.StoreObjectRoamed(value); + OnPropertyChanged(); } } public BookmarkReminderMode BookmarkReminderMode { - get => (BookmarkReminderMode)SettingsStorage.GetObjectRoamed("BookmarkReminderMode", (int)BookmarkReminderMode.New); - set => SettingsStorage.StoreObjectRoamed("BookmarkReminderMode", (int)value); + get => (BookmarkReminderMode)SettingsStorage.GetObjectRoamed((int)BookmarkReminderMode.New); + set => SettingsStorage.StoreObjectRoamed((int)value); } public bool RemoveBookmark { - get => SettingsStorage.GetObjectRoamed("RemoveBookmark", true); - set => SettingsStorage.StoreObjectRoamed("RemoveBookmark", value); + get => SettingsStorage.GetObjectRoamed(true); + set => SettingsStorage.StoreObjectRoamed(value); } public bool OpenBookmarksTab { - get => SettingsStorage.GetObjectRoamed("OpenBookmarksTab", false); - set => SettingsStorage.StoreObjectRoamed("OpenBookmarksTab", value); + get => SettingsStorage.GetObjectRoamed(false); + set => SettingsStorage.StoreObjectRoamed(value); } public bool OpenBookmarksStart { - get => SettingsStorage.GetObjectRoamed("OpenBookmarksStart", false); - set => SettingsStorage.StoreObjectRoamed("OpenBookmarksStart", value); + get => SettingsStorage.GetObjectRoamed(false); + set => SettingsStorage.StoreObjectRoamed(value); } public bool OpenReader { - get => SettingsStorage.GetObjectRoamed("OpenReader", false); - set => SettingsStorage.StoreObjectRoamed("OpenReader", value); + get => SettingsStorage.GetObjectRoamed(false); + set => SettingsStorage.StoreObjectRoamed(value); } public int KeyboardScroll { - get => SettingsStorage.GetObjectLocal("KeyboardScroll", 200); - set => SettingsStorage.StoreObjectLocal("KeyboardScroll", value); + get => SettingsStorage.GetObjectLocal(200); + set => SettingsStorage.StoreObjectLocal(value); } public bool FitToWidth { - get => SettingsStorage.GetObjectRoamed("FitToWidth", false); + get => SettingsStorage.GetObjectRoamed(false); set { - SettingsStorage.StoreObjectRoamed("FitToWidth", value); - OnPropertyChanged("FitToWidth"); + SettingsStorage.StoreObjectRoamed(value); + OnPropertyChanged(); } } public int FitScaleLimit { - get => SettingsStorage.GetObjectLocal("FitScaleLimit", 100); + get => SettingsStorage.GetObjectLocal(100); set { - SettingsStorage.StoreObjectLocal("FitScaleLimit", value); - OnPropertyChanged("FitScaleLimit"); + SettingsStorage.StoreObjectLocal(value); + OnPropertyChanged(); } } public AppTheme Theme { - get => (AppTheme)SettingsStorage.GetObjectLocal("Theme", (int)AppTheme.System); + get => (AppTheme)SettingsStorage.GetObjectLocal((int)AppTheme.System); set { - SettingsStorage.StoreObjectLocal("Theme", (int)value); + SettingsStorage.StoreObjectLocal((int)value); Platform.ChangeTheme(value); } } public bool CompactBookmarks { - get => SettingsStorage.GetObjectRoamed("CompactBookmarks", true); - set => SettingsStorage.StoreObjectRoamed("CompactBookmarks", value); + get => SettingsStorage.GetObjectRoamed(true); + set => SettingsStorage.StoreObjectRoamed(value); } public bool OpenCategoriesTab { - get => SettingsStorage.GetObjectRoamed("OpenCategoriesTab", false); - set => SettingsStorage.StoreObjectRoamed("OpenCategoriesTab", value); + get => SettingsStorage.GetObjectRoamed(false); + set => SettingsStorage.StoreObjectRoamed(value); } public string SortByDefault { - get => SettingsStorage.GetObjectRoamed("SortByDefault", "title"); - set => SettingsStorage.StoreObjectRoamed("SortByDefault", value); + get => SettingsStorage.GetObjectRoamed("title"); + set => SettingsStorage.StoreObjectRoamed(value); } public Order OrderByDefault { - get => (Order)SettingsStorage.GetObjectRoamed("OrderByDefault", (int)Order.Ascending); - set => SettingsStorage.StoreObjectRoamed("OrderByDefault", (int)value); + get => (Order)SettingsStorage.GetObjectRoamed((int)Order.Ascending); + set => SettingsStorage.StoreObjectRoamed((int)value); } public bool ReaderImageSetBuilder { - get => SettingsStorage.GetObjectLocal("ReaderImageSetBuilder", true); + get => SettingsStorage.GetObjectLocal(true); set { - SettingsStorage.StoreObjectLocal("ReaderImageSetBuilder", value); + SettingsStorage.StoreObjectLocal(value); Service.Events.RebuildReaderImagesSet(); } } public bool UseVisualTags { - get => SettingsStorage.GetObjectRoamed("UseVisualTags", true); - set => SettingsStorage.StoreObjectRoamed("UseVisualTags", value); + get => SettingsStorage.GetObjectRoamed(true); + set => SettingsStorage.StoreObjectRoamed(value); } public bool ScrollToChangePage { - get => SettingsStorage.GetObjectRoamed("ScrollToChangePage", false); - set => SettingsStorage.StoreObjectRoamed("ScrollToChangePage", value); + get => SettingsStorage.GetObjectRoamed(false); + set => SettingsStorage.StoreObjectRoamed(value); } public bool UseReaderBackground { - get => SettingsStorage.GetObjectLocal("UseReaderBackground", false); + get => SettingsStorage.GetObjectLocal(false); set { - SettingsStorage.StoreObjectLocal("UseReaderBackground", value); - OnPropertyChanged("UseReaderBackground"); + SettingsStorage.StoreObjectLocal(value); + OnPropertyChanged(); } } public string ReaderBackground { - get => SettingsStorage.GetObjectLocal("ReaderBackground", "#FF000000"); - set => SettingsStorage.StoreObjectLocal("ReaderBackground", value); + get => SettingsStorage.GetObjectLocal("#FF000000"); + set => SettingsStorage.StoreObjectLocal(value); } public bool AutoUpdate { - get => SettingsStorage.GetObjectLocal("AutoUpdate", false); - set => SettingsStorage.StoreObjectLocal("AutoUpdate", value); + get => SettingsStorage.GetObjectLocal(false); + set => SettingsStorage.StoreObjectLocal(value); } public bool OpenNextArchive { - get => SettingsStorage.GetObjectRoamed("OpenNextArchive", true); - set => SettingsStorage.StoreObjectRoamed("OpenNextArchive", value); + get => SettingsStorage.GetObjectRoamed(false); + set => SettingsStorage.StoreObjectRoamed(value); } public bool AutoLogin { - get => SettingsStorage.GetObjectRoamed("AutoLogin", true); - set => SettingsStorage.StoreObjectRoamed("AutoLogin", value); + get => SettingsStorage.GetObjectRoamed(true); + set => SettingsStorage.StoreObjectRoamed(value); } public bool CrashReporting { - get => SettingsStorage.GetObjectLocal("CrashReporting", true); - set => SettingsStorage.StoreObjectLocal("CrashReporting", value); + get => SettingsStorage.GetObjectLocal(true); + set => SettingsStorage.StoreObjectLocal(value); } public TagsPopupLocation TagsPopup { - get => (TagsPopupLocation)SettingsStorage.GetObjectRoamed("TagsPopup", (int)TagsPopupLocation.Middle); - set => SettingsStorage.StoreObjectRoamed("TagsPopup", (int)value); + get => (TagsPopupLocation)SettingsStorage.GetObjectRoamed((int)TagsPopupLocation.Middle); + set => SettingsStorage.StoreObjectRoamed((int)value); } public bool KeepPageDetailsOpen { - get => SettingsStorage.GetObjectRoamed("KeepPageDetailsOpen", false); - set => SettingsStorage.StoreObjectRoamed("KeepPageDetailsOpen", value); + get => SettingsStorage.GetObjectRoamed(false); + set => SettingsStorage.StoreObjectRoamed(value); } public bool ShowExtraPageDetails { - get => SettingsStorage.GetObjectRoamed("ShowExtraPageDetails", false); - set => SettingsStorage.StoreObjectRoamed("ShowExtraPageDetails", value); + get => SettingsStorage.GetObjectRoamed(false); + set => SettingsStorage.StoreObjectRoamed(value); } public bool UseVerticalReader { - get => SettingsStorage.GetObjectRoamed("UseVerticalReader", false); + get => SettingsStorage.GetObjectRoamed(false); set { - SettingsStorage.StoreObjectRoamed("UseVerticalReader", value); - OnPropertyChanged("UseVerticalReader"); + SettingsStorage.StoreObjectRoamed(value); + OnPropertyChanged(); } } public bool UseVerticalTabs { - get => SettingsStorage.GetObjectLocal("UseVerticalTabs", false); - set => SettingsStorage.StoreObjectLocal("UseVerticalTabs", value); + get => SettingsStorage.GetObjectLocal(false); + set => SettingsStorage.StoreObjectLocal(value); + } + public bool Autoplay + { + get => SettingsStorage.GetObjectLocal(false); + set => SettingsStorage.StoreObjectLocal(value); + } + public int AutoplayStartDelay + { + get => SettingsStorage.GetObjectRoamed(2000); + set => SettingsStorage.StoreObjectRoamed(value); + } + public int AutoplayBeforeChangeDelay + { + get => SettingsStorage.GetObjectRoamed(2000); + set => SettingsStorage.StoreObjectRoamed(value); + } + public int AutoplayAfterChangeDelay + { + get => SettingsStorage.GetObjectRoamed(2000); + set => SettingsStorage.StoreObjectRoamed(value); + } + public int AutoplaySpeed + { + get => SettingsStorage.GetObjectLocal(100); + set + { + SettingsStorage.StoreObjectLocal(value); + OnPropertyChanged(); + } } public static readonly int CurrentLocalVersion = 4; public int SettingsVersionLocal { - get => SettingsStorage.GetObjectLocal("SettingsVersion", CurrentLocalVersion); - set => SettingsStorage.StoreObjectLocal("SettingsVersion", value); + get => SettingsStorage.GetObjectLocal(CurrentLocalVersion); + set => SettingsStorage.StoreObjectLocal(value); } public static readonly int CurrentRoamedVersion = 2; public int SettingsVersionRoamed { - get => SettingsStorage.GetObjectRoamed("SettingsVersion", CurrentRoamedVersion); - set => SettingsStorage.StoreObjectRoamed("SettingsVersion", value); + get => SettingsStorage.GetObjectRoamed(CurrentRoamedVersion); + set => SettingsStorage.StoreObjectRoamed(value); } private Throttle save; @@ -271,7 +300,7 @@ public SettingsService(ISettingsStorageService settingsStorage, IFilesService fi catch (Exception e) { #if WINDOWS_UWP - Crashes.TrackError(e); + Crashes.TrackError(e); #endif } }); @@ -324,7 +353,7 @@ private void UpgradeSettings() switch (localVersion) { case 0: - KeyboardScroll = SettingsStorage.GetObjectLocal("SpacebarScroll", 200); + KeyboardScroll = SettingsStorage.GetObjectLocal(200, "SpacebarScroll"); SettingsStorage.DeleteObjectLocal("SpacebarScroll"); break; case 1: @@ -383,19 +412,20 @@ private void ProfilesChanges(object sender, NotifyCollectionChangedEventArgs e) OnPropertyChanged("AtLeastOneProfile"); } - public ServerProfile AddProfile(string name, string address, string apikey) + public ServerProfile AddProfile(string name, string address, string apikey, bool integration) { - ServerProfile profile = new ServerProfile(name, address, apikey); + ServerProfile profile = new ServerProfile(name, address, apikey, integration); Profiles.Add(profile); return profile; } - public void ModifyProfile(string uid, string name, string address, string apikey) + public void ModifyProfile(string uid, string name, string address, string apikey, bool integration) { var profile = Profiles.FirstOrDefault(p => p.UID.Equals(uid)); profile.Name = name; profile.ServerAddress = address; profile.ServerApiKey = apikey; + profile.Integration = integration; profile.Update(); SaveProfiles(); } diff --git a/LRReader.Shared/Services/SettingsStorage.cs b/LRReader.Shared/Services/SettingsStorage.cs index 566afb51..5182e460 100644 --- a/LRReader.Shared/Services/SettingsStorage.cs +++ b/LRReader.Shared/Services/SettingsStorage.cs @@ -1,4 +1,5 @@ using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; using System.Threading.Tasks; namespace LRReader.Shared.Services @@ -7,53 +8,54 @@ namespace LRReader.Shared.Services //[CallerMemberName] public interface ISettingsStorageService : IService { - void StoreObjectLocal(string key, object obj); + T? GetObjectLocal([CallerMemberName] string? key = null); - void StoreObjectRoamed(string key, object obj); + T? GetObjectRoamed([CallerMemberName] string? key = null); - T? GetObjectLocal(string key); + void DeleteObjectLocal(string key); - [return: NotNullIfNotNull("def")] - T? GetObjectLocal(string key, T? def); + void DeleteObjectRoamed(string key); - T? GetObjectRoamed(string key); + void StoreObjectLocal(object obj, [CallerMemberName] string? key = null); + + void StoreObjectRoamed(object obj, [CallerMemberName] string? key = null); [return: NotNullIfNotNull("def")] - T? GetObjectRoamed(string key, T? def); + T? GetObjectLocal(T? def, [CallerMemberName] string? key = null); - void DeleteObjectLocal(string key); + [return: NotNullIfNotNull("def")] + T? GetObjectRoamed(T? def, [CallerMemberName] string? key = null); - void DeleteObjectRoamed(string key); } public class StubSettingsStorageService : ISettingsStorageService { public Task Init() => Task.Delay(1); - public T? GetObjectLocal(string key) => GetObjectLocal(key, default); + public T? GetObjectLocal([CallerMemberName] string? key = null) => GetObjectLocal(default, key); - [return: NotNullIfNotNull("def")] - public T? GetObjectLocal(string key, T? def) => def; + public T? GetObjectRoamed([CallerMemberName] string? key = null) => GetObjectRoamed(default, key); - public T? GetObjectRoamed(string key) => GetObjectRoamed(key, default); - - [return: NotNullIfNotNull("def")] - public T? GetObjectRoamed(string key, T? def) => def; - - public void StoreObjectLocal(string key, object obj) + public void DeleteObjectLocal(string key) { } - public void StoreObjectRoamed(string key, object obj) + public void DeleteObjectRoamed(string key) { } - public void DeleteObjectLocal(string key) + public void StoreObjectLocal(object obj, [CallerMemberName] string? key = null) { } - public void DeleteObjectRoamed(string key) + public void StoreObjectRoamed(object obj, [CallerMemberName] string? key = null) { } + + [return: NotNullIfNotNull("def")] + public T? GetObjectLocal(T? def, [CallerMemberName] string? key = null) => def; + + [return: NotNullIfNotNull("def")] + public T? GetObjectRoamed(T? def, [CallerMemberName] string? key = null) => def; } } diff --git a/LRReader.Shared/Services/Updates.cs b/LRReader.Shared/Services/Updates.cs index 26616ef2..60ffcf46 100644 --- a/LRReader.Shared/Services/Updates.cs +++ b/LRReader.Shared/Services/Updates.cs @@ -14,7 +14,7 @@ public abstract class UpdatesService protected readonly SettingsService Settings; public Version MIN_VERSION = new Version(0, 8, 4); - public Version MAX_VERSION = new Version(0, 8, 81); + public Version MAX_VERSION = new Version(0, 8, 90); protected readonly RestClient client; @@ -29,8 +29,7 @@ public UpdatesService(PlatformService platform, ISettingsStorageService settings var uri = new Uri("https://api.guerra24.net/"); #endif var options = new RestClientOptions(uri) { UserAgent = "LRReader" }; - client = new RestClient(options); - client.UseNewtonsoftJson(); + client = new RestClient(options, configureSerialization: s => s.UseNewtonsoftJson()); } public abstract Task CheckForUpdates(); @@ -75,8 +74,8 @@ public async Task UpdateSupportedRange() var range = result.Data; MIN_VERSION = range.minSupported; MAX_VERSION = range.maxSupported; - SettingsStorage.StoreObjectLocal("MinVersion", MIN_VERSION.ToString()); - SettingsStorage.StoreObjectLocal("MaxVersion", MAX_VERSION.ToString()); + SettingsStorage.StoreObjectLocal(MIN_VERSION.ToString(), "MinVersion"); + SettingsStorage.StoreObjectLocal(MAX_VERSION.ToString(), "MaxVersion"); } else { @@ -86,8 +85,8 @@ public async Task UpdateSupportedRange() private void ReadVersion() { - MIN_VERSION = Version.Parse(SettingsStorage.GetObjectLocal("MinVersion", MIN_VERSION.ToString())); - MAX_VERSION = Version.Parse(SettingsStorage.GetObjectLocal("MaxVersion", MAX_VERSION.ToString())); + MIN_VERSION = Version.Parse(SettingsStorage.GetObjectLocal(MIN_VERSION.ToString(), "MinVersion")); + MAX_VERSION = Version.Parse(SettingsStorage.GetObjectLocal(MAX_VERSION.ToString(), "MaxVersion")); } } diff --git a/LRReader.Shared/ViewModels/ArchivePageViewModel.cs b/LRReader.Shared/ViewModels/ArchivePageViewModel.cs index 0b91ff60..7b242eb7 100644 --- a/LRReader.Shared/ViewModels/ArchivePageViewModel.cs +++ b/LRReader.Shared/ViewModels/ArchivePageViewModel.cs @@ -173,6 +173,9 @@ public bool UseVerticalReader } } + [ObservableProperty] + private bool _useAutoplay; + public event Action? RebuildReader; [ObservableProperty] diff --git a/LRReader.Shared/ViewModels/LoadingPageViewModel.cs b/LRReader.Shared/ViewModels/LoadingPageViewModel.cs index 1180baea..4f9cbe61 100644 --- a/LRReader.Shared/ViewModels/LoadingPageViewModel.cs +++ b/LRReader.Shared/ViewModels/LoadingPageViewModel.cs @@ -1,8 +1,10 @@ using System; +using System.Collections.Generic; using System.Threading.Tasks; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using LRReader.Shared.Extensions; +using LRReader.Shared.Models; using LRReader.Shared.Providers; using LRReader.Shared.Services; @@ -16,6 +18,7 @@ public partial class LoadingPageViewModel : ObservableObject private readonly ArchivesService Archives; private readonly UpdatesService Updates; private readonly ISettingsStorageService SettingsStorage; + private readonly IKarenService Karen; [ObservableProperty] private string _status = ""; @@ -32,7 +35,7 @@ public partial class LoadingPageViewModel : ObservableObject [ObservableProperty] private bool _animate; - public LoadingPageViewModel(SettingsService settings, PlatformService platform, ApiService api, ArchivesService archives, UpdatesService updates, ISettingsStorageService settingsStorage) + public LoadingPageViewModel(SettingsService settings, PlatformService platform, ApiService api, ArchivesService archives, UpdatesService updates, ISettingsStorageService settingsStorage, IKarenService karen) { Settings = settings; Platform = platform; @@ -40,6 +43,7 @@ public LoadingPageViewModel(SettingsService settings, PlatformService platform, Archives = archives; Updates = updates; SettingsStorage = settingsStorage; + Karen = karen; } private async Task Reload(double time = 5) @@ -63,7 +67,7 @@ public async Task Startup() { Updating = true; // Set wasUpdate in settingstorage - SettingsStorage.StoreObjectLocal("WasUpdated", true); + SettingsStorage.StoreObjectLocal(true, "WasUpdated"); var result = await Updates.DownloadAndInstall(new Progress(progress => Progress = progress), update); Updating = false; if (!result.Result) @@ -99,10 +103,13 @@ public async Task Startup() [RelayCommand] private async Task Connect() { + if (!Settings.Profile.IsLocalHost || !Settings.Profile.Integration) + Karen.Disconnect(); Status = ""; StatusSub = ""; Retry = false; Active = true; + int retires = 0; if (!Api.RefreshSettings(Settings.Profile)) { Status = Platform.GetLocalizedString("Pages/LoadingPage/InvalidAddress"); @@ -110,14 +117,30 @@ private async Task Connect() await Reload(); return; } + Retry: var serverInfo = await ServerProvider.GetServerInfo(); if (serverInfo == null) { - var address = Settings.Profile.ServerAddress; - if (address.Contains("127.0.0.") || address.Contains("localhost")) + if (Settings.Profile.IsLocalHost) { - Status = Platform.GetLocalizedString("Pages/LoadingPage/NoConnectionLocalHost"); - StatusSub = Platform.GetLocalizedString("Pages/LoadingPage/NoConnectionLocalHostSub"); + if (Karen.IsConnected && retires < 3) + { + Active = false; + Status = "Starting Server... Please wait"; + var data = new Dictionary(); + data["PacketType"] = (int)PacketType.InstanceStart; + await Karen.SendMessage(data); + await Task.Delay(TimeSpan.FromSeconds(2.5)); + Active = true; + Status = ""; + retires++; + goto Retry; + } + else + { + Status = Platform.GetLocalizedString("Pages/LoadingPage/NoConnectionLocalHost"); + StatusSub = Platform.GetLocalizedString("Pages/LoadingPage/NoConnectionLocalHostSub"); + } } else Status = Platform.GetLocalizedString("Pages/LoadingPage/NoConnection"); diff --git a/LRReader.Shared/ViewModels/SettingsPageViewModel.cs b/LRReader.Shared/ViewModels/SettingsPageViewModel.cs index 5e134826..b861f7b2 100644 --- a/LRReader.Shared/ViewModels/SettingsPageViewModel.cs +++ b/LRReader.Shared/ViewModels/SettingsPageViewModel.cs @@ -1,6 +1,8 @@ using System; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; +using System.Runtime.CompilerServices; using System.Threading.Tasks; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; @@ -21,6 +23,7 @@ public partial class SettingsPageViewModel : ObservableObject private readonly UpdatesService Updates; private readonly ApiService Api; private readonly TabsService Tabs; + private readonly IKarenService Karen; public readonly string LRReader = "LRReader"; @@ -49,6 +52,85 @@ public partial class SettingsPageViewModel : ObservableObject [ObservableProperty] private string? _updateError; + private string? _contentFolder; + public string? ContentFolder + { + get => _contentFolder; + set + { + if (value != null && SetProperty(ref _contentFolder, value) && SettingsManager.Profile.Integration) + SaveSetting(value); + } + } + + private string? _thumbnailFolder; + public string? ThumbnailFolder + { + get => _thumbnailFolder; + set + { + if (value != null && SetProperty(ref _thumbnailFolder, value) && SettingsManager.Profile.Integration) + SaveSetting(value); + } + } + + private bool _startServerAutomatically; + public bool StartServerAutomatically + { + get => _startServerAutomatically; + set + { + if (SetProperty(ref _startServerAutomatically, value) && SettingsManager.Profile.Integration) + SaveSetting(value); + } + } + + private bool _startWithWindows; + public bool StartWithWindows + { + get => _startWithWindows; + set + { + if (SetProperty(ref _startWithWindows, value) && SettingsManager.Profile.Integration) + SaveSetting(value); + } + } + + public int _networkPort; + public int NetworkPort + { + get => _networkPort; + set + { + if (SetProperty(ref _networkPort, value) && SettingsManager.Profile.Integration) + SaveSetting(value.ToString()); + } + } + + public bool _forceDebugMode; + public bool ForceDebugMode + { + get => _forceDebugMode; + set + { + if (SetProperty(ref _forceDebugMode, value) && SettingsManager.Profile.Integration) + SaveSetting(value); + } + } + + public bool _useWSL2; + public bool UseWSL2 + { + get => _useWSL2; + set + { + if (SetProperty(ref _useWSL2, value) && SettingsManager.Profile.Integration) + SaveSetting(value); + } + } + [ObservableProperty] + private bool _karenStatus; + public ObservableCollection SortBy = new ObservableCollection(); private int _sortByIndex = -1; public int SortByIndex @@ -76,7 +158,7 @@ public int SortByIndex public bool AvifMissing; public bool HeifMissing; - public SettingsPageViewModel(SettingsService settings, ImagesService images, ArchivesService archives, PlatformService platform, UpdatesService updates, ApiService api, TabsService tabs) + public SettingsPageViewModel(SettingsService settings, ImagesService images, ArchivesService archives, PlatformService platform, UpdatesService updates, ApiService api, TabsService tabs, IKarenService karen) { SettingsManager = settings; Images = images; @@ -84,6 +166,7 @@ public SettingsPageViewModel(SettingsService settings, ImagesService images, Arc Updates = updates; Api = api; Tabs = tabs; + Karen = karen; foreach (var n in archives.Namespaces) SortBy.Add(n); @@ -94,7 +177,6 @@ public async Task CheckForPackages() { SetProperty(ref AvifMissing, !await Platform.CheckAppInstalled("Microsoft.AV1VideoExtension_8wekyb3d8bbwe"), nameof(AvifMissing)); SetProperty(ref HeifMissing, !await Platform.CheckAppInstalled("Microsoft.HEIFImageExtension_8wekyb3d8bbwe"), nameof(HeifMissing)); - await Task.CompletedTask; } @@ -158,9 +240,22 @@ public async Task InstallUpdate() } } + private async void SaveSetting(object value, [CallerMemberName] string? propertyName = null) => await Karen.SaveSetting((SettingType)Enum.Parse(typeof(SettingType), propertyName), value); + public async Task UpdateServerInfo() { ServerInfo = await ServerProvider.GetServerInfo(); + if (SettingsManager.Profile.Integration) + { + ContentFolder = await Karen.LoadSetting(SettingType.ContentFolder); + ThumbnailFolder = await Karen.LoadSetting(SettingType.ThumbnailFolder); + StartServerAutomatically = await Karen.LoadSetting(SettingType.StartServerAutomatically); + StartWithWindows = await Karen.LoadSetting(SettingType.StartWithWindows); + NetworkPort = int.Parse(await Karen.LoadSetting(SettingType.NetworkPort) ?? "0"); + ForceDebugMode = await Karen.LoadSetting(SettingType.ForceDebugMode); + UseWSL2 = await Karen.LoadSetting(SettingType.UseWSL2); + KarenStatus = Karen.IsConnected; + } } public async Task CheckThumbnailJob() @@ -200,7 +295,7 @@ private async Task AddProfile() var address = dialog.Address; if (!(address.StartsWith("http://") || address.StartsWith("https://"))) address = "http://" + address; - SettingsManager.AddProfile(dialog.Name, address, dialog.ApiKey); + SettingsManager.AddProfile(dialog.Name, address, dialog.ApiKey, dialog.Integration); } } @@ -211,13 +306,14 @@ private async Task EditProfile(ServerProfile profile) dialog.Name = profile.Name; dialog.Address = profile.ServerAddress; dialog.ApiKey = profile.ServerApiKey; + dialog.Integration = profile.Integration; var result = await dialog.ShowAsync(); if (result == IDialogResult.Primary) { var address = dialog.Address; if (!(address.StartsWith("http://") || address.StartsWith("https://"))) address = "http://" + address; - SettingsManager.ModifyProfile(profile.UID, dialog.Name, address, dialog.ApiKey); + SettingsManager.ModifyProfile(profile.UID, dialog.Name, address, dialog.ApiKey, dialog.Integration); Api.RefreshSettings(profile); } } @@ -291,5 +387,15 @@ private async Task ClearThumbnailCache() [RelayCommand] private Task OpenLink(string url) => Platform.OpenInBrowser(new Uri(url)); + [RelayCommand] + private async Task Repair() + { + var res = await Karen.SendMessage(new Dictionary { { "PacketType", (int)PacketType.InstanceRepair } }); + if (res != null) + { + Tabs.CloseAllTabs(); + Platform.GoToPage(Pages.FirstRun, PagesTransition.DrillIn); + } + } } } diff --git a/LRReader.UWP.Installer/LRReader.UWP.Installer.csproj b/LRReader.UWP.Installer/LRReader.UWP.Installer.csproj index f2b32e56..9352c1be 100644 --- a/LRReader.UWP.Installer/LRReader.UWP.Installer.csproj +++ b/LRReader.UWP.Installer/LRReader.UWP.Installer.csproj @@ -144,7 +144,7 @@ 10.0.22621.755 - 2.0.3 + 3.0.0-preview.1 diff --git a/LRReader.UWP.Installer/MainWindow.xaml.cs b/LRReader.UWP.Installer/MainWindow.xaml.cs index 32a48fff..ce30bbe9 100644 --- a/LRReader.UWP.Installer/MainWindow.xaml.cs +++ b/LRReader.UWP.Installer/MainWindow.xaml.cs @@ -196,7 +196,7 @@ private async void Uninstall_Click(object sender, RoutedEventArgs e) private async Task LaunchAdmin(string command) { - var process = new Process(); + using var process = new Process(); process.StartInfo.Verb = "runas"; process.StartInfo.FileName = Assembly.GetExecutingAssembly().Location; process.StartInfo.Arguments = command; diff --git a/LRReader.UWP/App.xaml.cs b/LRReader.UWP/App.xaml.cs index 0fba3ace..e885cb60 100644 --- a/LRReader.UWP/App.xaml.cs +++ b/LRReader.UWP/App.xaml.cs @@ -6,6 +6,7 @@ using Microsoft.AppCenter.Crashes; using Windows.ApplicationModel; using Windows.ApplicationModel.Activation; +using Windows.ApplicationModel.AppService; using Windows.ApplicationModel.Core; using Windows.UI; using Windows.UI.ViewManagement; @@ -13,6 +14,7 @@ using Windows.UI.Xaml.Media; using static LRReader.Shared.Services.Service; using ColorHelper = Microsoft.Toolkit.Uwp.Helpers.ColorHelper; +using UnhandledExceptionEventArgs = Windows.UI.Xaml.UnhandledExceptionEventArgs; namespace LRReader.UWP { @@ -72,6 +74,12 @@ protected override void OnLaunched(LaunchActivatedEventArgs e) Window.Current.Activate(); } + protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args) + { + if (args.TaskInstance.TriggerDetails is AppServiceTriggerDetails) + Service.Karen.Connect(args.TaskInstance); + } + /// /// Invoked when application execution is being suspended. Application state is saved /// without knowing whether the application will be terminated or resumed with the contents diff --git a/LRReader.UWP/Init.cs b/LRReader.UWP/Init.cs index 39cc489f..90ac6ec9 100644 --- a/LRReader.UWP/Init.cs +++ b/LRReader.UWP/Init.cs @@ -10,6 +10,7 @@ namespace LRReader.UWP { public static class Init { + public static void EarlyInit() { Service.BuildServices((ServiceCollection collection) => @@ -18,6 +19,7 @@ public static void EarlyInit() collection.Replace(ServiceDescriptor.Singleton()); collection.Replace(ServiceDescriptor.Singleton()); collection.Replace(ServiceDescriptor.Singleton()); + collection.Replace(ServiceDescriptor.Singleton()); #if !DEBUG #if SIDELOAD collection.Replace(ServiceDescriptor.Singleton()); diff --git a/LRReader.UWP/LRReader.UWP.csproj b/LRReader.UWP/LRReader.UWP.csproj index f1658ec5..b0e21c68 100644 --- a/LRReader.UWP/LRReader.UWP.csproj +++ b/LRReader.UWP/LRReader.UWP.csproj @@ -149,6 +149,7 @@ Templates.xaml + @@ -445,10 +446,10 @@ 2.8.2-prerelease.220830001 - 1.0.1518.46 + 1.0.1587.40 - 1.1.5 + 1.1.6 1.3.1 diff --git a/LRReader.UWP/Package.appxmanifest b/LRReader.UWP/Package.appxmanifest index f2cf0553..bfc23919 100644 --- a/LRReader.UWP/Package.appxmanifest +++ b/LRReader.UWP/Package.appxmanifest @@ -47,6 +47,11 @@ + + + + + diff --git a/LRReader.UWP/Services/Karen.cs b/LRReader.UWP/Services/Karen.cs new file mode 100644 index 00000000..fda2fb46 --- /dev/null +++ b/LRReader.UWP/Services/Karen.cs @@ -0,0 +1,85 @@ +#nullable enable +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using LRReader.Shared.Models; +using LRReader.Shared.Services; +using Windows.ApplicationModel.AppService; +using Windows.ApplicationModel.Background; +using Windows.Foundation.Collections; + +namespace LRReader.UWP.Services +{ + public class KarenService : IKarenService + { + private BackgroundTaskDeferral? Deferral; + private AppServiceConnection? Connection; + + + public void Connect(object args) + { + var instance = (IBackgroundTaskInstance)args; + var details = (AppServiceTriggerDetails)instance.TriggerDetails; + instance.Canceled += Instance_Canceled; + Deferral = instance.GetDeferral(); + Connection = details.AppServiceConnection; + } + + public async Task?> SendMessage(IDictionary data) + { + if (Connection == null) + return null; + var set = new ValueSet(); + foreach (var keyPair in data) + set.Add(keyPair); + + var res = await Connection?.SendMessageAsync(set); + + if (res.Status == AppServiceResponseStatus.Success) + return res.Message; + return null; + } + + public async Task LoadSetting(SettingType type) + { + if (Connection == null) + return default; + var data = new Dictionary(); + data["PacketType"] = (int)PacketType.InstanceSetting; + data["PacketSettingOperation"] = (int)SettingOperation.Load; + data["PacketSettingType"] = (int)type; + var result = await SendMessage(data); + if (result != null && result.TryGetValue("PacketValue", out var res)) + return (T)res; + return default; + } + + public async Task SaveSetting(SettingType type, object value) + { + if (Connection == null) + return; + var data = new Dictionary(); + data["PacketType"] = (int)PacketType.InstanceSetting; + data["PacketSettingOperation"] = (int)SettingOperation.Save; + data["PacketSettingType"] = (int)type; + data["PacketValue"] = value; + await SendMessage(data); + return; + } + + public void Disconnect() + { + Deferral?.Complete(); + Connection?.Dispose(); + Connection = null; + Deferral = null; + } + + public bool IsConnected => Connection != null; + + private void Instance_Canceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason) + { + Disconnect(); + } + } +} diff --git a/LRReader.UWP/Services/SettingsStorage.cs b/LRReader.UWP/Services/SettingsStorage.cs index ea92a05b..2e24e1ce 100644 --- a/LRReader.UWP/Services/SettingsStorage.cs +++ b/LRReader.UWP/Services/SettingsStorage.cs @@ -1,5 +1,6 @@ #nullable enable using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; using System.Threading.Tasks; using LRReader.Shared.Services; using Windows.Storage; @@ -13,27 +14,27 @@ public class SettingsStorageService : ISettingsStorageService public Task Init() => Task.Delay(1); - public T? GetObjectLocal(string key) => GetObjectLocal(key, default); + public T? GetObjectLocal([CallerMemberName] string? key = null) => GetObjectLocal(default, key); [return: NotNullIfNotNull("def")] - public T? GetObjectLocal(string key, T? def) + public T? GetObjectLocal(T? def, [CallerMemberName] string? key = null) { var val = localSettings.Values[key]; return val != null ? (T)val : def; } - public T? GetObjectRoamed(string key) => GetObjectRoamed(key, default); + public T? GetObjectRoamed([CallerMemberName] string? key = null) => GetObjectRoamed(default, key); [return: NotNullIfNotNull("def")] - public T? GetObjectRoamed(string key, T? def) + public T? GetObjectRoamed(T? def, [CallerMemberName] string? key = null) { var val = roamedSettings.Values[key]; return val != null ? (T)val : def; } - public void StoreObjectLocal(string key, object obj) => localSettings.Values[key] = obj; + public void StoreObjectLocal(object obj, [CallerMemberName] string? key = null) => localSettings.Values[key] = obj; - public void StoreObjectRoamed(string key, object obj) => roamedSettings.Values[key] = obj; + public void StoreObjectRoamed(object obj, [CallerMemberName] string? key = null) => roamedSettings.Values[key] = obj; public void DeleteObjectLocal(string key) => localSettings.Values.Remove(key); diff --git a/LRReader.UWP/Views/Content/Settings/Reader.xaml b/LRReader.UWP/Views/Content/Settings/Reader.xaml index a4ab7005..020fd147 100644 --- a/LRReader.UWP/Views/Content/Settings/Reader.xaml +++ b/LRReader.UWP/Views/Content/Settings/Reader.xaml @@ -90,6 +90,49 @@ + + + + + + + + + + + + + % + + + + + + + + + + + + + diff --git a/LRReader.UWP/Views/Content/Settings/Server.xaml b/LRReader.UWP/Views/Content/Settings/Server.xaml index a2b6cf3d..6aa23a12 100644 --- a/LRReader.UWP/Views/Content/Settings/Server.xaml +++ b/LRReader.UWP/Views/Content/Settings/Server.xaml @@ -138,6 +138,70 @@ + + + + + + + + + + + + diff --git a/LRReader.UWP/Views/Content/Settings/Server.xaml.cs b/LRReader.UWP/Views/Content/Settings/Server.xaml.cs index 19da6851..133f80d0 100644 --- a/LRReader.UWP/Views/Content/Settings/Server.xaml.cs +++ b/LRReader.UWP/Views/Content/Settings/Server.xaml.cs @@ -98,5 +98,10 @@ private void RegenThumb_Click(object sender, RoutedEventArgs e) { RegenThumbsFlyout.Hide(); } - } + + private void Repair_Click(object sender, RoutedEventArgs e) + { + RepairFlyout.Hide(); + } + } } diff --git a/LRReader.UWP/Views/Controls/ArchiveList.xaml b/LRReader.UWP/Views/Controls/ArchiveList.xaml index 7b34f361..95d19bbc 100644 --- a/LRReader.UWP/Views/Controls/ArchiveList.xaml +++ b/LRReader.UWP/Views/Controls/ArchiveList.xaml @@ -161,7 +161,7 @@ + + + + diff --git a/LRReader.UWP/Views/Dialogs/ServerProfileDialog.xaml.cs b/LRReader.UWP/Views/Dialogs/ServerProfileDialog.xaml.cs index 806bdefe..1c2ecb9f 100644 --- a/LRReader.UWP/Views/Dialogs/ServerProfileDialog.xaml.cs +++ b/LRReader.UWP/Views/Dialogs/ServerProfileDialog.xaml.cs @@ -26,6 +26,7 @@ public ServerProfileDialog(bool edit) public new string Name { get => ProfileName.Text; set => ProfileName.Text = value; } public string Address { get => ProfileServerAddress.Text; set => ProfileServerAddress.Text = value; } public string ApiKey { get => ProfileServerApiKey.Password; set => ProfileServerApiKey.Password = value; } + public bool Integration { get => KarenIntegration.IsOn; set => KarenIntegration.IsOn = value; } public new async Task ShowAsync() => (IDialogResult)(int)await base.ShowAsync(); @@ -46,6 +47,7 @@ private void ProfileServerAddress_TextChanging(TextBox sender, TextBoxTextChangi { bool allow = true; Command.Visibility = Visibility.Collapsed; + KarenStack.Visibility = Visibility.Collapsed; ProfileError.Text = ""; if (string.IsNullOrEmpty(ProfileServerAddress.Text)) { @@ -62,6 +64,7 @@ private void ProfileServerAddress_TextChanging(TextBox sender, TextBoxTextChangi ProfileError.Text = lang.GetString("ServerProfile/ErrorLocalHost").AsFormat("\n"); Command.Visibility = Visibility.Visible; CommandBox.Text = $"CheckNetIsolation loopbackexempt -a -n=\"{((UWPlatformService)Service.Platform).GetPackageFamilyName()}\""; + KarenStack.Visibility = Visibility.Visible; } IsPrimaryButtonEnabled = allow && ValidateProfileName(); } diff --git a/LRReader.UWP/Views/Main/HostTabPage.xaml.cs b/LRReader.UWP/Views/Main/HostTabPage.xaml.cs index 943dde18..2790947c 100644 --- a/LRReader.UWP/Views/Main/HostTabPage.xaml.cs +++ b/LRReader.UWP/Views/Main/HostTabPage.xaml.cs @@ -167,8 +167,8 @@ private void FullScreen_Invoked(KeyboardAccelerator sender, KeyboardAcceleratorI private async Task ShowWhatsNew() { - var ver = Version.Parse(SettingsStorage.GetObjectLocal("_version", new Version(0, 0, 0, 0).ToString())); - if (!SettingsStorage.GetObjectLocal("WasUpdated", false) && ver == new Version(0, 0, 0, 0)) + var ver = Version.Parse(SettingsStorage.GetObjectLocal(new Version(0, 0, 0, 0).ToString(), "_version")); + if (!SettingsStorage.GetObjectLocal(false, "WasUpdated") && ver == new Version(0, 0, 0, 0)) return; SettingsStorage.DeleteObjectLocal("WasUpdated"); SettingsStorage.DeleteObjectLocal("_version"); diff --git a/LRReader.UWP/Views/Tabs/ArchiveTab.xaml b/LRReader.UWP/Views/Tabs/ArchiveTab.xaml index d4fa54de..af1fe9b8 100644 --- a/LRReader.UWP/Views/Tabs/ArchiveTab.xaml +++ b/LRReader.UWP/Views/Tabs/ArchiveTab.xaml @@ -108,6 +108,17 @@ + + + + + + + + diff --git a/LRReader.UWP/Views/Tabs/Content/ArchiveTabContent.xaml.cs b/LRReader.UWP/Views/Tabs/Content/ArchiveTabContent.xaml.cs index 9091b20e..781b323a 100644 --- a/LRReader.UWP/Views/Tabs/Content/ArchiveTabContent.xaml.cs +++ b/LRReader.UWP/Views/Tabs/Content/ArchiveTabContent.xaml.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -20,10 +21,12 @@ using Windows.Storage.Pickers; using Windows.Storage.Provider; using Windows.System; +using Windows.UI.Composition; using Windows.UI.Core; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Media.Animation; using RefreshContainer = Microsoft.UI.Xaml.Controls.RefreshContainer; using RefreshRequestedEventArgs = Microsoft.UI.Xaml.Controls.RefreshRequestedEventArgs; @@ -41,11 +44,14 @@ public sealed partial class ArchiveTabContent : UserControl private bool _opened; private bool _focus = true; private bool _changePage; + private bool _changingPage; private float _lastZoom; private double _fitAgainstFixedWidth; private bool _transition; + private TimeSpan _previousTime = TimeSpan.Zero; + private SemaphoreSlim _loadSemaphore = new SemaphoreSlim(1); public ArchiveTabContent() @@ -139,7 +145,9 @@ private async void OpenReader(int page, object? item = null) _focus = true; FocusReader(); + _transition = false; + await PlayStop(Service.Settings.Autoplay); } public async void CloseReader() @@ -147,6 +155,7 @@ public async void CloseReader() if (_transition) return; _transition = true; + await PlayStop(false); var animate = Service.Platform.AnimationsEnabled; ConnectedAnimation? animLeft = null, animRight = null; @@ -212,6 +221,7 @@ public async void CloseReader() _wasNew = await Data.SaveReaderData(_wasNew); _transition = false; + _changePage = false; } private async void NextArchive() => await NextArchiveAsync(); @@ -503,18 +513,30 @@ private void ReaderControl_ManipulationDelta(object sender, ManipulationDeltaRou private async void NextPage(bool ignore = false) { + _changingPage = true; + if (Data.UseAutoplay) + await Task.Delay(TimeSpan.FromMilliseconds(Service.Settings.AutoplayBeforeChangeDelay)); if (Data.ReadRTL && !ignore) await GoLeft(); else await GoRight(); + if (Data.UseAutoplay) + await Task.Delay(TimeSpan.FromMilliseconds(Service.Settings.AutoplayAfterChangeDelay)); + _changingPage = false; } private async void PrevPage(bool ignore = false) { + _changingPage = true; + if (Data.UseAutoplay) + await Task.Delay(TimeSpan.FromMilliseconds(Service.Settings.AutoplayBeforeChangeDelay)); if (Data.ReadRTL && !ignore) await GoRight(); else await GoLeft(); + if (Data.UseAutoplay) + await Task.Delay(TimeSpan.FromMilliseconds(Service.Settings.AutoplayAfterChangeDelay)); + _changingPage = false; } private async Task GoRight() @@ -624,6 +646,38 @@ private async void ScrollViewer_ViewChanged(object sender, ScrollViewerViewChang await ReaderImage.ResizeHeight((int)Math.Round(ScrollViewer.ExtentHeight)); } + private void CompositionTarget_Rendering(object sender, object e) + { + var timings = (RenderingEventArgs)e; + if (!_changingPage) + { + if (ScrollViewer.VerticalOffset >= ScrollViewer.ScrollableHeight) + { + NextPage(); + } + else + { + var yOffset = ScrollViewer.VerticalOffset + Service.Settings.AutoplaySpeed * 0.01 * _lastZoom; + ScrollViewer.ChangeView(null, yOffset, null, true); + } + } + _previousTime = timings.RenderingTime; + } + + [RelayCommand] + private async Task PlayStop(bool state) + { + Data.UseAutoplay = state; + if (state) + { + ScrollViewer.ChangeView(null, 0, null, true); + await Task.Delay(TimeSpan.FromMilliseconds(Service.Settings.AutoplayStartDelay)); + Windows.UI.Xaml.Media.CompositionTarget.Rendering += CompositionTarget_Rendering; + } + else + Windows.UI.Xaml.Media.CompositionTarget.Rendering -= CompositionTarget_Rendering; + } + private async void DownloadButton_Click(object sender, RoutedEventArgs e) { Data.Downloading = true; diff --git a/LRReader.UWP/Views/Tabs/Content/ArchivesTabContent.xaml b/LRReader.UWP/Views/Tabs/Content/ArchivesTabContent.xaml index 77fb3436..1135e1de 100644 --- a/LRReader.UWP/Views/Tabs/Content/ArchivesTabContent.xaml +++ b/LRReader.UWP/Views/Tabs/Content/ArchivesTabContent.xaml @@ -165,6 +165,6 @@