diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4be1964f..2ff095b9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,11 +1,11 @@ name: Pixeval Build Pipeline on: - push: - branches-ignore: - - 'dependabot**' - tags-ignore: - - '**' + # push: + # branches-ignore: + # - 'dependabot**' + # tags-ignore: + # - '**' pull_request: paths-ignore: - '**.md' @@ -23,9 +23,9 @@ jobs: runs-on: windows-latest strategy: - fail-fast: false + fail-fast: true matrix: - configuration: [Debug] + configuration: [Release] platform: ['x64','x86','arm64'] env: @@ -70,8 +70,8 @@ jobs: shell: pwsh run: 'nuget restore $env:SOLUTION_NAME' - - name: Build Files (Debug) - id: build_app_with_debug + - name: Build Files (Release) + id: build_app_with_release shell: pwsh run: | msbuild $env:PACKAGE_PROJECT_NAME ` diff --git a/.gitignore b/.gitignore index c631a3ad..4c10ab12 100644 --- a/.gitignore +++ b/.gitignore @@ -184,7 +184,7 @@ publish/ *.azurePubxml # Note: Comment the next line if you want to checkin your web deploy settings, # but database connection strings (with potential passwords) will be unencrypted -*.pubxml +# *.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to diff --git a/Pixeval.sln b/Pixeval.sln index 60d281ad..85580c45 100644 --- a/Pixeval.sln +++ b/Pixeval.sln @@ -1,4 +1,3 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.31808.319 diff --git a/Pixeval.slnx b/Pixeval.slnx index a161cdb3..a7edfd9b 100644 --- a/Pixeval.slnx +++ b/Pixeval.slnx @@ -35,23 +35,19 @@ - - - - \ No newline at end of file diff --git a/src/Pixeval.Controls/AdvancedItemsView/ItemsViewLayoutType.cs b/src/Pixeval.Controls/AdvancedItemsView/ItemsViewLayoutType.cs index ea579ceb..f9551234 100644 --- a/src/Pixeval.Controls/AdvancedItemsView/ItemsViewLayoutType.cs +++ b/src/Pixeval.Controls/AdvancedItemsView/ItemsViewLayoutType.cs @@ -24,6 +24,7 @@ using Pixeval.Attributes; namespace Pixeval.Controls; +[LocalizationMetadata(typeof(AdvancedItemsViewResources))] public enum ItemsViewLayoutType { [LocalizedResource(typeof(AdvancedItemsViewResources), nameof(AdvancedItemsViewResources.LinedFlow))] diff --git a/src/Pixeval.Controls/AdvancedObservableCollection.cs b/src/Pixeval.Controls/AdvancedObservableCollection.cs index b09850e9..c7a7977f 100644 --- a/src/Pixeval.Controls/AdvancedObservableCollection.cs +++ b/src/Pixeval.Controls/AdvancedObservableCollection.cs @@ -33,12 +33,13 @@ using Windows.Foundation; using CommunityToolkit.WinUI.Collections; using CommunityToolkit.WinUI.Helpers; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; namespace Pixeval.Collections; [DebuggerDisplay("Count = {Count}")] -public class AdvancedObservableCollection : IList, IList, IReadOnlyList, INotifyCollectionChanged, INotifyPropertyChanged, ISupportIncrementalLoading, IComparer where T : class +public class AdvancedObservableCollection<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T> : IList, IList, IReadOnlyList, INotifyCollectionChanged, INotifyPropertyChanged, ISupportIncrementalLoading, IComparer where T : class { private readonly Dictionary _sortProperties = []; diff --git a/src/Pixeval.Controls/ColorHelper.cs b/src/Pixeval.Controls/Attributes/LocalizationMetadataAttribute.cs similarity index 54% rename from src/Pixeval.Controls/ColorHelper.cs rename to src/Pixeval.Controls/Attributes/LocalizationMetadataAttribute.cs index 6118b538..58800919 100644 --- a/src/Pixeval.Controls/ColorHelper.cs +++ b/src/Pixeval.Controls/Attributes/LocalizationMetadataAttribute.cs @@ -1,9 +1,8 @@ #region Copyright - // GPL v3 License // -// Pixeval/Pixeval.Controls -// Copyright (c) 2024 Pixeval.Controls/ColorHelper.cs +// Pixeval/Pixeval +// Copyright (c) 2024 Pixeval/EnumLocalizationMetadataAttribute.cs // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -17,31 +16,23 @@ // // You should have received a copy of the GNU General Public License // along with this program. If not, see . - #endregion -using Windows.UI; +using System; -namespace Pixeval.Controls; +namespace Pixeval.Attributes; -public static class ColorHelper +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum)] +public class LocalizationMetadataAttribute(Type resourceType) : Attribute { - public static unsafe Color GetAlphaColor(this uint color) - { - var ptr = &color; - var c = (byte*)ptr; - return Color.FromArgb(c[3], c[2], c[1], c[0]); - } + public Type ResourceType { get; } = resourceType; - public static unsafe uint GetAlphaUInt(this Color color) - { - uint ret; - var ptr = &ret; - var c = (byte*)ptr; - c[0] = color.B; - c[1] = color.G; - c[2] = color.R; - c[3] = color.A; - return ret; - } + public bool IsPartial { get; init; } +} + + +[AttributeUsage(AttributeTargets.Class)] +public class AttachedLocalizationMetadataAttribute(Type resourceType) : Attribute +{ + public Type ResourceType { get; } = resourceType; } diff --git a/src/Pixeval.Controls/Attributes/LocalizedResourceAttribute.cs b/src/Pixeval.Controls/Attributes/LocalizedResourceAttribute.cs index ef36261e..63de848e 100644 --- a/src/Pixeval.Controls/Attributes/LocalizedResourceAttribute.cs +++ b/src/Pixeval.Controls/Attributes/LocalizedResourceAttribute.cs @@ -24,7 +24,7 @@ using System; namespace Pixeval.Attributes; [AttributeUsage(AttributeTargets.Field)] -public class LocalizedResource(Type resourceLoader, string key, object? formatKey = null) : Attribute +public class LocalizedResourceAttribute(Type resourceLoader, string key, object? formatKey = null) : Attribute { public Type ResourceLoader { get; } = resourceLoader; @@ -32,3 +32,11 @@ public class LocalizedResource(Type resourceLoader, string key, object? formatKe public object? FormatKey { get; } = formatKey; } + +[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] +public class AttachedLocalizedResourceAttribute(string fieldName, string key) : Attribute +{ + public string FieldName { get; } = fieldName; + + public string Key { get; } = key; +} diff --git a/src/Pixeval.Controls/C.cs b/src/Pixeval.Controls/C.cs index dab6a6aa..39c99944 100644 --- a/src/Pixeval.Controls/C.cs +++ b/src/Pixeval.Controls/C.cs @@ -62,7 +62,7 @@ public static class C return Color.FromArgb(c[3], c[2], c[1], c[0]); } - public static SolidColorBrush ToSolidColorBrush(uint value) => new(value.GetAlphaColor()); + public static SolidColorBrush ToSolidColorBrush(uint value) => new(ToAlphaColor(value)); public static unsafe uint ToAlphaUInt(Color color) { diff --git a/src/Pixeval.Controls/Pixeval.Controls.csproj b/src/Pixeval.Controls/Pixeval.Controls.csproj index ff8f0faa..1fce51e6 100644 --- a/src/Pixeval.Controls/Pixeval.Controls.csproj +++ b/src/Pixeval.Controls/Pixeval.Controls.csproj @@ -7,9 +7,11 @@ win-x86;win-x64;win-arm64 true Enable + Debug;Release preview true zh-cn + true @@ -17,20 +19,21 @@ - all - runtime; build; native; contentfiles; analyzers; buildtransitive + all + runtime; build; native; contentfiles; analyzers; buildtransitive - + - - + + - + + diff --git a/src/Pixeval/Controls/StringRepresentableItem.cs b/src/Pixeval.Controls/StringRepresentableItem.cs similarity index 100% rename from src/Pixeval/Controls/StringRepresentableItem.cs rename to src/Pixeval.Controls/StringRepresentableItem.cs diff --git a/src/Pixeval.CoreApi/AppJsonSerializerContext.cs b/src/Pixeval.CoreApi/AppJsonSerializerContext.cs index a20faf4b..8fa9743f 100644 --- a/src/Pixeval.CoreApi/AppJsonSerializerContext.cs +++ b/src/Pixeval.CoreApi/AppJsonSerializerContext.cs @@ -20,11 +20,14 @@ #endregion +using System.Text.Json; +using System; using System.Text.Json.Serialization; using Pixeval.CoreApi.Global.Enum; using Pixeval.CoreApi.Model; using Pixeval.CoreApi.Net.Request; using Pixeval.CoreApi.Net.Response; +using Pixeval.CoreApi.Preference; namespace Pixeval.CoreApi; @@ -32,7 +35,7 @@ namespace Pixeval.CoreApi; [JsonSerializable(typeof(PixivBookmarkTagResponse))] [JsonSerializable(typeof(PixivCommentResponse))] [JsonSerializable(typeof(PixivIllustrationResponse))] -[JsonSerializable(typeof(PixivNextUrlResponse<>))] +// [JsonSerializable(typeof(PixivNextUrlResponse<>))] [JsonSerializable(typeof(PixivNovelResponse))] [JsonSerializable(typeof(PixivRelatedUsersResponse))] [JsonSerializable(typeof(PixivSingleIllustResponse))] @@ -138,4 +141,32 @@ namespace Pixeval.CoreApi; [JsonSerializable(typeof(WorkSortOption))] [JsonSerializable(typeof(WorkType))] [JsonSerializable(typeof(SimpleWorkType))] -internal partial class AppJsonSerializerContext : JsonSerializerContext; + +[JsonSerializable(typeof(string[]))] +[JsonSerializable(typeof(Tag[]))] +[JsonSerializable(typeof(MetaPage[]))] +[JsonSerializable(typeof(NovelIllustInfo[]))] +[JsonSerializable(typeof(NovelImage[]))] +[JsonSerializable(typeof(NovelReplaceableGlossary[]))] +[JsonSerializable(typeof(long[]))] +[JsonSerializable(typeof(NovelTag[]))] +[JsonSerializable(typeof(BookmarkTag[]))] +[JsonSerializable(typeof(Comment[]))] +[JsonSerializable(typeof(Illustration[]))] +[JsonSerializable(typeof(Novel[]))] +[JsonSerializable(typeof(User[]))] +[JsonSerializable(typeof(SpotlightBody[]))] +[JsonSerializable(typeof(PixivisionTag[]))] +[JsonSerializable(typeof(Illust[]))] +[JsonSerializable(typeof(RelatedArticle[]))] +[JsonSerializable(typeof(Spotlight[]))] +[JsonSerializable(typeof(Result[]))] +[JsonSerializable(typeof(TrendingTag[]))] +[JsonSerializable(typeof(Frame[]))] +[JsonSerializable(typeof(UserSpecifiedBookmarkTag[]))] +[JsonSerializable(typeof(Work[]))] + +[JsonSerializable(typeof(Session))] +public partial class AppJsonSerializerContext : JsonSerializerContext; + +public class SnakeCaseLowerEnumConverter() : JsonStringEnumConverter(JsonNamingPolicy.SnakeCaseLower) where T : struct, Enum; diff --git a/src/Pixeval.CoreApi/Engine/AbstractPixivAsyncEnumerator.cs b/src/Pixeval.CoreApi/Engine/AbstractPixivAsyncEnumerator.cs index 59725153..e4efd53d 100644 --- a/src/Pixeval.CoreApi/Engine/AbstractPixivAsyncEnumerator.cs +++ b/src/Pixeval.CoreApi/Engine/AbstractPixivAsyncEnumerator.cs @@ -21,6 +21,7 @@ using System; using System.Collections.Generic; using System.Net.Http; +using System.Text.Json; using System.Threading.Tasks; using Pixeval.CoreApi.Global.Exception; using Pixeval.CoreApi.Model; @@ -43,7 +44,7 @@ namespace Pixeval.CoreApi.Engine; /// The fetch engine public abstract class AbstractPixivAsyncEnumerator(TFetchEngine pixivFetchEngine, MakoApiKind apiKind) : IAsyncEnumerator - where TFetchEngine : class, IFetchEngine + where TFetchEngine : class, IFetchEngine where TEntity : class, IEntry where TRawEntity : class { @@ -100,13 +101,12 @@ public abstract class AbstractPixivAsyncEnumerator.AsFailure(await MakoNetworkException.FromHttpResponseMessageAsync(responseMessage, MakoClient.Configuration.DomainFronting).ConfigureAwait(false)); } - var result = (await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false)).FromJson(); + var str = await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false); - return result is null - ? Result.AsFailure() - : ValidateResponse(result) - ? Result.AsSuccess(result) - : Result.AsFailure(); + if (JsonSerializer.Deserialize(str, typeof(TRawEntity), AppJsonSerializerContext.Default) is TRawEntity result) + if (ValidateResponse(result)) + return Result.AsSuccess(result); + return Result.AsFailure(); } catch (Exception e) { diff --git a/src/Pixeval.CoreApi/Engine/RecursivePixivFetchEngine.cs b/src/Pixeval.CoreApi/Engine/RecursivePixivFetchEngine.cs index ca95d637..0f0c3366 100644 --- a/src/Pixeval.CoreApi/Engine/RecursivePixivFetchEngine.cs +++ b/src/Pixeval.CoreApi/Engine/RecursivePixivFetchEngine.cs @@ -29,9 +29,9 @@ namespace Pixeval.CoreApi.Engine; internal abstract class RecursivePixivAsyncEnumerator(TFetchEngine pixivFetchEngine, MakoApiKind makoApiKind) : AbstractPixivAsyncEnumerator(pixivFetchEngine, makoApiKind) - where TEntity : class, IEntry + where TEntity : class, IEntry where TRawEntity : class - where TFetchEngine : class, IFetchEngine + where TFetchEngine : class, IFetchEngine { private TRawEntity? RawEntity { get; set; } @@ -112,9 +112,9 @@ internal static class RecursivePixivAsyncEnumerators { public abstract class BaseRecursivePixivAsyncEnumerator(TFetchEngine pixivFetchEngine, MakoApiKind makoApiKind, string initialUrl) : RecursivePixivAsyncEnumerator(pixivFetchEngine, makoApiKind) - where TEntity : class, IEntry - where TRawEntity : PixivNextUrlResponse - where TFetchEngine : class, IFetchEngine + where TEntity : class, IEntry + where TRawEntity : class, IPixivNextUrlResponse + where TFetchEngine : class, IFetchEngine { protected override string InitialUrl => initialUrl; diff --git a/src/Pixeval.CoreApi/Global/Enum/PrivacyPolicy.cs b/src/Pixeval.CoreApi/Global/Enum/PrivacyPolicy.cs index 428aa2ee..1ea180a3 100644 --- a/src/Pixeval.CoreApi/Global/Enum/PrivacyPolicy.cs +++ b/src/Pixeval.CoreApi/Global/Enum/PrivacyPolicy.cs @@ -19,7 +19,7 @@ #endregion using System.ComponentModel; -using System.Runtime.Serialization; +using System.Text.Json.Serialization; namespace Pixeval.CoreApi.Global.Enum; @@ -27,13 +27,12 @@ namespace Pixeval.CoreApi.Global.Enum; /// The privacy policy of Pixiv, be aware that the option /// is only permitted when the ID is pointing to yourself /// +[JsonConverter(typeof(SnakeCaseLowerEnumConverter))] public enum PrivacyPolicy { [Description("public")] - [EnumMember(Value = "public")] Public, [Description("private")] - [EnumMember(Value = "private")] Private } diff --git a/src/Pixeval.CoreApi/Global/Enum/RankOption.cs b/src/Pixeval.CoreApi/Global/Enum/RankOption.cs index f1725af5..53004d27 100644 --- a/src/Pixeval.CoreApi/Global/Enum/RankOption.cs +++ b/src/Pixeval.CoreApi/Global/Enum/RankOption.cs @@ -90,27 +90,3 @@ public enum RankOption [Description("day_r18_ai")] DayR18Ai } - -public static class RankOptionExtension -{ - public static readonly RankOption[] NovelRankOptions = - [ - RankOption.Day, - RankOption.Week, - // RankOption.Month, - RankOption.DayMale, - RankOption.DayFemale, - // RankOption.DayManga, - // RankOption.WeekManga, - // RankOption.MonthManga, - RankOption.WeekOriginal, - RankOption.WeekRookie, - RankOption.DayR18, - RankOption.DayMaleR18, - RankOption.DayFemaleR18, - RankOption.WeekR18, - RankOption.WeekR18G, - RankOption.DayAi, - RankOption.DayR18Ai - ]; -} diff --git a/src/Pixeval.CoreApi/Global/Enum/TargetFilter.cs b/src/Pixeval.CoreApi/Global/Enum/TargetFilter.cs index dfd3cebd..bc63dc36 100644 --- a/src/Pixeval.CoreApi/Global/Enum/TargetFilter.cs +++ b/src/Pixeval.CoreApi/Global/Enum/TargetFilter.cs @@ -19,17 +19,16 @@ #endregion using System.ComponentModel; -using System.Runtime.Serialization; +using System.Text.Json.Serialization; namespace Pixeval.CoreApi.Global.Enum; +[JsonConverter(typeof(SnakeCaseLowerEnumConverter))] public enum TargetFilter { [Description("for_android")] - [EnumMember(Value = "for_android")] ForAndroid, [Description("for_ios")] - [EnumMember(Value = "for_ios")] ForIos } diff --git a/src/Pixeval.CoreApi/MakoClient.Extensions.cs b/src/Pixeval.CoreApi/MakoClient.Extensions.cs index 75cabc80..1fe209ae 100644 --- a/src/Pixeval.CoreApi/MakoClient.Extensions.cs +++ b/src/Pixeval.CoreApi/MakoClient.Extensions.cs @@ -95,7 +95,7 @@ public partial class MakoClient var span = contentHtml[startIndex..endIndex]; - return JsonSerializer.Deserialize(span)!; + return (NovelContent)JsonSerializer.Deserialize(span, typeof(NovelContent), AppJsonSerializerContext.Default)!; }); /// diff --git a/src/Pixeval.CoreApi/MakoClient.Logging.cs b/src/Pixeval.CoreApi/MakoClient.Logging.cs index c4da58a3..42cefe0b 100644 --- a/src/Pixeval.CoreApi/MakoClient.Logging.cs +++ b/src/Pixeval.CoreApi/MakoClient.Logging.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Net; using System.Net.Http; using System.Threading.Tasks; @@ -101,7 +102,7 @@ public partial class MakoClient } } - private async Task RunWithLoggerAsync(Func> task) + private async Task RunWithLoggerAsync(Func> task) { try { @@ -149,13 +150,14 @@ public partial class MakoClient internal void LogException(Exception e) => Logger.LogError("MakoClient Exception", e); + [DynamicDependency("ConstructSystemProxy", "SystemProxyInfo", "System.Net.Http")] static MakoClient() { var type = typeof(HttpClient).Assembly.GetType("System.Net.Http.SystemProxyInfo"); var method = type?.GetMethod("ConstructSystemProxy"); var @delegate = method?.CreateDelegate>(); - _getCurrentSystemProxy = @delegate ?? ThrowUtils.Throw>("Unable to find proxy functions"); + _getCurrentSystemProxy = @delegate ?? ThrowUtils.Throw>(new("Unable to find proxy functions")); HttpClient.DefaultProxy = _getCurrentSystemProxy(); } diff --git a/src/Pixeval.CoreApi/MakoClient.cs b/src/Pixeval.CoreApi/MakoClient.cs index 7c64617a..941c45e0 100644 --- a/src/Pixeval.CoreApi/MakoClient.cs +++ b/src/Pixeval.CoreApi/MakoClient.cs @@ -64,8 +64,9 @@ public partial class MakoClient : ICancellable, IDisposable, IAsyncDisposable makoClient.Session = (await makoClient.Provider.GetRequiredService().RefreshAsync(new RefreshSessionRequest(refreshToken)).ConfigureAwait(false)).ToSession(); return makoClient; } - catch + catch (Exception e) { + logger.LogError("Login error", e); await makoClient.DisposeAsync(); return null; } diff --git a/src/Pixeval.CoreApi/Model/Comment.cs b/src/Pixeval.CoreApi/Model/Comment.cs index 1278d6ec..f45ff6c8 100644 --- a/src/Pixeval.CoreApi/Model/Comment.cs +++ b/src/Pixeval.CoreApi/Model/Comment.cs @@ -30,7 +30,7 @@ public partial record Comment : IIdEntry public required long Id { get; set; } [JsonPropertyName("comment")] - public required string CommentContent { get; set; } + public required string CommentContent { get; set; } = ""; [JsonPropertyName("date")] public required DateTimeOffset Date { get; set; } @@ -52,7 +52,7 @@ public partial record Stamp public required long StampId { get; set; } [JsonPropertyName("stamp_url")] - public required string StampUrl { get; set; } + public required string StampUrl { get; set; } = DefaultImageUrls.ImageNotAvailable; } [Factory] @@ -62,10 +62,10 @@ public partial record CommentUser public required long Id { get; set; } [JsonPropertyName("name")] - public required string Name { get; set; } + public required string Name { get; set; } = ""; [JsonPropertyName("account")] - public required string Account { get; set; } + public required string Account { get; set; } = ""; [JsonPropertyName("profile_image_urls")] public required ProfileImageUrls ProfileImageUrls { get; set; } diff --git a/src/Pixeval.CoreApi/Model/NovelContent.cs b/src/Pixeval.CoreApi/Model/NovelContent.cs index 8d456893..fb977b89 100644 --- a/src/Pixeval.CoreApi/Model/NovelContent.cs +++ b/src/Pixeval.CoreApi/Model/NovelContent.cs @@ -301,8 +301,8 @@ internal class SpecialDictionaryConverter : JsonConverter return ThrowUtils.Json(); case JsonTokenType.PropertyName: { - var jsonConverter = (JsonConverter)options.GetConverter(typeof(T)); - list.Add(jsonConverter.Read(ref reader, typeof(T), options)!); + var propertyValue = (T)JsonSerializer.Deserialize(ref reader, typeof(T), AppJsonSerializerContext.Default)!; + list.Add(propertyValue); break; } case JsonTokenType.EndObject: diff --git a/src/Pixeval.CoreApi/Model/TokenResponse.cs b/src/Pixeval.CoreApi/Model/TokenResponse.cs index 83a36a3a..2202713d 100644 --- a/src/Pixeval.CoreApi/Model/TokenResponse.cs +++ b/src/Pixeval.CoreApi/Model/TokenResponse.cs @@ -31,19 +31,19 @@ namespace Pixeval.CoreApi.Model; public partial record TokenResponse { [JsonPropertyName("access_token")] - public required string AccessToken { get; set; } + public required string AccessToken { get; set; } = ""; [JsonPropertyName("expires_in")] public required long ExpiresIn { get; set; } [JsonPropertyName("token_type")] - public required string TokenType { get; set; } + public required string TokenType { get; set; } = ""; [JsonPropertyName("scope")] - public required string Scope { get; set; } + public required string Scope { get; set; } = ""; [JsonPropertyName("refresh_token")] - public required string RefreshToken { get; set; } + public required string RefreshToken { get; set; } = ""; [JsonPropertyName("user")] public required TokenUser User { get; set; } @@ -79,13 +79,13 @@ public partial record TokenUser public required long Id { get; set; } [JsonPropertyName("name")] - public required string Name { get; set; } + public required string Name { get; set; } = ""; [JsonPropertyName("account")] - public required string Account { get; set; } + public required string Account { get; set; } = ""; [JsonPropertyName("mail_address")] - public required string MailAddress { get; set; } + public required string MailAddress { get; set; } = ""; [JsonPropertyName("is_premium")] public required bool IsPremium { get; set; } @@ -104,11 +104,11 @@ public partial record TokenUser public partial record TokenProfileImageUrls { [JsonPropertyName("px_16x16")] - public required string Px16X16 { get; set; } + public required string Px16X16 { get; set; } = DefaultImageUrls.NoProfile; [JsonPropertyName("px_50x50")] - public required string Px50X50 { get; set; } + public required string Px50X50 { get; set; } = DefaultImageUrls.NoProfile; [JsonPropertyName("px_170x170")] - public required string Px170X170 { get; set; } + public required string Px170X170 { get; set; } = DefaultImageUrls.NoProfile; } diff --git a/src/Pixeval.CoreApi/Net/EndPoints/IAppApiEndPoint.cs b/src/Pixeval.CoreApi/Net/EndPoints/IAppApiEndPoint.cs index 897a5974..4a6bac2b 100644 --- a/src/Pixeval.CoreApi/Net/EndPoints/IAppApiEndPoint.cs +++ b/src/Pixeval.CoreApi/Net/EndPoints/IAppApiEndPoint.cs @@ -53,7 +53,7 @@ public interface IAppApiEndPoint Task GetSingleNovelAsync([AliasAs("novel_id")] long id); [HttpGet("/webview/v2/novel")] - Task GetNovelContentAsync([AliasAs("id")] long id, [AliasAs("raw")] bool raw = false); + Task GetNovelContentAsync(long id, bool raw = false); /* [AliasAs("viewer_version")] string viewerVersion = "20221031_ai", [AliasAs("font")] string x1 = "mincho", @@ -68,7 +68,7 @@ public interface IAppApiEndPoint */ [HttpGet("/v1/user/related")] - Task RelatedUserAsync([AliasAs("filter")] TargetFilter filter, [AliasAs("seed_user_id")] long userId); + Task RelatedUserAsync(TargetFilter filter, [AliasAs("seed_user_id")] long userId); [HttpPost("/v1/user/follow/add")] Task FollowUserAsync([FormContent] FollowUserRequest request); @@ -77,10 +77,10 @@ public interface IAppApiEndPoint Task RemoveFollowUserAsync([FormContent] RemoveFollowUserRequest request); [HttpGet("/v1/trending-tags/illust")] - Task GetTrendingTagsAsync([AliasAs("filter")] TargetFilter filter); + Task GetTrendingTagsAsync(TargetFilter filter); [HttpGet("/v1/trending-tags/novel")] - Task GetTrendingTagsForNovelAsync([AliasAs("filter")] TargetFilter filter); + Task GetTrendingTagsForNovelAsync(TargetFilter filter); [HttpGet("/v1/ugoira/metadata")] Task GetUgoiraMetadataAsync([AliasAs("illust_id")] long id); diff --git a/src/Pixeval.CoreApi/Net/Response/PixivBookmarkTagResponse.cs b/src/Pixeval.CoreApi/Net/Response/PixivBookmarkTagResponse.cs index 963555c6..4a83660c 100644 --- a/src/Pixeval.CoreApi/Net/Response/PixivBookmarkTagResponse.cs +++ b/src/Pixeval.CoreApi/Net/Response/PixivBookmarkTagResponse.cs @@ -26,8 +26,11 @@ using Pixeval.CoreApi.Model; namespace Pixeval.CoreApi.Net.Response; [Factory] -public partial record PixivBookmarkTagResponse : PixivNextUrlResponse +public partial record PixivBookmarkTagResponse : IPixivNextUrlResponse { + [JsonPropertyName("next_url")] + public required string? NextUrl { get; set; } + [JsonPropertyName("bookmark_tags")] - public override required BookmarkTag[] Entities { get; set; } = []; + public /*override*/ required BookmarkTag[] Entities { get; set; } = []; } diff --git a/src/Pixeval.CoreApi/Net/Response/PixivCommentResponse.cs b/src/Pixeval.CoreApi/Net/Response/PixivCommentResponse.cs index 3d686693..fcec183a 100644 --- a/src/Pixeval.CoreApi/Net/Response/PixivCommentResponse.cs +++ b/src/Pixeval.CoreApi/Net/Response/PixivCommentResponse.cs @@ -24,8 +24,11 @@ using Pixeval.CoreApi.Model; namespace Pixeval.CoreApi.Net.Response; [Factory] -public partial record PixivCommentResponse : PixivNextUrlResponse +public partial record PixivCommentResponse : IPixivNextUrlResponse { + [JsonPropertyName("next_url")] + public required string? NextUrl { get; set; } + [JsonPropertyName("comments")] - public override required Comment[] Entities { get; set; } = []; + public /*override*/ required Comment[] Entities { get; set; } = []; } diff --git a/src/Pixeval.CoreApi/Net/Response/PixivIllustrationResponse.cs b/src/Pixeval.CoreApi/Net/Response/PixivIllustrationResponse.cs index cb2318f9..9267b93a 100644 --- a/src/Pixeval.CoreApi/Net/Response/PixivIllustrationResponse.cs +++ b/src/Pixeval.CoreApi/Net/Response/PixivIllustrationResponse.cs @@ -24,8 +24,11 @@ using Pixeval.CoreApi.Model; namespace Pixeval.CoreApi.Net.Response; [Factory] -public partial record PixivIllustrationResponse : PixivNextUrlResponse +public partial record PixivIllustrationResponse : IPixivNextUrlResponse { + [JsonPropertyName("next_url")] + public required string? NextUrl { get; set; } + [JsonPropertyName("illusts")] - public override required Illustration[] Entities { get; set; } = []; + public /*override*/ required Illustration[] Entities { get; set; } = []; } diff --git a/src/Pixeval.CoreApi/Net/Response/PixivNextUrlResponse.cs b/src/Pixeval.CoreApi/Net/Response/PixivNextUrlResponse.cs index 6640cd8a..eecc8ae7 100644 --- a/src/Pixeval.CoreApi/Net/Response/PixivNextUrlResponse.cs +++ b/src/Pixeval.CoreApi/Net/Response/PixivNextUrlResponse.cs @@ -25,10 +25,10 @@ using Pixeval.CoreApi.Model; namespace Pixeval.CoreApi.Net.Response; -public abstract record PixivNextUrlResponse where TEntity : class, IEntry +public interface IPixivNextUrlResponse where TEntity : class, IEntry { [JsonPropertyName("next_url")] - public required string? NextUrl { get; set; } = null; + string? NextUrl { get; set; } - public abstract TEntity[] Entities { get; set; } + TEntity[] Entities { get; set; } } diff --git a/src/Pixeval.CoreApi/Net/Response/PixivNovelResponse.cs b/src/Pixeval.CoreApi/Net/Response/PixivNovelResponse.cs index faccba55..85d9ee1f 100644 --- a/src/Pixeval.CoreApi/Net/Response/PixivNovelResponse.cs +++ b/src/Pixeval.CoreApi/Net/Response/PixivNovelResponse.cs @@ -24,8 +24,11 @@ using Pixeval.CoreApi.Model; namespace Pixeval.CoreApi.Net.Response; [Factory] -public partial record PixivNovelResponse : PixivNextUrlResponse +public partial record PixivNovelResponse : IPixivNextUrlResponse { + [JsonPropertyName("next_url")] + public required string? NextUrl { get; set; } + [JsonPropertyName("novels")] - public override required Novel[] Entities { get; set; } = []; + public /*override*/ required Novel[] Entities { get; set; } = []; } diff --git a/src/Pixeval.CoreApi/Net/Response/PixivSpotlightResponse.cs b/src/Pixeval.CoreApi/Net/Response/PixivSpotlightResponse.cs index 28b26755..3784b1b8 100644 --- a/src/Pixeval.CoreApi/Net/Response/PixivSpotlightResponse.cs +++ b/src/Pixeval.CoreApi/Net/Response/PixivSpotlightResponse.cs @@ -24,8 +24,11 @@ using Pixeval.CoreApi.Model; namespace Pixeval.CoreApi.Net.Response; [Factory] -public partial record PixivSpotlightResponse : PixivNextUrlResponse +public partial record PixivSpotlightResponse : IPixivNextUrlResponse { + [JsonPropertyName("next_url")] + public required string? NextUrl { get; set; } + [JsonPropertyName("spotlight_articles")] - public override required Spotlight[] Entities { get; set; } = []; + public /*override*/ required Spotlight[] Entities { get; set; } = []; } diff --git a/src/Pixeval.CoreApi/Net/Response/PixivUserResponse.cs b/src/Pixeval.CoreApi/Net/Response/PixivUserResponse.cs index 2c1498a0..11dd799a 100644 --- a/src/Pixeval.CoreApi/Net/Response/PixivUserResponse.cs +++ b/src/Pixeval.CoreApi/Net/Response/PixivUserResponse.cs @@ -24,8 +24,11 @@ using Pixeval.CoreApi.Model; namespace Pixeval.CoreApi.Net.Response; [Factory] -public partial record PixivUserResponse : PixivNextUrlResponse +public partial record PixivUserResponse : IPixivNextUrlResponse { + [JsonPropertyName("next_url")] + public required string? NextUrl { get; set; } + [JsonPropertyName("user_previews")] - public override required User[] Entities { get; set; } = []; + public /*override*/ required User[] Entities { get; set; } = []; } diff --git a/src/Pixeval.CoreApi/Pixeval.CoreApi.csproj b/src/Pixeval.CoreApi/Pixeval.CoreApi.csproj index 8e074075..d2e0456c 100644 --- a/src/Pixeval.CoreApi/Pixeval.CoreApi.csproj +++ b/src/Pixeval.CoreApi/Pixeval.CoreApi.csproj @@ -6,13 +6,13 @@ true enable preview + true - diff --git a/src/Pixeval.CoreApi/Preference/Session.cs b/src/Pixeval.CoreApi/Preference/Session.cs index ff82d481..6b5a88f6 100644 --- a/src/Pixeval.CoreApi/Preference/Session.cs +++ b/src/Pixeval.CoreApi/Preference/Session.cs @@ -19,8 +19,8 @@ #endregion using System; +using System.Text.Json; using System.Text.Json.Serialization; -using Pixeval.Utilities; namespace Pixeval.CoreApi.Preference; @@ -46,5 +46,5 @@ public record Session( [property: JsonPropertyName("account")] string Account, [property: JsonPropertyName("isPremium")] bool IsPremium) { - public override string? ToString() => this.ToJson(); + public override string ToString() => JsonSerializer.Serialize(this, typeof(Session), AppJsonSerializerContext.Default); } diff --git a/src/Pixeval.SourceGen/FactoryGenerator.cs b/src/Pixeval.SourceGen/FactoryGenerator.cs index 5953a872..b744ba69 100644 --- a/src/Pixeval.SourceGen/FactoryGenerator.cs +++ b/src/Pixeval.SourceGen/FactoryGenerator.cs @@ -32,8 +32,12 @@ public class FactoryGenerator : IIncrementalGenerator IdentifierName(symbol.Name), syntax.Initializer is { } init ? init.Value - : symbol.Type.NullableAnnotation is NullableAnnotation.NotAnnotated && symbol.Type.GetAttributes().Any(i => i.AttributeClass?.MetadataName == AttributeName) - ? InvocationExpression(symbol.Type.GetStaticMemberAccessExpression(createDefault)) + : symbol.Type.NullableAnnotation is NullableAnnotation.NotAnnotated && symbol.Type + .GetAttributes().Any(i => i.AttributeClass?.MetadataName == AttributeName) + ? InvocationExpression(MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(symbol.Type.ToDisplayString()), + IdentifierName(createDefault))) : DefaultExpression(symbol.Type.GetTypeSyntax(false))); }); @@ -70,12 +74,10 @@ public class FactoryGenerator : IIncrementalGenerator AttributeFullName, (_, _) => true, (syntaxContext, _) => syntaxContext - ).Combine(context.CompilationProvider); + ); - context.RegisterSourceOutput(generatorAttributes, (spc, tuple) => + context.RegisterSourceOutput(generatorAttributes, (spc, ga) => { - var (ga, compilation) = tuple; - if (ga.TargetSymbol is not INamedTypeSymbol symbol) return; diff --git a/src/Pixeval.SourceGen/LocalizationMetadataGenerator.cs b/src/Pixeval.SourceGen/LocalizationMetadataGenerator.cs new file mode 100644 index 00000000..f2af42c4 --- /dev/null +++ b/src/Pixeval.SourceGen/LocalizationMetadataGenerator.cs @@ -0,0 +1,313 @@ +#region Copyright +// GPL v3 License +// +// Pixeval/Pixeval.SourceGen +// Copyright (c) 2024 Pixeval.SourceGen/EnumLocalizationMetadataGenerator.cs +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +#endregion + +using Microsoft.CodeAnalysis; +using static Pixeval.SourceGen.SyntaxHelper; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp; +using System.Collections.Immutable; +using System.Linq; +using System.Text; +using System.Collections.Generic; + +namespace Pixeval.SourceGen; + +public abstract class LocalizationMetadataGeneratorBase +{ + public static ClassDeclarationSyntax GetClassDeclaration(string typeFullName, string name, string resourceTypeName, List<(string Name, string ResourceName)> members, bool isPartial) + { + const string stringRepresentableItemName = "global::Pixeval.Controls.StringRepresentableItem"; + const string valuesTable = "ValuesTable"; + const string getResource = "GetResource"; + return ClassDeclaration(name) + .AddModifiers(Token(SyntaxKind.StaticKeyword), + isPartial ? Token(SyntaxKind.PartialKeyword) : Token(SyntaxKind.PublicKeyword)) + .WithOpenBraceToken(Token(SyntaxKind.OpenBraceToken)) + .AddMembers( + MethodDeclaration( + ParseTypeName( + $"global::System.Collections.Generic.IReadOnlyList<{stringRepresentableItemName}>"), + "GetItems") + .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword))) + .WithBody( + Block(List([ + LocalDeclarationStatement( + VariableDeclaration(ParseTypeName("var"), SeparatedList([ + VariableDeclarator("array").WithInitializer(EqualsValueClause( + ArrayCreationExpression( + ArrayType( + ParseTypeName(stringRepresentableItemName), + List([ + ArrayRankSpecifier(SeparatedList([ + (ExpressionSyntax)MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(valuesTable), + IdentifierName("Count")) + ])) + ]))))) + ]))), + LocalDeclarationStatement( + VariableDeclaration(ParseTypeName("var"), SeparatedList([ + VariableDeclarator("i").WithInitializer(EqualsValueClause( + LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(0)))) + ]))), + ForEachStatement(ParseTypeName("var"), Identifier("t"), IdentifierName(valuesTable), + Block(List([ + ExpressionStatement( + AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, + ElementAccessExpression(IdentifierName("array"), + BracketedArgumentList(SeparatedList([Argument(IdentifierName("i"))]))), + ObjectCreationExpression(ParseTypeName(stringRepresentableItemName), + ArgumentList(SeparatedList([ + Argument(MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName("t"), + IdentifierName("Key"))), + Argument(InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(resourceTypeName), + IdentifierName(getResource)), + ArgumentList(SeparatedList([ + Argument(MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName("t"), + IdentifierName("Value"))) + ])))) + ])), null))), + ExpressionStatement(PrefixUnaryExpression(SyntaxKind.PreIncrementExpression, + IdentifierName("i"))) + ]))), + ReturnStatement(IdentifierName("array")) + ]))), + MethodDeclaration(ParseTypeName("string"), getResource) + .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword))) + .WithParameterList(ParameterList(SeparatedList([ + Parameter(Identifier("id")).WithType(ParseTypeName("string")) + ]))) + .WithExpressionBody( + ArrowExpressionClause( + InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(resourceTypeName), IdentifierName(getResource)), + ArgumentList(SeparatedList([ + Argument(IdentifierName("id")) + ]))))) + .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)), + MethodDeclaration(ParseTypeName("string"), getResource) + .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword))) + .WithParameterList(ParameterList(SeparatedList([ + Parameter(Identifier("value")).WithType(ParseTypeName(typeFullName)) + ]))) + .WithExpressionBody( + ArrowExpressionClause( + InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(resourceTypeName), IdentifierName(getResource)), + ArgumentList(SeparatedList([ + Argument(ElementAccessExpression( + IdentifierName(valuesTable), BracketedArgumentList(SeparatedList([ + Argument(IdentifierName("value")) + ])))) + ]))))) + .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)), + PropertyDeclaration( + ParseTypeName("global::System.Collections.Frozen.FrozenDictionary"), + "NamesTable") + .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword))) + .WithExpressionBody(ArrowExpressionClause(InvocationExpression(MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName("global::System.Collections.Frozen.FrozenDictionary"), + IdentifierName("ToFrozenDictionary")), + ArgumentList(SeparatedList([ + Argument( + ObjectCreationExpression( + ParseTypeName("global::System.Collections.Generic.Dictionary"), + null, + InitializerExpression(SyntaxKind.ObjectInitializerExpression, + SeparatedList(members.Select(member => + (ExpressionSyntax)AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + ImplicitElementAccess(BracketedArgumentList(SeparatedList( + [Argument(NameOfExpression(member.Name))] + ))), + NameOfExpression(MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(resourceTypeName), + IdentifierName(member.ResourceName))))))))) + ]))))) + .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)), + PropertyDeclaration( + ParseTypeName($"{FrozenDictionaryTypeName}<{typeFullName}, string>"), + valuesTable) + .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword))) + .WithExpressionBody(ArrowExpressionClause(InvocationExpression(MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(FrozenDictionaryTypeName), + IdentifierName("ToFrozenDictionary")), + ArgumentList(SeparatedList([ + Argument( + ObjectCreationExpression( + ParseTypeName( + $"global::System.Collections.Generic.Dictionary<{typeFullName}, string>"), + null, + InitializerExpression(SyntaxKind.ObjectInitializerExpression, + SeparatedList(members.Select(member => + (ExpressionSyntax)AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + ImplicitElementAccess(BracketedArgumentList(SeparatedList( + [Argument(IdentifierName(member.Name))] + ))), + NameOfExpression(MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(resourceTypeName), + IdentifierName(member.ResourceName))))))))) + ]))))) + .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))) + .WithCloseBraceToken(Token(SyntaxKind.CloseBraceToken)); + } +} + +[Generator] +public class LocalizationMetadataGenerator : LocalizationMetadataGeneratorBase, IIncrementalGenerator +{ + private const string AttributeName = "LocalizationMetadataAttribute"; + + private const string SubAttributeName = "LocalizedResourceAttribute"; + + private const string AttributeNamespace = nameof(Pixeval) + ".Attributes"; + + private const string AttributeFullName = AttributeNamespace + "." + AttributeName; + + private const string SubAttributeFullName = AttributeNamespace + "." + SubAttributeName; + + internal string? TypeWithAttribute(INamedTypeSymbol typeSymbol, ImmutableArray attributeList) + { + if (attributeList is not [{ ConstructorArguments: [{ Value: INamedTypeSymbol resourceType }, ..] }]) + return null; + + var isPartial = attributeList is [{ NamedArguments: [{ Key: "IsPartial", Value.Value: true }] }]; + + List<(string Name, string ResourceName)> members = []; + + foreach (var member in typeSymbol.GetMembers()) + { + if (member is not IFieldSymbol field) + continue; + + if (field.GetAttribute(SubAttributeFullName) is not { ConstructorArguments: [_, { Value: string resourceName }, ..] }) + continue; + + members.Add((typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + "." + field.Name, resourceName)); + } + + var generatedType = GetClassDeclaration(typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), $"{typeSymbol.Name}Extension", resourceType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), members, isPartial); + + var generatedNamespace = GetFileScopedNamespaceDeclaration(typeSymbol.ContainingNamespace.ToDisplayString(), generatedType, true); + var compilationUnit = CompilationUnit() + .AddMembers(generatedNamespace) + .NormalizeWhitespace(); + return SyntaxTree(compilationUnit, encoding: Encoding.UTF8).GetText().ToString(); + } + + public void Initialize(IncrementalGeneratorInitializationContext context) + { + var generatorAttributes = context.SyntaxProvider.ForAttributeWithMetadataName( + AttributeFullName, + (_, _) => true, + (syntaxContext, _) => syntaxContext + ); + + context.RegisterSourceOutput(generatorAttributes, (spc, ga) => + { + if (ga.TargetSymbol is not INamedTypeSymbol symbol) + return; + + if (TypeWithAttribute(symbol, ga.Attributes) is { } source) + spc.AddSource( + // 不能重名 + $"{symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat.WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted))}_{AttributeFullName}.g.cs", + source); + }); + } +} + +[Generator] +public class AttachedLocalizationMetadataGenerator : LocalizationMetadataGeneratorBase, IIncrementalGenerator +{ + private const string AttributeName = "AttachedLocalizationMetadataAttribute`1"; + + private const string SubAttributeName = "AttachedLocalizedResourceAttribute"; + + private const string AttributeNamespace = nameof(Pixeval) + ".Attributes"; + + private const string AttributeFullName = AttributeNamespace + "." + AttributeName; + + private const string SubAttributeFullName = AttributeNamespace + "." + SubAttributeName; + + internal string? TypeWithAttribute(INamedTypeSymbol typeSymbol, ImmutableArray attributeList) + { + if (attributeList is not [{ ConstructorArguments: [{ Value: INamedTypeSymbol resourceType }, ..], AttributeClass.TypeArguments: [INamedTypeSymbol attachedType, ..] }]) + return null; + + List<(string Name, string ResourceName)> members = []; + + foreach (var attribute in typeSymbol.GetAttributes()) + { + if (attribute.AttributeClass?.ToDisplayString() is not SubAttributeFullName) + continue; + + if (attribute is not { ConstructorArguments: [{ Value: string fieldName }, { Value: string resourceName }, ..] }) + continue; + + members.Add((attachedType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + "." + fieldName, resourceName)); + } + + var generatedType = GetClassDeclaration(attachedType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), typeSymbol.Name, resourceType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), members, true); + + var generatedNamespace = GetFileScopedNamespaceDeclaration(typeSymbol.ContainingNamespace.ToDisplayString(), generatedType, true); + var compilationUnit = CompilationUnit() + .AddMembers(generatedNamespace) + .NormalizeWhitespace(); + return SyntaxTree(compilationUnit, encoding: Encoding.UTF8).GetText().ToString(); + } + + public void Initialize(IncrementalGeneratorInitializationContext context) + { + var generatorAttributes = context.SyntaxProvider.ForAttributeWithMetadataName( + AttributeFullName, + (_, _) => true, + (syntaxContext, _) => syntaxContext + ); + + context.RegisterSourceOutput(generatorAttributes, (spc, ga) => + { + if (ga.TargetSymbol is not INamedTypeSymbol symbol) + return; + + if (TypeWithAttribute(symbol, ga.Attributes) is { } source) + spc.AddSource( + // 不能重名 + $"{symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat.WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted))}_{AttributeFullName}.g.cs", + source); + }); + } +} diff --git a/src/Pixeval.SourceGen/MetaPathMacroGenerator.cs b/src/Pixeval.SourceGen/MetaPathMacroGenerator.cs new file mode 100644 index 00000000..dab0bd25 --- /dev/null +++ b/src/Pixeval.SourceGen/MetaPathMacroGenerator.cs @@ -0,0 +1,97 @@ +#region Copyright +// GPL v3 License +// +// Pixeval/Pixeval.SourceGen +// Copyright (c) 2024 Pixeval.SourceGen/MetaPathMacroGenerator.cs +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +#endregion + +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Text; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp; +using static Pixeval.SourceGen.SyntaxHelper; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; + +namespace Pixeval.SourceGen; + +[Generator] +public class MetaPathMacroGenerator : IIncrementalGenerator +{ + private const string AttributeName = "MetaPathMacroAttribute`1"; + + private const string AttributeNamespace = nameof(Pixeval) + ".Download.Macros"; + + private const string AttributeFullName = AttributeNamespace + "." + AttributeName; + + internal string TypeWithAttribute(ImmutableArray gas, out string fileName) + { + var dictionary = new Dictionary>(SymbolEqualityComparer.Default); + foreach (var ga in gas) + { + if (ga is not { TargetSymbol: INamedTypeSymbol symbol, Attributes: var attributeList }) + continue; + foreach (var attributeData in attributeList) + if (attributeData.AttributeClass?.TypeArguments is [{ } type]) + if (dictionary.TryGetValue(type, out var list)) + list.Add(symbol); + else + dictionary[type] = [symbol]; + } + + const string getAttachedTypeInstances = "Get{0}Instances"; + + fileName = "MetaPathMacroAttributeHelper.g.cs"; + var generatedType = ClassDeclaration("MetaPathMacroAttributeHelper") + .AddModifiers(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword)) + .WithOpenBraceToken(Token(SyntaxKind.OpenBraceToken)) + .AddMembers(dictionary.Select(t => + (MemberDeclarationSyntax)MethodDeclaration( + ParseTypeName("global::System.Collections.Generic.IReadOnlyList"), + string.Format(getAttachedTypeInstances, t.Key.Name)) + .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword))) + .WithExpressionBody(ArrowExpressionClause(CollectionExpression(SeparatedList(t.Value.Select(v => + (CollectionElementSyntax)ExpressionElement(ObjectCreationExpression(v.GetTypeSyntax(false)) + .WithArgumentList(ArgumentList())))) + ))) + .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))).ToArray() + ) + .WithCloseBraceToken(Token(SyntaxKind.CloseBraceToken)); + var generatedNamespace = GetFileScopedNamespaceDeclaration(AttributeNamespace, generatedType, true); + var compilationUnit = CompilationUnit() + .AddMembers(generatedNamespace) + .NormalizeWhitespace(); + return SyntaxTree(compilationUnit, encoding: Encoding.UTF8).GetText().ToString(); + } + + public void Initialize(IncrementalGeneratorInitializationContext context) + { + var generatorAttributes = context.SyntaxProvider.ForAttributeWithMetadataName( + AttributeFullName, + (_, _) => true, + (syntaxContext, _) => syntaxContext + ).Collect(); + + context.RegisterSourceOutput(generatorAttributes, (spc, gas) => + { + if (gas.Length > 0) + if (TypeWithAttribute(gas, out var name) is { } source) + spc.AddSource(name, source); + }); + } +} diff --git a/src/Pixeval.SourceGen/Pixeval.SourceGen.csproj b/src/Pixeval.SourceGen/Pixeval.SourceGen.csproj index 72a8ba69..baab2964 100644 --- a/src/Pixeval.SourceGen/Pixeval.SourceGen.csproj +++ b/src/Pixeval.SourceGen/Pixeval.SourceGen.csproj @@ -6,16 +6,17 @@ true Generated true - AnyCPU - Debug;Release + Debug;Release + AnyCPU latest + false - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + - + \ No newline at end of file diff --git a/src/Pixeval.SourceGen/SettingsEntryGenerator.cs b/src/Pixeval.SourceGen/SettingsEntryGenerator.cs new file mode 100644 index 00000000..8f6f982d --- /dev/null +++ b/src/Pixeval.SourceGen/SettingsEntryGenerator.cs @@ -0,0 +1,123 @@ +#region Copyright + +// GPL v3 License +// +// Pixeval/Pixeval.SourceGen +// Copyright (c) 2024 Pixeval.SourceGen/SettingsEntryGenerator.cs +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#endregion + +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Text; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using static Pixeval.SourceGen.SyntaxHelper; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; + +namespace Pixeval.SourceGen; + +// [Generator] +public class SettingsEntryGenerator : IIncrementalGenerator +{ + private const string AttributeName = "SettingsEntryAttribute"; + + private const string AttributeNamespace = nameof(Pixeval) + ".Attributes"; + + private const string AttributeFullName = AttributeNamespace + "." + AttributeName; + + internal string TypeWithAttribute(ImmutableArray gas, out string fileName) + { + var list = new List<(IPropertySymbol Property, (int, string, string) Attribute)>(); + foreach (var ga in gas) + if (ga is + { + TargetSymbol: IPropertySymbol symbol, + Attributes: [{ ConstructorArguments: [{ Value: int a }, { Value: string b }, { Value: string c }] }] + }) + list.Add((symbol, (a, b, c))); + + const string metadataTable = "MetadataTable"; + + fileName = "SettingsEntryAttribute.g.cs"; + var generatedType = ClassDeclaration("SettingsEntryAttribute") + .AddModifiers(Token(SyntaxKind.PartialKeyword)) + .WithOpenBraceToken(Token(SyntaxKind.OpenBraceToken)) + .AddMembers( + PropertyDeclaration( + ParseTypeName($"{FrozenDictionaryTypeName}"), + metadataTable) + .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword))) + .WithExpressionBody(ArrowExpressionClause(InvocationExpression(MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(FrozenDictionaryTypeName), + IdentifierName("ToFrozenDictionary")), + ArgumentList(SeparatedList([ + Argument( + ObjectCreationExpression( + ParseTypeName( + $"global::System.Collections.Generic.Dictionary"), + null, + InitializerExpression(SyntaxKind.ObjectInitializerExpression, + SeparatedList(list.Select(elem => + (ExpressionSyntax)AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + ImplicitElementAccess(BracketedArgumentList(SeparatedList( + [ + Argument(NameOfExpression( + elem.Property.ContainingType.ToDisplayString( + SymbolDisplayFormat.FullyQualifiedFormat) + "." + + elem.Property.Name)) + ] + ))), + ObjectCreationExpression(ParseTypeName("global::" + AttributeFullName)) + .WithArgumentList(ArgumentList(SeparatedList([ + Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, + Literal(elem.Attribute.Item1))), + Argument(LiteralExpression(SyntaxKind.StringLiteralExpression, + Literal(elem.Attribute.Item2))), + Argument(LiteralExpression(SyntaxKind.StringLiteralExpression, + Literal(elem.Attribute.Item3))) + ]))))))))) + ]))))) + .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)) + ) + .WithCloseBraceToken(Token(SyntaxKind.CloseBraceToken)); + var generatedNamespace = GetFileScopedNamespaceDeclaration(AttributeNamespace, generatedType, true); + var compilationUnit = CompilationUnit() + .AddMembers(generatedNamespace) + .NormalizeWhitespace(); + return SyntaxTree(compilationUnit, encoding: Encoding.UTF8).GetText().ToString(); + } + + public void Initialize(IncrementalGeneratorInitializationContext context) + { + var generatorAttributes = context.SyntaxProvider.ForAttributeWithMetadataName( + AttributeFullName, + (_, _) => true, + (syntaxContext, _) => syntaxContext + ).Collect(); + + context.RegisterSourceOutput(generatorAttributes, (spc, gas) => + { + if (gas.Length > 0) + if (TypeWithAttribute(gas, out var name) is { } source) + spc.AddSource(name, source); + }); + } +} diff --git a/src/Pixeval.SourceGen/SyntaxHelper.cs b/src/Pixeval.SourceGen/SyntaxHelper.cs index 7ecf37f7..17ae2481 100644 --- a/src/Pixeval.SourceGen/SyntaxHelper.cs +++ b/src/Pixeval.SourceGen/SyntaxHelper.cs @@ -32,6 +32,7 @@ public static class SyntaxHelper { internal const string AttributeNamespace = "WinUI3Utilities.Attributes."; internal const string DisableSourceGeneratorAttribute = AttributeNamespace + "DisableSourceGeneratorAttribute"; + internal const string FrozenDictionaryTypeName = "global::System.Collections.Frozen.FrozenDictionary"; public static bool HasAttribute(this ISymbol s, string attributeFqName) { @@ -43,19 +44,6 @@ public static class SyntaxHelper return mds.GetAttributes().FirstOrDefault(attr => attr?.AttributeClass?.ToDisplayString() == attributeFqName); } - /// - /// 缩进(伪tab) - /// - /// tab数量 - /// 4n个space - internal static string Spacing(int n) - { - var temp = ""; - for (var i = 0; i < n; ++i) - temp += " "; - return temp; - } - /// /// Generate the following code /// @@ -69,13 +57,23 @@ public static class SyntaxHelper return isNullable ? NullableType(typeName) : typeName; } - internal static MemberAccessExpressionSyntax GetStaticMemberAccessExpression(this ITypeSymbol typeSymbol, string name) - { - return MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - IdentifierName(typeSymbol.ToDisplayString()), - IdentifierName(name)); - } + /// + /// Generate the following code + /// + /// nameof() + /// + /// + /// NameOfExpression + internal static InvocationExpressionSyntax NameOfExpression(string name) => NameOfExpression(IdentifierName(name)); + + /// + /// Generate the following code + /// + /// nameof() + /// + /// + /// NameOfExpression + internal static InvocationExpressionSyntax NameOfExpression(ExpressionSyntax expressionSyntax) => InvocationExpression(IdentifierName("nameof"), ArgumentList().AddArguments(Argument(expressionSyntax))); internal static IEnumerable GetProperties(this ITypeSymbol typeSymbol, INamedTypeSymbol attribute) { @@ -114,31 +112,6 @@ public static class SyntaxHelper return false; } - /// - /// 获取某的namespace并加入集合 - /// - /// namespaces集合 - /// 已判断过的types - /// 上下文所在的类 - /// type的symbol - internal static void UseNamespace(this HashSet namespaces, HashSet usedTypes, INamedTypeSymbol contextType, ITypeSymbol symbol) - { - if (usedTypes.Contains(symbol)) - return; - - _ = usedTypes.Add(symbol); - - if (symbol.ContainingNamespace is not { } ns) - return; - - if (!SymbolEqualityComparer.Default.Equals(ns, contextType.ContainingNamespace)) - _ = namespaces.Add(ns.ToDisplayString()); - - if (symbol is INamedTypeSymbol { IsGenericType: true } genericSymbol) - foreach (var a in genericSymbol.TypeArguments) - namespaces.UseNamespace(usedTypes, contextType, a); - } - /// /// Generate the following code /// @@ -163,21 +136,21 @@ public static class SyntaxHelper /// /// Generate the following code /// - /// partial
+ /// partial
/// {
///
/// } ///
///
/// TypeDeclaration - internal static TypeDeclarationSyntax GetDeclaration(string name, INamedTypeSymbol symbol, params MemberDeclarationSyntax[] member) + internal static TypeDeclarationSyntax GetDeclaration(string name, INamedTypeSymbol originalSymbol, params MemberDeclarationSyntax[] member) { - TypeDeclarationSyntax typeDeclarationTemp = symbol.TypeKind switch + TypeDeclarationSyntax typeDeclarationTemp = originalSymbol.TypeKind switch { - TypeKind.Class when !symbol.IsRecord => ClassDeclaration(name), - TypeKind.Struct when !symbol.IsRecord => StructDeclaration(name), - TypeKind.Class or TypeKind.Struct when symbol.IsRecord => RecordDeclaration(Token(SyntaxKind.RecordKeyword), name), - _ => throw new ArgumentOutOfRangeException(nameof(symbol.TypeKind)) + TypeKind.Class when !originalSymbol.IsRecord => ClassDeclaration(name), + TypeKind.Struct when !originalSymbol.IsRecord => StructDeclaration(name), + TypeKind.Class or TypeKind.Struct when originalSymbol.IsRecord => RecordDeclaration(Token(SyntaxKind.RecordKeyword), name), + _ => throw new ArgumentOutOfRangeException(nameof(originalSymbol.TypeKind)) }; return typeDeclarationTemp.AddModifiers(Token(SyntaxKind.PartialKeyword)) .WithOpenBraceToken(Token(SyntaxKind.OpenBraceToken)) diff --git a/src/Pixeval.Utilities/AsyncLazy.cs b/src/Pixeval.Utilities/AsyncLazy.cs index 27090147..d3672b8d 100644 --- a/src/Pixeval.Utilities/AsyncLazy.cs +++ b/src/Pixeval.Utilities/AsyncLazy.cs @@ -20,6 +20,7 @@ using System; using System.Runtime.CompilerServices; +using System.Threading; using System.Threading.Tasks; namespace Pixeval.Utilities; @@ -37,6 +38,8 @@ public class AsyncLazy ///
private T? _value; + private readonly SemaphoreSlim _slimLock = new(1, 1); + public AsyncLazy(T value) { _value = value; @@ -52,7 +55,18 @@ public class AsyncLazy public ValueTask ValueAsync => ValueAsyncInternal(); [MethodImpl(MethodImplOptions.AggressiveInlining)] - private async ValueTask ValueAsyncInternal() => IsValueCreated ? _value! : await CreateValueAsync(); + private async ValueTask ValueAsyncInternal() + { + await _slimLock.WaitAsync(); + try + { + return IsValueCreated ? _value! : await CreateValueAsync(); + } + finally + { + _ = _slimLock.Release(); + } + } private async Task CreateValueAsync() { diff --git a/src/Pixeval.Utilities/Description.cs b/src/Pixeval.Utilities/Description.cs index 009a02d5..61fbc0f1 100644 --- a/src/Pixeval.Utilities/Description.cs +++ b/src/Pixeval.Utilities/Description.cs @@ -20,13 +20,14 @@ using System; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Reflection; namespace Pixeval.Utilities; public static class DescriptionHelper { - public static string GetDescription(this TEnum @enum) where TEnum : Enum + public static string GetDescription<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] TEnum>(this TEnum @enum) where TEnum : Enum { return (typeof(TEnum).GetField(@enum.ToString())?.GetCustomAttribute(typeof(DescriptionAttribute)) as DescriptionAttribute)?.Description ?? ThrowUtils.InvalidOperation("Attribute not found"); } diff --git a/src/Pixeval.Utilities/Objects.cs b/src/Pixeval.Utilities/Objects.cs index e6d94493..9f048288 100644 --- a/src/Pixeval.Utilities/Objects.cs +++ b/src/Pixeval.Utilities/Objects.cs @@ -19,7 +19,6 @@ #endregion using System; -using System.Buffers; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; @@ -28,10 +27,8 @@ using System.Net.Http; using System.Runtime.CompilerServices; using System.Security.Cryptography; using System.Text; -using System.Text.Json; using System.Text.RegularExpressions; using System.Threading.Tasks; -using CommunityToolkit.HighPerformance; using CommunityToolkit.HighPerformance.Buffers; namespace Pixeval.Utilities; @@ -118,39 +115,6 @@ public static class Objects return bytes.Select(b => b.ToString("x2")).Aggregate(string.Concat); } - public static async Task ToJsonAsync(this TEntity? obj, Action? serializerOptionConfigure = null) - { - if (obj is null) - { - return null; - } - - await using var memoryStream = new MemoryStream(); - await JsonSerializer.SerializeAsync(memoryStream, obj, new JsonSerializerOptions().Apply(option => serializerOptionConfigure?.Invoke(option))).ConfigureAwait(false); - return memoryStream.ToArray().GetString(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string? ToJson(this object? obj, Action? serializerOptionConfigure = null) - { - return obj?.Let(o => JsonSerializer.Serialize(o, new JsonSerializerOptions().Apply(option => serializerOptionConfigure?.Invoke(option)))); - } - - public static async ValueTask FromJsonAsync(this IMemoryOwner bytes, Action? serializerOptionConfigure = null) - { - using (bytes) - { - await using var stream = bytes.Memory.AsStream(); - return await JsonSerializer.DeserializeAsync(stream, new JsonSerializerOptions().Apply(option => serializerOptionConfigure?.Invoke(option))).ConfigureAwait(false); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TEntity? FromJson(this string str, Action? serializerOptionConfigure = null) - { - return JsonSerializer.Deserialize(str, new JsonSerializerOptions().Apply(option => serializerOptionConfigure?.Invoke(option))); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool EqualsIgnoreCase(this string str1, string str2) { diff --git a/src/Pixeval.Utilities/Pixeval.Utilities.csproj b/src/Pixeval.Utilities/Pixeval.Utilities.csproj index 3c61edfd..9c646280 100644 --- a/src/Pixeval.Utilities/Pixeval.Utilities.csproj +++ b/src/Pixeval.Utilities/Pixeval.Utilities.csproj @@ -5,6 +5,7 @@ x86;x64;arm64 enable preview + true diff --git a/src/Pixeval.Utilities/ThrowUtils.cs b/src/Pixeval.Utilities/ThrowUtils.cs index 1f4a3b15..0578ec58 100644 --- a/src/Pixeval.Utilities/ThrowUtils.cs +++ b/src/Pixeval.Utilities/ThrowUtils.cs @@ -39,8 +39,8 @@ public static class ThrowUtils [DoesNotReturn] [MethodImpl(MethodImplOptions.NoInlining)] - public static TResult Throw(params object[] parameters) where TException : Exception - => throw ((Exception)Activator.CreateInstance(typeof(TException), parameters)!); + public static TResult Throw(TException e) where TException : Exception + => throw e; /// [DoesNotReturn] diff --git a/src/Pixeval/App.xaml.cs b/src/Pixeval/App.xaml.cs index 200c3b37..b33304e4 100644 --- a/src/Pixeval/App.xaml.cs +++ b/src/Pixeval/App.xaml.cs @@ -55,6 +55,7 @@ public partial class App public App() { _ = this.UseSegoeMetrics(); + SettingsValueConverter.Context = SettingsSerializeContext.Default; AppViewModel = new AppViewModel(this); BookmarkTag.AllCountedTagString = MiscResources.AllCountedTagName; AppInfo.SetNameResolvers(AppViewModel.AppSettings); diff --git a/src/Pixeval/AppManagement/AppSettings.cs b/src/Pixeval/AppManagement/AppSettings.cs index 9cf26580..d3240e43 100644 --- a/src/Pixeval/AppManagement/AppSettings.cs +++ b/src/Pixeval/AppManagement/AppSettings.cs @@ -22,6 +22,7 @@ using System; using System.Collections.Generic; using System.Globalization; using System.IO; +using System.Text.Json.Serialization; using Windows.Foundation; using Microsoft.UI.Composition.SystemBackdrops; using Microsoft.UI.Xaml; @@ -301,3 +302,7 @@ public partial record AppSettings() : IWindowSettings : $"@{{if_illust={picPath}}}@{{if_novel={docPath}}}"; } } + +[JsonSerializable(typeof(string[]))] +[JsonSerializable(typeof(HashSet))] +public partial class SettingsSerializeContext : JsonSerializerContext; diff --git a/src/Pixeval/AppManagement/Versioning.cs b/src/Pixeval/AppManagement/Versioning.cs index 5638a087..c0a1f987 100644 --- a/src/Pixeval/AppManagement/Versioning.cs +++ b/src/Pixeval/AppManagement/Versioning.cs @@ -49,7 +49,7 @@ public class Versioning AppReleaseModels = null; if (client.DefaultRequestHeaders.UserAgent.Count is 0) client.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 Edg/121.0.0.0"); - if (await client.GetFromJsonAsync("https://api.github.com/repos/Pixeval/Pixeval/releases") is { Length: > 0 } gitHubReleases) + if (await client.GetFromJsonAsync("https://api.github.com/repos/Pixeval/Pixeval/releases", typeof(GitHubRelease[]), GitHubReleaseSerializeContext.Default) is GitHubRelease[] { Length: > 0 } gitHubReleases) { var appReleaseModels = new List(gitHubReleases.Length); foreach (var release in gitHubReleases) @@ -102,7 +102,11 @@ public record AppReleaseModel( } } -file class GitHubRelease +[JsonSerializable(typeof(GitHubRelease[]))] +[JsonSerializable(typeof(Assets[]))] +public partial class GitHubReleaseSerializeContext : JsonSerializerContext; + +public class GitHubRelease { [JsonPropertyName("tag_name")] public required string TagName { get; init; } @@ -114,7 +118,7 @@ file class GitHubRelease public required string Notes { get; init; } } -file class Assets +public class Assets { [JsonPropertyName("browser_download_url")] public required string BrowserDownloadUrl { get; init; } diff --git a/src/Pixeval/AppViewModel.cs b/src/Pixeval/AppViewModel.cs index 7f8153dc..61de4cde 100644 --- a/src/Pixeval/AppViewModel.cs +++ b/src/Pixeval/AppViewModel.cs @@ -37,7 +37,7 @@ using Windows.Storage; namespace Pixeval; -public class AppViewModel(App app) : IDisposable +public partial class AppViewModel(App app) : IDisposable { private bool _activatedByProtocol; diff --git a/src/Pixeval/Attributes/SettingsEntryAttribute.cs b/src/Pixeval/Attributes/SettingsEntryAttribute.cs index 65c5a4b7..e0b03c78 100644 --- a/src/Pixeval/Attributes/SettingsEntryAttribute.cs +++ b/src/Pixeval/Attributes/SettingsEntryAttribute.cs @@ -20,18 +20,23 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using FluentIcons.Common; using Pixeval.AppManagement; -using Pixeval.Util; using Pixeval.Utilities; using WinUI3Utilities; namespace Pixeval.Attributes; [AttributeUsage(AttributeTargets.Property)] -public class SettingsEntryAttribute(Symbol symbol, string resourceKeyHeader, string? resourceKeyDescription) : Attribute +public partial class SettingsEntryAttribute(Symbol symbol, string resourceKeyHeader, string? resourceKeyDescription) : Attribute { + public SettingsEntryAttribute(int symbol, string resourceKeyHeader, string? resourceKeyDescription) + : this((Symbol)symbol, resourceKeyHeader, resourceKeyDescription) + { + } + public static readonly SettingsEntryAttribute AutoUpdate = new(Symbol.Communication, nameof(SettingsPageResources.DownloadUpdateAutomaticallyEntryHeader), nameof(SettingsPageResources.DownloadUpdateAutomaticallyEntryDescription)); @@ -52,24 +57,22 @@ public class SettingsEntryAttribute(Symbol symbol, string resourceKeyHeader, str typeof(AppSettings).GetProperties(BindingFlags.Instance | BindingFlags.Public) .SelectNotNull(f => f.GetCustomAttribute())); - private static Type ResourceLoader => typeof(SettingsPageResources); - public Symbol Symbol { get; } = symbol; public string LocalizedResourceHeader { get; } = - LocalizedResourceAttributeHelper.GetLocalizedResourceContent(ResourceLoader, resourceKeyHeader) ?? ""; + SettingsPageResources.GetResource(resourceKeyHeader); public string LocalizedResourceDescription { get; } = resourceKeyDescription is null ? "" - : LocalizedResourceAttributeHelper.GetLocalizedResourceContent(ResourceLoader, resourceKeyDescription) ?? ""; + : SettingsPageResources.GetResource(resourceKeyDescription); public static SettingsEntryAttribute GetFromPropertyName(string propertyName) { return GetFromPropertyName(propertyName); } - public static SettingsEntryAttribute GetFromPropertyName(string propertyName) + public static SettingsEntryAttribute GetFromPropertyName<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(string propertyName) { return typeof(T).GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public) ?.GetCustomAttribute() ?? diff --git a/src/Pixeval/Controls/DocumentViewer/DocumentViewerViewModel.cs b/src/Pixeval/Controls/DocumentViewer/DocumentViewerViewModel.cs index fa344d72..4c0dc46c 100644 --- a/src/Pixeval/Controls/DocumentViewer/DocumentViewerViewModel.cs +++ b/src/Pixeval/Controls/DocumentViewer/DocumentViewerViewModel.cs @@ -42,7 +42,7 @@ using QuestPDF.Infrastructure; namespace Pixeval.Controls; -public class DocumentViewerViewModel(NovelContent novelContent) : ObservableObject, IDisposable, INovelParserViewModel, INovelParserViewModel +public partial class DocumentViewerViewModel(NovelContent novelContent) : ObservableObject, IDisposable, INovelParserViewModel, INovelParserViewModel { /// /// 需要从外部Invoke diff --git a/src/Pixeval/Controls/Download/DownloadItemDataProvider.cs b/src/Pixeval/Controls/Download/DownloadItemDataProvider.cs index 5bd335ac..6cfc9f37 100644 --- a/src/Pixeval/Controls/Download/DownloadItemDataProvider.cs +++ b/src/Pixeval/Controls/Download/DownloadItemDataProvider.cs @@ -7,7 +7,7 @@ using Pixeval.Download.Models; namespace Pixeval.Controls; -public class DownloadItemDataProvider : ObservableObject, IDisposable +public partial class DownloadItemDataProvider : ObservableObject, IDisposable { public AdvancedObservableCollection View { get; } = []; diff --git a/src/Pixeval/Controls/Download/DownloadItemViewModel.cs b/src/Pixeval/Controls/Download/DownloadItemViewModel.cs index c569aeaf..82ed931b 100644 --- a/src/Pixeval/Controls/Download/DownloadItemViewModel.cs +++ b/src/Pixeval/Controls/Download/DownloadItemViewModel.cs @@ -36,7 +36,7 @@ using Pixeval.CoreApi.Model; namespace Pixeval.Controls; /// -public sealed class DownloadItemViewModel : WorkEntryViewModel +public sealed partial class DownloadItemViewModel : WorkEntryViewModel { public DownloadTaskBase DownloadTask { get; } diff --git a/src/Pixeval/Controls/Download/DownloadListOption.cs b/src/Pixeval/Controls/Download/DownloadListOption.cs index 55bc4f0d..66f58032 100644 --- a/src/Pixeval/Controls/Download/DownloadListOption.cs +++ b/src/Pixeval/Controls/Download/DownloadListOption.cs @@ -22,6 +22,7 @@ using Pixeval.Attributes; namespace Pixeval.Controls; +[LocalizationMetadata(typeof(DownloadPageResources))] public enum DownloadListOption { [LocalizedResource(typeof(DownloadPageResources), nameof(DownloadPageResources.DownloadListOptionAllQueued))] diff --git a/src/Pixeval/Controls/Entry/EntryViewViewModel.cs b/src/Pixeval/Controls/Entry/EntryViewViewModel.cs index 24e29eb6..19fa7dbc 100644 --- a/src/Pixeval/Controls/Entry/EntryViewViewModel.cs +++ b/src/Pixeval/Controls/Entry/EntryViewViewModel.cs @@ -19,6 +19,7 @@ #endregion using System; +using System.Diagnostics.CodeAnalysis; using CommunityToolkit.Mvvm.ComponentModel; using Pixeval.Collections; using Pixeval.CoreApi.Engine; @@ -26,7 +27,7 @@ using Pixeval.CoreApi.Model; namespace Pixeval.Controls; -public abstract class EntryViewViewModel +public abstract class EntryViewViewModel : ObservableObject, IDisposable where T : class, IIdEntry where TViewModel : EntryViewModel, IViewModelFactory diff --git a/src/Pixeval/Controls/Entry/IDataProvider.cs b/src/Pixeval/Controls/Entry/IDataProvider.cs index 5f524f8b..42def2ff 100644 --- a/src/Pixeval/Controls/Entry/IDataProvider.cs +++ b/src/Pixeval/Controls/Entry/IDataProvider.cs @@ -20,6 +20,7 @@ using System; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using CommunityToolkit.WinUI.Collections; using Pixeval.Collections; using Pixeval.CoreApi.Engine; @@ -27,7 +28,7 @@ using Pixeval.CoreApi.Model; namespace Pixeval.Controls; -public interface IDataProvider +public interface IDataProvider : INotifyPropertyChanged, INotifyPropertyChanging, IDisposable where T : class, IIdEntry where TViewModel : class, IViewModelFactory diff --git a/src/Pixeval/Controls/Entry/SharableViewDataProvider.cs b/src/Pixeval/Controls/Entry/SharableViewDataProvider.cs index 92c1ec8b..c4fc5a83 100644 --- a/src/Pixeval/Controls/Entry/SharableViewDataProvider.cs +++ b/src/Pixeval/Controls/Entry/SharableViewDataProvider.cs @@ -21,6 +21,7 @@ #endregion using System; +using System.Diagnostics.CodeAnalysis; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.WinUI.Collections; using Pixeval.Collections; @@ -34,7 +35,7 @@ namespace Pixeval.Controls; /// 复用时调用会在所有复用对象都Dispose时Dispose
/// 初始化时调用 ///
-public class SharableViewDataProvider +public partial class SharableViewDataProvider : ObservableObject, IDataProvider, IDisposable where T : class, IIdEntry where TViewModel : EntryViewModel, IViewModelFactory, IDisposable diff --git a/src/Pixeval/Controls/Entry/SimpleViewDataProvider.cs b/src/Pixeval/Controls/Entry/SimpleViewDataProvider.cs index a13539f8..edc65d5a 100644 --- a/src/Pixeval/Controls/Entry/SimpleViewDataProvider.cs +++ b/src/Pixeval/Controls/Entry/SimpleViewDataProvider.cs @@ -19,6 +19,7 @@ #endregion using System; +using System.Diagnostics.CodeAnalysis; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.WinUI.Collections; using Pixeval.Collections; @@ -27,7 +28,7 @@ using Pixeval.CoreApi.Model; namespace Pixeval.Controls; -public class SimpleViewDataProvider : ObservableObject, IDataProvider +public partial class SimpleViewDataProvider : ObservableObject, IDataProvider where T : class, IIdEntry where TViewModel : class, IViewModelFactory, IDisposable { diff --git a/src/Pixeval/Controls/EnumComboBox.cs b/src/Pixeval/Controls/EnumComboBox.cs index 0fc81404..ec5b6b9b 100644 --- a/src/Pixeval/Controls/EnumComboBox.cs +++ b/src/Pixeval/Controls/EnumComboBox.cs @@ -25,14 +25,12 @@ using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Markup; using Pixeval.CoreApi.Global.Enum; -using Pixeval.Util; using WinUI3Utilities; using WinUI3Utilities.Attributes; namespace Pixeval.Controls; [DependencyProperty("SelectedEnum", DependencyPropertyDefaultValue.Default, nameof(OnSelectedEnumChanged), IsNullable = true)] -[DependencyProperty("EnumSource", DependencyPropertyDefaultValue.Default, nameof(OnEnumSourceChanged))] public sealed partial class EnumComboBox : ComboBox { public new event EventHandler? SelectionChanged; @@ -41,6 +39,12 @@ public sealed partial class EnumComboBox : ComboBox { Style = Application.Current.Resources["DefaultComboBoxStyle"] as Style; base.SelectionChanged += ComboBox_SelectionChanged; + var token = RegisterPropertyChangedCallback(ItemsSourceProperty, (sender, _) => + { + if (sender is EnumComboBox { ItemsSource: IEnumerable enumerable } box) + box.SelectedItem = enumerable.FirstOrDefault(); + }); + Unloaded += (sender, _) => sender.To().UnregisterPropertyChangedCallback(ItemsSourceProperty, token); } public bool RaiseEventAfterLoaded { get; set; } = true; @@ -65,33 +69,23 @@ public sealed partial class EnumComboBox : ComboBox var comboBox = sender.To(); comboBox.SelectedItem = comboBox.ItemsSource?.To>().FirstOrDefault(r => Equals(r.Item, comboBox.SelectedEnum)); } - - private static void OnEnumSourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) - { - var comboBox = sender.To(); - comboBox.ItemsSource = LocalizedResourceAttributeHelper.GetLocalizedResourceContents(comboBox.EnumSource); - if (comboBox.SelectedEnum is null) - comboBox.SelectedEnum = comboBox.EnumSource.GetValue(0); - else - comboBox.SelectedItem = comboBox.ItemsSource.To>().FirstOrDefault(r => Equals(r.Item, comboBox.SelectedEnum)); - } } -[MarkupExtensionReturnType(ReturnType = typeof(Array))] -public sealed class EnumValuesExtension : MarkupExtension +[MarkupExtensionReturnType(ReturnType = typeof(IReadOnlyList))] +public sealed partial class EnumValuesExtension : MarkupExtension { public EnumValuesEnum Type { get; set; } protected override object ProvideValue() { - return Enum.GetValues(Type switch + return Type switch { - EnumValuesEnum.WorkType => typeof(WorkType), - EnumValuesEnum.SimpleWorkType => typeof(SimpleWorkType), - EnumValuesEnum.WorkSortOption => typeof(WorkSortOption), - EnumValuesEnum.PrivacyPolicy => typeof(PrivacyPolicy), + EnumValuesEnum.WorkType => WorkTypeExtension.GetItems(), + EnumValuesEnum.SimpleWorkType => SimpleWorkTypeExtension.GetItems(), + EnumValuesEnum.WorkSortOption => WorkSortOptionExtension.GetItems(), + EnumValuesEnum.PrivacyPolicy => PrivacyPolicyExtension.GetItems(), _ => throw new ArgumentOutOfRangeException() - }); + }; } } diff --git a/src/Pixeval/Controls/FlyoutContent/CommentRepliesBlock.xaml.cs b/src/Pixeval/Controls/FlyoutContent/CommentRepliesBlock.xaml.cs index 948a9da5..555640fc 100644 --- a/src/Pixeval/Controls/FlyoutContent/CommentRepliesBlock.xaml.cs +++ b/src/Pixeval/Controls/FlyoutContent/CommentRepliesBlock.xaml.cs @@ -24,6 +24,7 @@ using System.Threading.Tasks; using CommunityToolkit.WinUI; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; +using Pixeval.CoreApi; using Pixeval.CoreApi.Global.Enum; using Pixeval.CoreApi.Net.Response; using WinUI3Utilities; @@ -79,7 +80,7 @@ public sealed partial class CommentRepliesBlock private async Task AddComment(HttpResponseMessage postCommentResponse) { - if (postCommentResponse.IsSuccessStatusCode && await postCommentResponse.Content.ReadFromJsonAsync() is { Comment: { } comment }) + if (postCommentResponse.IsSuccessStatusCode && await postCommentResponse.Content.ReadFromJsonAsync(typeof(PostCommentResponse), AppJsonSerializerContext.Default) is PostCommentResponse { Comment: { } comment }) ViewModel.AddComment(comment); } } diff --git a/src/Pixeval/Controls/Illustration/IllustrationViewViewModel.cs b/src/Pixeval/Controls/Illustration/IllustrationViewViewModel.cs index 97612017..6ed38b0e 100644 --- a/src/Pixeval/Controls/Illustration/IllustrationViewViewModel.cs +++ b/src/Pixeval/Controls/Illustration/IllustrationViewViewModel.cs @@ -25,7 +25,7 @@ using IllustrationViewDataProvider = Pixeval.Controls.SharableViewDataProvider< namespace Pixeval.Controls; -public sealed class IllustrationViewViewModel : SortableEntryViewViewModel +public sealed partial class IllustrationViewViewModel : SortableEntryViewViewModel { public IllustrationViewViewModel(IllustrationViewViewModel viewModel) : this(viewModel.DataProvider.CloneRef()) { diff --git a/src/Pixeval/Controls/Illustrator/IllustratorViewViewModel.cs b/src/Pixeval/Controls/Illustrator/IllustratorViewViewModel.cs index 3aafc2f8..f63e775e 100644 --- a/src/Pixeval/Controls/Illustrator/IllustratorViewViewModel.cs +++ b/src/Pixeval/Controls/Illustrator/IllustratorViewViewModel.cs @@ -25,7 +25,7 @@ using IllustratorViewDataProvider = Pixeval.Controls.SimpleViewDataProvider< namespace Pixeval.Controls; -public sealed class IllustratorViewViewModel : EntryViewViewModel +public sealed partial class IllustratorViewViewModel : EntryViewViewModel { public override IllustratorViewDataProvider DataProvider { get; } = new(); diff --git a/src/Pixeval/Controls/Novel/NovelItem.xaml b/src/Pixeval/Controls/Novel/NovelItem.xaml index 91ffe212..ef183ba9 100644 --- a/src/Pixeval/Controls/Novel/NovelItem.xaml +++ b/src/Pixeval/Controls/Novel/NovelItem.xaml @@ -16,6 +16,10 @@ PointerExited="NovelItem_OnPointerExited" mc:Ignorable="d"> + +public sealed partial class NovelViewViewModel : SortableEntryViewViewModel { public NovelViewViewModel(NovelViewViewModel viewModel) : this(viewModel.DataProvider.CloneRef()) { diff --git a/src/Pixeval/Controls/SettingRadioButtons.xaml.cs b/src/Pixeval/Controls/SettingRadioButtons.xaml.cs index 8fa1af7c..f041bf62 100644 --- a/src/Pixeval/Controls/SettingRadioButtons.xaml.cs +++ b/src/Pixeval/Controls/SettingRadioButtons.xaml.cs @@ -18,7 +18,6 @@ // along with this program. If not, see . #endregion -using System; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; @@ -26,13 +25,12 @@ using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Windows.Foundation; using CommunityToolkit.WinUI; -using Pixeval.Util; using WinUI3Utilities; using WinUI3Utilities.Attributes; namespace Pixeval.Controls; -[DependencyProperty("ItemsSource", DependencyPropertyDefaultValue.Default, nameof(OnItemsSourceChanged))] +[DependencyProperty("ItemsSource", DependencyPropertyDefaultValue.Default, nameof(OnItemsSourceChanged))] [DependencyProperty("SelectedItem", propertyChanged: nameof(OnSelectedItemChanged))] [DependencyProperty("Header")] public sealed partial class SettingRadioButtons : UserControl @@ -60,7 +58,7 @@ public sealed partial class SettingRadioButtons : UserControl { var buttons = sender.To(); - buttons.Buttons.ItemsSource = LocalizedResourceAttributeHelper.GetLocalizedResourceContents(buttons.ItemsSource); + buttons.Buttons.ItemsSource = buttons.ItemsSource; } private static void OnSelectedItemChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) diff --git a/src/Pixeval/Controls/Settings/EnumSettingsCard.xaml b/src/Pixeval/Controls/Settings/EnumSettingsCard.xaml index 66fc4331..d25e9735 100644 --- a/src/Pixeval/Controls/Settings/EnumSettingsCard.xaml +++ b/src/Pixeval/Controls/Settings/EnumSettingsCard.xaml @@ -16,7 +16,7 @@ diff --git a/src/Pixeval/Controls/Settings/ProxySettingsExpander.xaml b/src/Pixeval/Controls/Settings/ProxySettingsExpander.xaml index b154640b..404cdc56 100644 --- a/src/Pixeval/Controls/Settings/ProxySettingsExpander.xaml +++ b/src/Pixeval/Controls/Settings/ProxySettingsExpander.xaml @@ -16,7 +16,7 @@ diff --git a/src/Pixeval/Controls/Spotlight/SpotlightItemViewModel.cs b/src/Pixeval/Controls/Spotlight/SpotlightItemViewModel.cs index ab657b1c..70fd2ca3 100644 --- a/src/Pixeval/Controls/Spotlight/SpotlightItemViewModel.cs +++ b/src/Pixeval/Controls/Spotlight/SpotlightItemViewModel.cs @@ -24,7 +24,7 @@ using Pixeval.Util; namespace Pixeval.Controls; -public class SpotlightItemViewModel : ThumbnailEntryViewModel, IViewModelFactory +public partial class SpotlightItemViewModel : ThumbnailEntryViewModel, IViewModelFactory { public static SpotlightItemViewModel CreateInstance(Spotlight entry) => new(entry); diff --git a/src/Pixeval/Controls/Spotlight/SpotlightViewViewModel.cs b/src/Pixeval/Controls/Spotlight/SpotlightViewViewModel.cs index 2d3a2a64..5cc12612 100644 --- a/src/Pixeval/Controls/Spotlight/SpotlightViewViewModel.cs +++ b/src/Pixeval/Controls/Spotlight/SpotlightViewViewModel.cs @@ -25,7 +25,7 @@ using SpotlightViewDataProvider = Pixeval.Controls.SimpleViewDataProvider< namespace Pixeval.Controls; -public sealed class SpotlightViewViewModel : EntryViewViewModel +public sealed partial class SpotlightViewViewModel : EntryViewViewModel { public override SpotlightViewDataProvider DataProvider { get; } = new(); diff --git a/src/Pixeval/Controls/Work/SortableEntryViewViewModel.cs b/src/Pixeval/Controls/Work/SortableEntryViewViewModel.cs index c5752d56..d0317100 100644 --- a/src/Pixeval/Controls/Work/SortableEntryViewViewModel.cs +++ b/src/Pixeval/Controls/Work/SortableEntryViewViewModel.cs @@ -20,6 +20,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.WinUI.Collections; @@ -29,7 +30,7 @@ using Pixeval.Utilities; namespace Pixeval.Controls; -public abstract partial class SortableEntryViewViewModel +public abstract partial class SortableEntryViewViewModel : EntryViewViewModel, ISortableEntryViewViewModel where T : class, IWorkEntry where TViewModel : EntryViewModel, IViewModelFactory, IWorkViewModel diff --git a/src/Pixeval/Controls/Work/WorkView.xaml.cs b/src/Pixeval/Controls/Work/WorkView.xaml.cs index 3e145f3f..016aafd1 100644 --- a/src/Pixeval/Controls/Work/WorkView.xaml.cs +++ b/src/Pixeval/Controls/Work/WorkView.xaml.cs @@ -63,8 +63,7 @@ public sealed partial class WorkView : IEntryView { ArgumentNullException.ThrowIfNull(ViewModel); if (await viewModel.TryLoadThumbnailAsync(ViewModel)) - // TODO 不知道为什么NovelItem的Resource会有问题 - if (sender is IllustrationItem && sender.IsFullyOrPartiallyVisible(this)) + if (sender.IsFullyOrPartiallyVisible(this)) sender.GetResource("ThumbnailStoryboard").Begin(); else sender.Opacity = 1; diff --git a/src/Pixeval/Download/DownloadManager.cs b/src/Pixeval/Download/DownloadManager.cs index adb90d52..f7642879 100644 --- a/src/Pixeval/Download/DownloadManager.cs +++ b/src/Pixeval/Download/DownloadManager.cs @@ -33,7 +33,7 @@ using Pixeval.Utilities.Threading; namespace Pixeval.Download; -public class DownloadManager : IDisposable where TDownloadTask : DownloadTaskBase +public partial class DownloadManager : IDisposable where TDownloadTask : DownloadTaskBase { private readonly Channel _downloadTaskChannel; private readonly HttpClient _httpClient; diff --git a/src/Pixeval/Download/DownloadStartingDeferral.cs b/src/Pixeval/Download/DownloadStartingDeferral.cs index 9d8caf5c..3aee45c8 100644 --- a/src/Pixeval/Download/DownloadStartingDeferral.cs +++ b/src/Pixeval/Download/DownloadStartingDeferral.cs @@ -23,7 +23,7 @@ using Pixeval.Utilities.Threading; namespace Pixeval.Download; -public class DownloadStartingDeferral : IDisposable +public partial class DownloadStartingDeferral : IDisposable { /// /// Set its result to if you want the download to proceed, otherwise, diff --git a/src/Pixeval/Download/IllustrationMetaPathParser.cs b/src/Pixeval/Download/IllustrationMetaPathParser.cs index 77d68feb..2183e90d 100644 --- a/src/Pixeval/Download/IllustrationMetaPathParser.cs +++ b/src/Pixeval/Download/IllustrationMetaPathParser.cs @@ -18,7 +18,7 @@ // along with this program. If not, see . #endregion -using System.Linq; +using System.Collections.Generic; using Pixeval.Controls; using Pixeval.Download.MacroParser; using Pixeval.Download.Macros; @@ -30,9 +30,9 @@ public class IllustrationMetaPathParser : IMetaPathParser _parser = new(); - public static IMacro[] MacroProviderStatic { get; } = MetaPathMacroAttributeHelper.GetAttachedTypeInstances().ToArray(); + public static IReadOnlyList MacroProviderStatic { get; } = MetaPathMacroAttributeHelper.GetIWorkViewModelInstances(); - public IMacro[] MacroProvider => MacroProviderStatic; + public IReadOnlyList MacroProvider => MacroProviderStatic; public string Reduce(string raw, IllustrationItemViewModel context) { diff --git a/src/Pixeval/Download/MacroParser/Ast/INode.cs b/src/Pixeval/Download/MacroParser/Ast/INode.cs index e25c9bbb..1cbcd289 100644 --- a/src/Pixeval/Download/MacroParser/Ast/INode.cs +++ b/src/Pixeval/Download/MacroParser/Ast/INode.cs @@ -18,9 +18,11 @@ // along with this program. If not, see . #endregion +using System.Collections.Generic; + namespace Pixeval.Download.MacroParser.Ast; public interface IMetaPathNode { - string Evaluate(IMacro[] env, TContext context); + string Evaluate(IReadOnlyList env, TContext context); } diff --git a/src/Pixeval/Download/MacroParser/Ast/Macro.cs b/src/Pixeval/Download/MacroParser/Ast/Macro.cs index d0300c2c..d7160312 100644 --- a/src/Pixeval/Download/MacroParser/Ast/Macro.cs +++ b/src/Pixeval/Download/MacroParser/Ast/Macro.cs @@ -18,6 +18,7 @@ // along with this program. If not, see . #endregion +using System.Collections.Generic; using System.Diagnostics; using Pixeval.Utilities; using WinUI3Utilities; @@ -28,7 +29,7 @@ namespace Pixeval.Download.MacroParser.Ast; public record Macro(PlainText MacroName, OptionalMacroParameter? OptionalParameters, bool IsNot) : SingleNode { - public override string Evaluate(IMacro[] env, TContext context) + public override string Evaluate(IReadOnlyList env, TContext context) { var result = env.TryResolve(MacroName.Text, IsNot); return (result) switch diff --git a/src/Pixeval/Download/MacroParser/Ast/OptionalMacroParameter.cs b/src/Pixeval/Download/MacroParser/Ast/OptionalMacroParameter.cs index 5a27e0f6..88f0a94f 100644 --- a/src/Pixeval/Download/MacroParser/Ast/OptionalMacroParameter.cs +++ b/src/Pixeval/Download/MacroParser/Ast/OptionalMacroParameter.cs @@ -18,6 +18,7 @@ // along with this program. If not, see . #endregion +using System.Collections.Generic; using System.Diagnostics; namespace Pixeval.Download.MacroParser.Ast; @@ -25,7 +26,7 @@ namespace Pixeval.Download.MacroParser.Ast; [DebuggerDisplay("{Content}")] public record OptionalMacroParameter(Sequence Content) : IMetaPathNode { - public string Evaluate(IMacro[] env, TContext context) + public string Evaluate(IReadOnlyList env, TContext context) { return Content.Evaluate(env, context); } diff --git a/src/Pixeval/Download/MacroParser/Ast/PlainText.cs b/src/Pixeval/Download/MacroParser/Ast/PlainText.cs index 2a34182b..ba5a230e 100644 --- a/src/Pixeval/Download/MacroParser/Ast/PlainText.cs +++ b/src/Pixeval/Download/MacroParser/Ast/PlainText.cs @@ -18,6 +18,7 @@ // along with this program. If not, see . #endregion +using System.Collections.Generic; using System.Diagnostics; namespace Pixeval.Download.MacroParser.Ast; @@ -25,7 +26,7 @@ namespace Pixeval.Download.MacroParser.Ast; [DebuggerDisplay("{Text}")] public record PlainText(string Text) : SingleNode { - public override string Evaluate(IMacro[] env, TContext context) + public override string Evaluate(IReadOnlyList env, TContext context) { return Text; } diff --git a/src/Pixeval/Download/MacroParser/Ast/Sequence.cs b/src/Pixeval/Download/MacroParser/Ast/Sequence.cs index 01796af5..0eceb11e 100644 --- a/src/Pixeval/Download/MacroParser/Ast/Sequence.cs +++ b/src/Pixeval/Download/MacroParser/Ast/Sequence.cs @@ -18,6 +18,7 @@ // along with this program. If not, see . #endregion +using System.Collections.Generic; using System.Diagnostics; namespace Pixeval.Download.MacroParser.Ast; @@ -25,7 +26,7 @@ namespace Pixeval.Download.MacroParser.Ast; [DebuggerDisplay("{First} {Remains}")] public record Sequence(SingleNode First, Sequence? Remains) : IMetaPathNode { - public string Evaluate(IMacro[] env, TContext context) + public string Evaluate(IReadOnlyList env, TContext context) { return First.Evaluate(env, context) + (Remains?.Evaluate(env, context) ?? string.Empty); } diff --git a/src/Pixeval/Download/MacroParser/Ast/SingleNode.cs b/src/Pixeval/Download/MacroParser/Ast/SingleNode.cs index 143c564d..e3a3958e 100644 --- a/src/Pixeval/Download/MacroParser/Ast/SingleNode.cs +++ b/src/Pixeval/Download/MacroParser/Ast/SingleNode.cs @@ -18,9 +18,11 @@ // along with this program. If not, see . #endregion +using System.Collections.Generic; + namespace Pixeval.Download.MacroParser.Ast; public abstract record SingleNode : IMetaPathNode { - public abstract string Evaluate(IMacro[] env, TContext context); + public abstract string Evaluate(IReadOnlyList env, TContext context); } diff --git a/src/Pixeval/Download/MacroParser/IMetaPathParser.cs b/src/Pixeval/Download/MacroParser/IMetaPathParser.cs index cc08b961..42900617 100644 --- a/src/Pixeval/Download/MacroParser/IMetaPathParser.cs +++ b/src/Pixeval/Download/MacroParser/IMetaPathParser.cs @@ -18,11 +18,13 @@ // along with this program. If not, see . #endregion +using System.Collections.Generic; + namespace Pixeval.Download.MacroParser; public interface IMetaPathParser { - IMacro[] MacroProvider { get; } + IReadOnlyList MacroProvider { get; } string Reduce(string raw, TContext context); } diff --git a/src/Pixeval/Download/Macros/MetaPathMacroAttribute.cs b/src/Pixeval/Download/Macros/MetaPathMacroAttribute.cs index 07156574..3bd7e696 100644 --- a/src/Pixeval/Download/Macros/MetaPathMacroAttribute.cs +++ b/src/Pixeval/Download/Macros/MetaPathMacroAttribute.cs @@ -19,33 +19,8 @@ #endregion using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using Pixeval.Download.MacroParser; namespace Pixeval.Download.Macros; [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] public class MetaPathMacroAttribute : Attribute; - -public static class MetaPathMacroAttributeHelper -{ - public static IEnumerable GetAttachedTypeInstances() - { - return Assembly.GetExecutingAssembly().GetTypes() - .Where(t => t.GetCustomAttribute(typeof(MetaPathMacroAttribute<>))?.GetType() is not null) - .Select(Activator.CreateInstance) - .OfType(); - } - - public static IEnumerable GetAttachedTypeInstances() - { - return Assembly.GetExecutingAssembly().GetTypes() - .Where(t => - t.GetCustomAttribute(typeof(MetaPathMacroAttribute<>))?.GetType() - .GenericTypeArguments is [var type] && type.IsAssignableFrom(typeof(TContext))) - .Select(Activator.CreateInstance) - .OfType(); - } -} diff --git a/src/Pixeval/Download/Models/IllustrationDownloadTask.cs b/src/Pixeval/Download/Models/IllustrationDownloadTask.cs index b88af5b9..5ba74cae 100644 --- a/src/Pixeval/Download/Models/IllustrationDownloadTask.cs +++ b/src/Pixeval/Download/Models/IllustrationDownloadTask.cs @@ -32,7 +32,7 @@ using WinUI3Utilities; namespace Pixeval.Download.Models; -public class IllustrationDownloadTask(DownloadHistoryEntry entry, IllustrationItemViewModel illustration) +public partial class IllustrationDownloadTask(DownloadHistoryEntry entry, IllustrationItemViewModel illustration) : DownloadTaskBase(entry) { public override IWorkViewModel ViewModel => IllustrationViewModel; diff --git a/src/Pixeval/Download/Models/IntrinsicIllustrationDownloadTask.cs b/src/Pixeval/Download/Models/IntrinsicIllustrationDownloadTask.cs index ea2aaceb..844c384b 100644 --- a/src/Pixeval/Download/Models/IntrinsicIllustrationDownloadTask.cs +++ b/src/Pixeval/Download/Models/IntrinsicIllustrationDownloadTask.cs @@ -25,7 +25,7 @@ using Pixeval.Database; namespace Pixeval.Download.Models; -public sealed class IntrinsicIllustrationDownloadTask : IllustrationDownloadTask +public sealed partial class IntrinsicIllustrationDownloadTask : IllustrationDownloadTask { /// /// The disposal of is not handled diff --git a/src/Pixeval/Download/Models/IntrinsicMangaDownloadTask.cs b/src/Pixeval/Download/Models/IntrinsicMangaDownloadTask.cs index c7979d7d..bb62a8d7 100644 --- a/src/Pixeval/Download/Models/IntrinsicMangaDownloadTask.cs +++ b/src/Pixeval/Download/Models/IntrinsicMangaDownloadTask.cs @@ -29,7 +29,7 @@ using WinUI3Utilities; namespace Pixeval.Download.Models; -public sealed class IntrinsicMangaDownloadTask : MangaDownloadTask +public sealed partial class IntrinsicMangaDownloadTask : MangaDownloadTask { public IntrinsicMangaDownloadTask(DownloadHistoryEntry entry, IllustrationItemViewModel illustrationViewModel, IReadOnlyList streams) : base(entry, illustrationViewModel) { diff --git a/src/Pixeval/Download/Models/IntrinsicNovelDownloadTask.cs b/src/Pixeval/Download/Models/IntrinsicNovelDownloadTask.cs index dd5afdc0..28419625 100644 --- a/src/Pixeval/Download/Models/IntrinsicNovelDownloadTask.cs +++ b/src/Pixeval/Download/Models/IntrinsicNovelDownloadTask.cs @@ -26,7 +26,7 @@ using Pixeval.Database; namespace Pixeval.Download.Models; -public sealed class IntrinsicNovelDownloadTask : NovelDownloadTask +public sealed partial class IntrinsicNovelDownloadTask : NovelDownloadTask { public IntrinsicNovelDownloadTask(DownloadHistoryEntry entry, NovelItemViewModel novel, DocumentViewerViewModel viewModel) : base(entry, novel, viewModel.NovelContent, viewModel) => diff --git a/src/Pixeval/Download/Models/IntrinsicUgoiraDownloadTask.cs b/src/Pixeval/Download/Models/IntrinsicUgoiraDownloadTask.cs index a46f91f3..e2366057 100644 --- a/src/Pixeval/Download/Models/IntrinsicUgoiraDownloadTask.cs +++ b/src/Pixeval/Download/Models/IntrinsicUgoiraDownloadTask.cs @@ -29,7 +29,7 @@ using SixLabors.ImageSharp; namespace Pixeval.Download.Models; -public sealed class IntrinsicUgoiraDownloadTask : UgoiraDownloadTask +public sealed partial class IntrinsicUgoiraDownloadTask : UgoiraDownloadTask { /// /// The disposal of is not handled diff --git a/src/Pixeval/Download/Models/LazyInitializedIllustrationDownloadTask.cs b/src/Pixeval/Download/Models/LazyInitializedIllustrationDownloadTask.cs index 960b7775..85e665e2 100644 --- a/src/Pixeval/Download/Models/LazyInitializedIllustrationDownloadTask.cs +++ b/src/Pixeval/Download/Models/LazyInitializedIllustrationDownloadTask.cs @@ -25,7 +25,7 @@ using WinUI3Utilities; namespace Pixeval.Download.Models; -public class LazyInitializedIllustrationDownloadTask(DownloadHistoryEntry entry) +public partial class LazyInitializedIllustrationDownloadTask(DownloadHistoryEntry entry) : IllustrationDownloadTask(entry, null!), ILazyLoadDownloadTask { public override async Task DownloadAsync(Downloader downloadStreamAsync) diff --git a/src/Pixeval/Download/Models/LazyInitializedMangaDownloadTask.cs b/src/Pixeval/Download/Models/LazyInitializedMangaDownloadTask.cs index 24f1f61b..93ac9573 100644 --- a/src/Pixeval/Download/Models/LazyInitializedMangaDownloadTask.cs +++ b/src/Pixeval/Download/Models/LazyInitializedMangaDownloadTask.cs @@ -27,7 +27,7 @@ using WinUI3Utilities; namespace Pixeval.Download.Models; -public class LazyInitializedMangaDownloadTask(DownloadHistoryEntry entry) +public partial class LazyInitializedMangaDownloadTask(DownloadHistoryEntry entry) : MangaDownloadTask(entry, null!), ILazyLoadDownloadTask { public override async Task DownloadAsync(Downloader downloadStreamAsync) diff --git a/src/Pixeval/Download/Models/LazyInitializedNovelDownloadTask.cs b/src/Pixeval/Download/Models/LazyInitializedNovelDownloadTask.cs index adc29bfa..31d0bcd7 100644 --- a/src/Pixeval/Download/Models/LazyInitializedNovelDownloadTask.cs +++ b/src/Pixeval/Download/Models/LazyInitializedNovelDownloadTask.cs @@ -27,7 +27,7 @@ using WinUI3Utilities; namespace Pixeval.Download.Models; -public class LazyInitializedNovelDownloadTask(DownloadHistoryEntry entry) +public partial class LazyInitializedNovelDownloadTask(DownloadHistoryEntry entry) : NovelDownloadTask(entry, null!, null!, null!), ILazyLoadDownloadTask { public override async Task DownloadAsync(Downloader downloadStreamAsync) diff --git a/src/Pixeval/Download/Models/LazyInitializedUgoiraDownloadTask.cs b/src/Pixeval/Download/Models/LazyInitializedUgoiraDownloadTask.cs index 8ace6ec4..3089ddd8 100644 --- a/src/Pixeval/Download/Models/LazyInitializedUgoiraDownloadTask.cs +++ b/src/Pixeval/Download/Models/LazyInitializedUgoiraDownloadTask.cs @@ -25,7 +25,7 @@ using WinUI3Utilities; namespace Pixeval.Download.Models; -public class LazyInitializedUgoiraDownloadTask(DownloadHistoryEntry databaseEntry) : UgoiraDownloadTask(databaseEntry, null!, null!), ILazyLoadDownloadTask +public partial class LazyInitializedUgoiraDownloadTask(DownloadHistoryEntry databaseEntry) : UgoiraDownloadTask(databaseEntry, null!, null!), ILazyLoadDownloadTask { public override async Task DownloadAsync(Downloader downloadStreamAsync) { diff --git a/src/Pixeval/Download/Models/MangaDownloadTask.cs b/src/Pixeval/Download/Models/MangaDownloadTask.cs index 91ea4ffc..43bcdfa6 100644 --- a/src/Pixeval/Download/Models/MangaDownloadTask.cs +++ b/src/Pixeval/Download/Models/MangaDownloadTask.cs @@ -30,7 +30,7 @@ using Pixeval.Util.IO; namespace Pixeval.Download.Models; -public class MangaDownloadTask(DownloadHistoryEntry entry, IllustrationItemViewModel illustration) +public partial class MangaDownloadTask(DownloadHistoryEntry entry, IllustrationItemViewModel illustration) : IllustrationDownloadTask(entry, illustration) { protected int CurrentIndex { get; set; } diff --git a/src/Pixeval/Download/Models/NovelDownloadTask.cs b/src/Pixeval/Download/Models/NovelDownloadTask.cs index b8f27713..82387075 100644 --- a/src/Pixeval/Download/Models/NovelDownloadTask.cs +++ b/src/Pixeval/Download/Models/NovelDownloadTask.cs @@ -36,7 +36,7 @@ using WinUI3Utilities; namespace Pixeval.Download.Models; -public class NovelDownloadTask : DownloadTaskBase +public partial class NovelDownloadTask : DownloadTaskBase { public NovelDownloadTask( DownloadHistoryEntry entry, diff --git a/src/Pixeval/Download/Models/UgoiraDownloadTask.cs b/src/Pixeval/Download/Models/UgoiraDownloadTask.cs index 95e96e18..fb32af77 100644 --- a/src/Pixeval/Download/Models/UgoiraDownloadTask.cs +++ b/src/Pixeval/Download/Models/UgoiraDownloadTask.cs @@ -20,6 +20,7 @@ using System.Collections.Generic; using System.IO; +using System.Linq; using System.Threading.Tasks; using Pixeval.Controls; using Pixeval.CoreApi.Net.Response; @@ -31,7 +32,7 @@ using SixLabors.ImageSharp; namespace Pixeval.Download.Models; -public class UgoiraDownloadTask( +public partial class UgoiraDownloadTask( DownloadHistoryEntry entry, IllustrationItemViewModel illustration, UgoiraMetadataResponse metadata) @@ -69,13 +70,8 @@ public class UgoiraDownloadTask( { if (App.AppViewModel.AppSettings.UgoiraDownloadFormat is UgoiraDownloadFormat.OriginalZip) { - var dict = new Dictionary(); - for (var i = 0; i < urls.Count; ++i) - { - var extension = Path.GetExtension(urls[i]); - dict.Add($"{i}{extension}", streams[i]); - } - var zipStream = await IoHelper.WriteZipAsync(dict, Dispose); + var names = urls.Select(Path.GetExtension).Select((extension, i) => $"{i}{extension}").ToArray(); + var zipStream = await IoHelper.WriteZipAsync(names, streams, Dispose); await zipStream.StreamSaveToFileAsync(destination); Report(100); } diff --git a/src/Pixeval/Download/NovelMetaPathParser.cs b/src/Pixeval/Download/NovelMetaPathParser.cs index 9691071c..fb58768f 100644 --- a/src/Pixeval/Download/NovelMetaPathParser.cs +++ b/src/Pixeval/Download/NovelMetaPathParser.cs @@ -20,7 +20,7 @@ #endregion -using System.Linq; +using System.Collections.Generic; using Pixeval.Controls; using Pixeval.Download.MacroParser; using Pixeval.Download.Macros; @@ -32,9 +32,9 @@ public class NovelMetaPathParser : IMetaPathParser { private readonly MacroParser _parser = new(); - public static IMacro[] MacroProviderStatic { get; } = MetaPathMacroAttributeHelper.GetAttachedTypeInstances().ToArray(); + public static IReadOnlyList MacroProviderStatic { get; } = MetaPathMacroAttributeHelper.GetIWorkViewModelInstances(); - public IMacro[] MacroProvider => MacroProviderStatic; + public IReadOnlyList MacroProvider => MacroProviderStatic; public string Reduce(string raw, NovelItemViewModel context) { diff --git a/src/Pixeval/Options/FontWeightsOption.cs b/src/Pixeval/Options/FontWeightsOption.cs index baa2728c..cbe49561 100644 --- a/src/Pixeval/Options/FontWeightsOption.cs +++ b/src/Pixeval/Options/FontWeightsOption.cs @@ -23,6 +23,7 @@ using Pixeval.Attributes; namespace Pixeval.Options; +[LocalizationMetadata(typeof(MiscResources))] public enum FontWeightsOption { /// diff --git a/src/Pixeval/Options/IllustrationDownloadFormat.cs b/src/Pixeval/Options/IllustrationDownloadFormat.cs index 47aef377..2d1eff57 100644 --- a/src/Pixeval/Options/IllustrationDownloadFormat.cs +++ b/src/Pixeval/Options/IllustrationDownloadFormat.cs @@ -24,6 +24,7 @@ using Pixeval.Attributes; namespace Pixeval.Options; +[LocalizationMetadata(typeof(MiscResources))] public enum IllustrationDownloadFormat { [LocalizedResource(typeof(MiscResources), nameof(MiscResources.Jpg))] diff --git a/src/Pixeval/Options/MainPageTabItem.cs b/src/Pixeval/Options/MainPageTabItem.cs index ec39641d..368bb5eb 100644 --- a/src/Pixeval/Options/MainPageTabItem.cs +++ b/src/Pixeval/Options/MainPageTabItem.cs @@ -28,6 +28,7 @@ namespace Pixeval.Options; /// We require a strict matching between the value of the enum member and the order of the /// in /// +[LocalizationMetadata(typeof(MainPageResources))] public enum MainPageTabItem { [LocalizedResource(typeof(MainPageResources), nameof(MainPageResources.RecommendationsTabContent))] diff --git a/src/Pixeval/Options/NovelDownloadFormat.cs b/src/Pixeval/Options/NovelDownloadFormat.cs index e3a93928..b04b67f8 100644 --- a/src/Pixeval/Options/NovelDownloadFormat.cs +++ b/src/Pixeval/Options/NovelDownloadFormat.cs @@ -24,6 +24,7 @@ using Pixeval.Attributes; namespace Pixeval.Options; +[LocalizationMetadata(typeof(MiscResources))] public enum NovelDownloadFormat { [LocalizedResource(typeof(MiscResources), nameof(MiscResources.Pdf))] diff --git a/src/Pixeval/Options/ProxyType.cs b/src/Pixeval/Options/ProxyType.cs index e1d158c1..badd71a6 100644 --- a/src/Pixeval/Options/ProxyType.cs +++ b/src/Pixeval/Options/ProxyType.cs @@ -22,19 +22,45 @@ using Pixeval.Attributes; namespace Pixeval.Options; +[LocalizationMetadata(typeof(ProxyTypeResources))] public enum ProxyType { - [LocalizedResource(typeof(MiscResources), nameof(MiscResources.ProxyOptionSystem))] + [LocalizedResource(typeof(MiscResources), nameof(ProxyTypeResources.ProxyOptionSystem))] System, - [LocalizedResource(typeof(MiscResources), nameof(MiscResources.ProxyOptionNone))] + [LocalizedResource(typeof(MiscResources), nameof(ProxyTypeResources.ProxyOptionNone))] None, + [LocalizedResource(typeof(MiscResources), nameof(ProxyTypeResources.ProxyOptionHttp))] Http, - + + [LocalizedResource(typeof(MiscResources), nameof(ProxyTypeResources.ProxyOptionSocks4))] Socks4, - + + [LocalizedResource(typeof(MiscResources), nameof(ProxyTypeResources.ProxyOptionSocks4A))] Socks4A, - + + [LocalizedResource(typeof(MiscResources), nameof(ProxyTypeResources.ProxyOptionSocks5))] Socks5 } + +public static class ProxyTypeResources +{ + public static string GetResource(string id) => id switch + { + nameof(ProxyType.Http) => ProxyOptionHttp, + + nameof(ProxyType.Socks4) => ProxyOptionSocks4, + nameof(ProxyType.Socks4A) => ProxyOptionSocks4A, + nameof(ProxyType.Socks5) => ProxyOptionSocks5, + nameof(ProxyType.System) => ProxyOptionSystem, + _ => ProxyOptionNone + }; + + public const string ProxyOptionHttp = "http/https"; + public const string ProxyOptionSocks4 = "socks4"; + public const string ProxyOptionSocks4A = "socks4a"; + public const string ProxyOptionSocks5 = "socks5"; + public static string ProxyOptionNone => MiscResources.ProxyOptionSystem; + public static string ProxyOptionSystem => MiscResources.ProxyOptionNone; +} diff --git a/src/Pixeval/Options/ThumbnailDirection.cs b/src/Pixeval/Options/ThumbnailDirection.cs index d87c4fc9..dd6583a6 100644 --- a/src/Pixeval/Options/ThumbnailDirection.cs +++ b/src/Pixeval/Options/ThumbnailDirection.cs @@ -1,4 +1,4 @@ -#region Copyright (c) Pixeval/Pixeval +#region Copyright (c) Pixeval/Pixeval // GPL v3 License // // Pixeval/Pixeval @@ -22,6 +22,7 @@ using Pixeval.Attributes; namespace Pixeval.Options; +[LocalizationMetadata(typeof(MiscResources))] public enum ThumbnailDirection { [LocalizedResource(typeof(MiscResources), nameof(MiscResources.ThumbnailDirectionLandscape))] @@ -29,4 +30,4 @@ public enum ThumbnailDirection [LocalizedResource(typeof(MiscResources), nameof(MiscResources.ThumbnailDirectionPortrait))] Portrait -} \ No newline at end of file +} diff --git a/src/Pixeval/Options/UgoiraDownloadFormat.cs b/src/Pixeval/Options/UgoiraDownloadFormat.cs index 008a0d95..f0d326ee 100644 --- a/src/Pixeval/Options/UgoiraDownloadFormat.cs +++ b/src/Pixeval/Options/UgoiraDownloadFormat.cs @@ -22,6 +22,7 @@ using Pixeval.Attributes; namespace Pixeval.Options; +[LocalizationMetadata(typeof(MiscResources))] public enum UgoiraDownloadFormat { [LocalizedResource(typeof(MiscResources), nameof(MiscResources.Tiff))] diff --git a/src/Pixeval/Pages/Capability/BookmarkPageViewModel.cs b/src/Pixeval/Pages/Capability/BookmarkPageViewModel.cs index b3a0d30e..ae59f682 100644 --- a/src/Pixeval/Pages/Capability/BookmarkPageViewModel.cs +++ b/src/Pixeval/Pages/Capability/BookmarkPageViewModel.cs @@ -32,7 +32,7 @@ using WinUI3Utilities; namespace Pixeval.Pages.Capability; -public class BookmarkPageViewModel(long userId) : ObservableObject, IDisposable +public partial class BookmarkPageViewModel(long userId) : ObservableObject, IDisposable { public long UserId { get; } = userId; diff --git a/src/Pixeval/Pages/Capability/RankingsPage.xaml.cs b/src/Pixeval/Pages/Capability/RankingsPage.xaml.cs index 5b041e4a..e6ac3222 100644 --- a/src/Pixeval/Pages/Capability/RankingsPage.xaml.cs +++ b/src/Pixeval/Pages/Capability/RankingsPage.xaml.cs @@ -25,7 +25,6 @@ using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Pixeval.Controls; using Pixeval.CoreApi.Global.Enum; -using Pixeval.Util; using WinRT; using WinUI3Utilities; @@ -33,10 +32,9 @@ namespace Pixeval.Pages.Capability; public sealed partial class RankingsPage : IScrollViewHost { - private static readonly List _illustrationRankOption = LocalizedResourceAttributeHelper.GetLocalizedResourceContents(); + private static readonly IReadOnlyList _illustrationRankOption = RankOptionExtension.GetItems(); - private static readonly List _novelRankOption = - LocalizedResourceAttributeHelper.GetLocalizedResourceContents(RankOptionExtension.NovelRankOptions); + private static readonly IReadOnlyList _novelRankOption = NovelRankOptionExtension.GetItems(); public RankingsPage() { @@ -63,12 +61,12 @@ public sealed partial class RankingsPage : IScrollViewHost if (SimpleWorkTypeComboBox.GetSelectedItem() is SimpleWorkType.IllustAndManga) { RankOptionComboBox.ItemsSource = _illustrationRankOption; - RankOptionComboBox.SelectedItem = RankOptionComboBox.ItemsSource.To>().First(r => Equals(r.Item, App.AppViewModel.AppSettings.IllustrationRankOption)); + RankOptionComboBox.SelectedItem = _illustrationRankOption.First(r => Equals(r.Item, App.AppViewModel.AppSettings.IllustrationRankOption)); } else { RankOptionComboBox.ItemsSource = _novelRankOption; - RankOptionComboBox.SelectedItem = RankOptionComboBox.ItemsSource.To>().First(r => Equals(r.Item, App.AppViewModel.AppSettings.NovelRankOption)); + RankOptionComboBox.SelectedItem = _novelRankOption.First(r => Equals(r.Item, App.AppViewModel.AppSettings.NovelRankOption)); } } diff --git a/src/Pixeval/Pages/Download/DownloadPage.xaml b/src/Pixeval/Pages/Download/DownloadPage.xaml index 2a231c41..872e5f45 100644 --- a/src/Pixeval/Pages/Download/DownloadPage.xaml +++ b/src/Pixeval/Pages/Download/DownloadPage.xaml @@ -8,7 +8,6 @@ xmlns:fluent="using:FluentIcons.WinUI" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:system="using:System" - xmlns:util="using:Pixeval.Util" mc:Ignorable="d"> - + diff --git a/src/Pixeval/Pages/IllustrationViewer/ImageViewerPageViewModel.cs b/src/Pixeval/Pages/IllustrationViewer/ImageViewerPageViewModel.cs index 59dae5e9..beb6aa83 100644 --- a/src/Pixeval/Pages/IllustrationViewer/ImageViewerPageViewModel.cs +++ b/src/Pixeval/Pages/IllustrationViewer/ImageViewerPageViewModel.cs @@ -47,24 +47,6 @@ namespace Pixeval.Pages.IllustrationViewer; public partial class ImageViewerPageViewModel : UiObservableObject, IDisposable { - public enum LoadingPhase - { - [LocalizedResource(typeof(ImageViewerPageResources), nameof(ImageViewerPageResources.CheckingCache))] - CheckingCache, - - [LocalizedResource(typeof(ImageViewerPageResources), nameof(ImageViewerPageResources.LoadingFromCache))] - LoadingFromCache, - - [LocalizedResource(typeof(ImageViewerPageResources), nameof(ImageViewerPageResources.MergingUgoiraFrames))] - MergingUgoiraFrames, - - [LocalizedResource(typeof(ImageViewerPageResources), nameof(ImageViewerPageResources.DownloadingImageFormatted), DownloadingImage)] - DownloadingImage, - - [LocalizedResource(typeof(ImageViewerPageResources), nameof(ImageViewerPageResources.LoadingImage))] - LoadingImage - } - private bool _disposed; [ObservableProperty] @@ -193,11 +175,11 @@ public partial class ImageViewerPageViewModel : UiObservableObject, IDisposable private void AdvancePhase(LoadingPhase phase, double progress = 0) { - LoadingText = phase.GetLocalizedResource() switch - { - { FormatKey: LoadingPhase } attr => attr.GetLocalizedResourceContent()?.Format((int)(LoadingProgress = progress)), - var attr => attr?.GetLocalizedResourceContent() - }; + if (phase is LoadingPhase.DownloadingImage) + LoadingText = LoadingPhaseExtension.GetResource(LoadingPhase.DownloadingImage) + ?.Format((int)(LoadingProgress = progress)); + else + LoadingText = LoadingPhaseExtension.GetResource(phase); } private async Task LoadImage() @@ -450,3 +432,22 @@ public partial class ImageViewerPageViewModel : UiObservableObject, IDisposable LoadSuccessfully = false; } } + +[LocalizationMetadata(typeof(ImageViewerPageResources))] +public enum LoadingPhase +{ + [LocalizedResource(typeof(ImageViewerPageResources), nameof(ImageViewerPageResources.CheckingCache))] + CheckingCache, + + [LocalizedResource(typeof(ImageViewerPageResources), nameof(ImageViewerPageResources.LoadingFromCache))] + LoadingFromCache, + + [LocalizedResource(typeof(ImageViewerPageResources), nameof(ImageViewerPageResources.MergingUgoiraFrames))] + MergingUgoiraFrames, + + [LocalizedResource(typeof(ImageViewerPageResources), nameof(ImageViewerPageResources.DownloadingImageFormatted), DownloadingImage)] + DownloadingImage, + + [LocalizedResource(typeof(ImageViewerPageResources), nameof(ImageViewerPageResources.LoadingImage))] + LoadingImage +} diff --git a/src/Pixeval/Pages/Login/LoginPage.xaml b/src/Pixeval/Pages/Login/LoginPage.xaml index c9475755..059928a3 100644 --- a/src/Pixeval/Pages/Login/LoginPage.xaml +++ b/src/Pixeval/Pages/Login/LoginPage.xaml @@ -10,7 +10,6 @@ xmlns:local="using:Pixeval.Pages.Login" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:pixeval="using:Pixeval" - xmlns:util="using:Pixeval.Util" xmlns:windowing="using:Pixeval.Controls.Windowing" Loaded="LoginPage_OnLoaded" Unloaded="LoginPage_OnUnloaded" @@ -194,7 +193,7 @@ + Text="{x:Bind local:LoginPhaseEnumExtension.GetResource(_viewModel.LoginPhase), Mode=OneWay}" /> (null, new DrillInNavigationTransitionInfo()); Current._viewModel.LogoutExit = false; AppInfo.SaveContext(); @@ -145,7 +145,7 @@ public sealed partial class LoginPage try { await _viewModel.WebView2LoginAsync(HWnd, useNewAccount, () => DispatcherQueue.TryEnqueue(SuccessNavigating)); - _viewModel.AdvancePhase(LoginPageViewModel.LoginPhaseEnum.WaitingForUserInput); + _viewModel.AdvancePhase(LoginPhaseEnum.WaitingForUserInput); } catch (Exception exception) { diff --git a/src/Pixeval/Pages/Login/LoginPageViewModel.cs b/src/Pixeval/Pages/Login/LoginPageViewModel.cs index 5c5b1853..d9edbb7a 100644 --- a/src/Pixeval/Pages/Login/LoginPageViewModel.cs +++ b/src/Pixeval/Pages/Login/LoginPageViewModel.cs @@ -25,7 +25,7 @@ using System.IO; using System.Linq; using System.Net; using System.Net.NetworkInformation; -using System.Reflection; +// using System.Reflection; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; using System.Web; @@ -37,8 +37,8 @@ using Microsoft.UI.Xaml.Controls; using Microsoft.Win32; using Pixeval.AppManagement; using Pixeval.Attributes; -using Pixeval.Bypass; using Pixeval.Controls.DialogContent; +// using Pixeval.Bypass; using Pixeval.Controls.Windowing; using Pixeval.CoreApi; using Pixeval.CoreApi.Preference; @@ -52,27 +52,6 @@ namespace Pixeval.Pages.Login; [SettingsViewModel(nameof(LoginContext))] public partial class LoginPageViewModel(UIElement owner) : ObservableObject { - public enum LoginPhaseEnum - { - [LocalizedResource(typeof(LoginPageResources), nameof(LoginPageResources.LoginPhaseRefreshing))] - Refreshing, - - [LocalizedResource(typeof(LoginPageResources), nameof(LoginPageResources.LoginPhaseWaitingForUserInput))] - WaitingForUserInput, - - [LocalizedResource(typeof(LoginPageResources), nameof(LoginPageResources.LoginPhaseCheckingCertificateInstallation))] - CheckingCertificateInstallation, - - [LocalizedResource(typeof(LoginPageResources), nameof(LoginPageResources.LoginPhaseInstallingCertificate))] - InstallingCertificate, - - [LocalizedResource(typeof(LoginPageResources), nameof(LoginPageResources.LoginPhaseCheckingWebView2Installation))] - CheckingWebView2Installation, - - [LocalizedResource(typeof(LoginPageResources), nameof(LoginPageResources.LoginPhaseSuccessNavigating))] - SuccessNavigating - } - /// /// 表示要不要展示 /// @@ -361,13 +340,35 @@ public partial class LoginPageViewModel(UIElement owner) : ObservableObject var process = Process.Start(startInfo); if (EnableDomainFronting) { - var pid = process!.Id; - var dllPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!, "runtimes", - "win-x64", "native", "bypass.dll"); - var injection = Injector.Inject((uint)pid, dllPath); - Injector.InstallChromeHook(injection, true, dllPath); + // var pid = process!.Id; + // var dllPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!, "runtimes", + // "win-x64", "native", "bypass.dll"); + // var injection = Injector.Inject((uint)pid, dllPath); + // Injector.InstallChromeHook(injection, true, dllPath); } } #endregion } + +[LocalizationMetadata(typeof(LoginPageResources))] +public enum LoginPhaseEnum +{ + [LocalizedResource(typeof(LoginPageResources), nameof(LoginPageResources.LoginPhaseRefreshing))] + Refreshing, + + [LocalizedResource(typeof(LoginPageResources), nameof(LoginPageResources.LoginPhaseWaitingForUserInput))] + WaitingForUserInput, + + [LocalizedResource(typeof(LoginPageResources), nameof(LoginPageResources.LoginPhaseCheckingCertificateInstallation))] + CheckingCertificateInstallation, + + [LocalizedResource(typeof(LoginPageResources), nameof(LoginPageResources.LoginPhaseInstallingCertificate))] + InstallingCertificate, + + [LocalizedResource(typeof(LoginPageResources), nameof(LoginPageResources.LoginPhaseCheckingWebView2Installation))] + CheckingWebView2Installation, + + [LocalizedResource(typeof(LoginPageResources), nameof(LoginPageResources.LoginPhaseSuccessNavigating))] + SuccessNavigating +} diff --git a/src/Pixeval/Pages/Login/PixivAuth.cs b/src/Pixeval/Pages/Login/PixivAuth.cs index 0a1be9e7..b5c38a9c 100644 --- a/src/Pixeval/Pages/Login/PixivAuth.cs +++ b/src/Pixeval/Pages/Login/PixivAuth.cs @@ -22,13 +22,14 @@ using System; using System.Net.Http; using System.Security.Cryptography; using System.Text; +using System.Text.Json; using Pixeval.CoreApi.Model; using Pixeval.CoreApi.Net; using Pixeval.CoreApi.Preference; using Pixeval.Util; using System.Threading.Tasks; +using Pixeval.CoreApi; using Pixeval.Util.IO; -using Pixeval.Utilities; namespace Pixeval.Pages.Login; @@ -82,7 +83,8 @@ public static class PixivAuth // using会有resharper警告,所以这里用Dispose httpClient.Dispose(); _ = result.EnsureSuccessStatusCode(); - var session = (await result.Content.ReadAsStringAsync()).FromJson()!.ToSession(); + var str = await result.Content.ReadAsStringAsync(); + var session = ((TokenResponse)JsonSerializer.Deserialize(str, typeof(TokenResponse), AppJsonSerializerContext.Default)!).ToSession(); App.AppViewModel.LoginContext.RefreshToken = session.RefreshToken; return session; } diff --git a/src/Pixeval/Pages/Misc/ClearDataKind.cs b/src/Pixeval/Pages/Misc/ClearDataKind.cs index 60b16fd0..9800e484 100644 --- a/src/Pixeval/Pages/Misc/ClearDataKind.cs +++ b/src/Pixeval/Pages/Misc/ClearDataKind.cs @@ -22,6 +22,7 @@ using Pixeval.Attributes; namespace Pixeval.Pages.Misc; +[LocalizationMetadata(typeof(SettingsPageResources))] public enum ClearDataKind { [LocalizedResource(typeof(SettingsPageResources), nameof(SettingsPageResources.FileCacheCleared))] diff --git a/src/Pixeval/Pages/Misc/SettingsEntryCategory.cs b/src/Pixeval/Pages/Misc/SettingsEntryCategory.cs index 220712e0..8fc14bce 100644 --- a/src/Pixeval/Pages/Misc/SettingsEntryCategory.cs +++ b/src/Pixeval/Pages/Misc/SettingsEntryCategory.cs @@ -24,6 +24,7 @@ using Pixeval.Attributes; namespace Pixeval.Pages.Misc; +[LocalizationMetadata(typeof(SettingsPageResources))] public enum SettingsEntryCategory { [LocalizedResource(typeof(SettingsPageResources), nameof(SettingsPageResources.VersionSettingsGroupText))] diff --git a/src/Pixeval/Pages/Misc/SettingsPageViewModel.cs b/src/Pixeval/Pages/Misc/SettingsPageViewModel.cs index f35758fb..aa9c85e3 100644 --- a/src/Pixeval/Pages/Misc/SettingsPageViewModel.cs +++ b/src/Pixeval/Pages/Misc/SettingsPageViewModel.cs @@ -27,7 +27,6 @@ using Microsoft.UI.Xaml.Controls; using Pixeval.AppManagement; using Pixeval.Controls; using Pixeval.Options; -using Pixeval.Util; using Pixeval.Util.ComponentModels; using Pixeval.Util.IO; using Pixeval.Util.UI; @@ -79,10 +78,12 @@ public partial class SettingsPageViewModel : UiObservableObject, IDisposable [ new(SettingsEntryCategory.Application) { - new EnumAppSettingsEntry(AppSettings, - t => t.Theme) { ValueChanged = t => WindowFactory.SetTheme((ElementTheme)t) }, - new EnumAppSettingsEntry(AppSettings, - t => t.Backdrop) { ValueChanged = t => WindowFactory.SetBackdrop((BackdropType)t) }, + new EnumAppSettingsEntry(AppSettings, + t => t.Theme, + ElementThemeExtension.GetItems()) { ValueChanged = t => WindowFactory.SetTheme((ElementTheme)t) }, + new EnumAppSettingsEntry(AppSettings, + t => t.Backdrop, + BackdropTypeExtension.GetItems()) { ValueChanged = t => WindowFactory.SetBackdrop((BackdropType)t) }, new FontAppSettingsEntry(AppSettings, t => t.AppFontFamilyName), new LanguageAppSettingsEntry(AppSettings), @@ -102,17 +103,21 @@ public partial class SettingsPageViewModel : UiObservableObject, IDisposable }, new BoolAppSettingsEntry(AppSettings, t => t.UseFileCache), - new EnumAppSettingsEntry(AppSettings, - t => t.DefaultSelectedTabItem) + new EnumAppSettingsEntry(AppSettings, + t => t.DefaultSelectedTabItem, + MainPageTabItemExtension.GetItems()) }, new(SettingsEntryCategory.BrowsingExperience) { - new EnumAppSettingsEntry(AppSettings, - t => t.ThumbnailDirection), - new EnumAppSettingsEntry(AppSettings, - t => t.ItemsViewLayoutType), - new EnumAppSettingsEntry(AppSettings, - t => t.TargetFilter), + new EnumAppSettingsEntry(AppSettings, + t => t.ThumbnailDirection, + ThumbnailDirectionExtension.GetItems()), + new EnumAppSettingsEntry(AppSettings, + t => t.ItemsViewLayoutType, + ItemsViewLayoutTypeExtension.GetItems()), + new EnumAppSettingsEntry(AppSettings, + t => t.TargetFilter, + TargetFilterExtension.GetItems()), new TokenizingAppSettingsEntry(AppSettings), new BoolAppSettingsEntry(AppSettings, t => t.BrowseOriginalImage), @@ -148,37 +153,43 @@ public partial class SettingsPageViewModel : UiObservableObject, IDisposable Max = 20, Min = 0 }, - new EnumAppSettingsEntry(AppSettings, - t => t.WorkSortOption), - new EnumAppSettingsEntry(AppSettings, - t => t.SimpleWorkType), + new EnumAppSettingsEntry(AppSettings, + t => t.WorkSortOption, + WorkSortOptionExtension.GetItems()), + new EnumAppSettingsEntry(AppSettings, + t => t.SimpleWorkType, + SimpleWorkTypeExtension.GetItems()), new MultiValuesAppSettingsEntry(AppSettings, SettingsPageResources.RankOptionEntryHeader, SettingsPageResources.RankOptionEntryDescription, Symbol.ArrowTrending, [ - new EnumAppSettingsEntry(AppSettings, + new EnumAppSettingsEntry(AppSettings, WorkTypeEnum.Illustration, - t => t.IllustrationRankOption), + t => t.IllustrationRankOption, + RankOptionExtension.GetItems()), new EnumAppSettingsEntry(AppSettings, WorkTypeEnum.Novel, t => t.NovelRankOption, - RankOptionExtension.NovelRankOptions) + NovelRankOptionExtension.GetItems()) ]), new MultiValuesAppSettingsEntry(AppSettings, SettingsPageResources.DefaultSearchTagMatchOptionEntryHeader, SettingsPageResources.DefaultSearchTagMatchOptionEntryDescription, Symbol.CheckmarkCircleSquare, [ - new EnumAppSettingsEntry(AppSettings, + new EnumAppSettingsEntry(AppSettings, WorkTypeEnum.Illustration, - t => t.SearchIllustrationTagMatchOption), - new EnumAppSettingsEntry(AppSettings, + t => t.SearchIllustrationTagMatchOption, + SearchIllustrationTagMatchOptionExtension.GetItems()), + new EnumAppSettingsEntry(AppSettings, WorkTypeEnum.Novel, - t => t.SearchNovelTagMatchOption) + t => t.SearchNovelTagMatchOption, + SearchNovelTagMatchOptionExtension.GetItems()) ]), - new EnumAppSettingsEntry(AppSettings, - t => t.SearchDuration), + new EnumAppSettingsEntry(AppSettings, + t => t.SearchDuration, + SearchDurationExtension.GetItems()), new DateRangeWithSwitchAppSettingsEntry(AppSettings) }, new(SettingsEntryCategory.Download) @@ -204,15 +215,18 @@ public partial class SettingsPageViewModel : UiObservableObject, IDisposable SettingsPageResources.WorkDownloadFormatEntryDescription, Symbol.TextPeriodAsterisk, [ - new EnumAppSettingsEntry(AppSettings, + new EnumAppSettingsEntry(AppSettings, WorkTypeEnum.Illustration, - t => t.IllustrationDownloadFormat), - new EnumAppSettingsEntry(AppSettings, + t => t.IllustrationDownloadFormat, + IllustrationDownloadFormatExtension.GetItems()), + new EnumAppSettingsEntry(AppSettings, WorkTypeEnum.Ugoira, - t => t.UgoiraDownloadFormat), - new EnumAppSettingsEntry(AppSettings, + t => t.UgoiraDownloadFormat, + UgoiraDownloadFormatExtension.GetItems()), + new EnumAppSettingsEntry(AppSettings, WorkTypeEnum.Novel, - t => t.NovelDownloadFormat) + t => t.NovelDownloadFormat, + NovelDownloadFormatExtension.GetItems()) ]), new BoolAppSettingsEntry(AppSettings, t => t.DownloadWhenBookmarked) @@ -343,7 +357,7 @@ public partial class SettingsPageViewModel : UiObservableObject, IDisposable public void ShowClearData(ClearDataKind kind) { - HWnd.SuccessGrowl(kind.GetLocalizedResourceContent()!); + HWnd.SuccessGrowl(ClearDataKindExtension.GetResource(kind)); } public void CancelToken() @@ -354,6 +368,7 @@ public partial class SettingsPageViewModel : UiObservableObject, IDisposable public void Dispose() { + GC.SuppressFinalize(this); CancelToken(); } } diff --git a/src/Pixeval/Pages/Misc/Supporter.cs b/src/Pixeval/Pages/Misc/Supporter.cs index 471ed4e4..b296fb8d 100644 --- a/src/Pixeval/Pages/Misc/Supporter.cs +++ b/src/Pixeval/Pages/Misc/Supporter.cs @@ -35,8 +35,8 @@ public record Supporter(string Nickname, string Name, ImageSource ProfilePicture private static readonly IEnumerable<(string Nickname, string Name)> _supporters = new (string Nickname, string Name)[] { - ("Sep", "Guro2"), - ("无论时间", "wulunshijian"), + ("Sep", "Guro2"), + ("无论时间", "wulunshijian"), ("CN", "ControlNet"), ("CY", "Cyl18"), ("对味", "duiweiya"), @@ -64,7 +64,7 @@ public record Supporter(string Nickname, string Name, ImageSource ProfilePicture if (httpClient.DefaultRequestHeaders.UserAgent.Count is 0) httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 Edg/121.0.0.0"); foreach (var (nickname, name) in _supporters) - if (await httpClient.GetFromJsonAsync("https://api.github.com/users/" + name) is { } user) + if (await httpClient.GetFromJsonAsync("https://api.github.com/users/" + name, typeof(GitHubUser), GitHubUserSerializeContext.Default) is GitHubUser user) { var supporter = new Supporter(nickname, '@' + name, new BitmapImage(user.AvatarUrl), user.HtmlUrl); Supporters.Add(supporter); @@ -81,7 +81,10 @@ public record Supporter(string Nickname, string Name, ImageSource ProfilePicture } } -file class GitHubUser +[JsonSerializable(typeof(GitHubUser))] +public partial class GitHubUserSerializeContext : JsonSerializerContext; + +public class GitHubUser { [JsonPropertyName("avatar_url")] public required Uri AvatarUrl { get; init; } diff --git a/src/Pixeval/Pages/NovelViewer/NovelViewerPageViewModel.cs b/src/Pixeval/Pages/NovelViewer/NovelViewerPageViewModel.cs index cb65cc63..a559419f 100644 --- a/src/Pixeval/Pages/NovelViewer/NovelViewerPageViewModel.cs +++ b/src/Pixeval/Pages/NovelViewer/NovelViewerPageViewModel.cs @@ -283,7 +283,7 @@ public partial class NovelViewerPageViewModel : DetailedUiObservableObject, IDis public ColorAppSettingsEntry NovelFontColorEntry { get; } = new(AppSettings, t => t.NovelFontColor) { ValueChanged = ValueChanged }; - public EnumAppSettingsEntry NovelFontWeightEntry { get; } = new(AppSettings, t => t.NovelFontWeight) { ValueChanged = ValueChanged }; + public EnumAppSettingsEntry NovelFontWeightEntry { get; } = new(AppSettings, t => t.NovelFontWeight, FontWeightsOptionExtension.GetItems()) { ValueChanged = ValueChanged }; public IntAppSettingsEntry NovelFontSizeEntry { get; } = new(AppSettings, t => t.NovelFontSize) { diff --git a/src/Pixeval/Pages/SuggestionModelDataTemplateSelector.cs b/src/Pixeval/Pages/SuggestionModelDataTemplateSelector.cs index 0409e8cc..adfccd1e 100644 --- a/src/Pixeval/Pages/SuggestionModelDataTemplateSelector.cs +++ b/src/Pixeval/Pages/SuggestionModelDataTemplateSelector.cs @@ -25,7 +25,7 @@ using WinUI3Utilities; namespace Pixeval.Pages; -public class SuggestionModelDataTemplateSelector : DataTemplateSelector +public partial class SuggestionModelDataTemplateSelector : DataTemplateSelector { public string? IllustrationHeader { get; set; } diff --git a/src/Pixeval/Pages/Tags/TagsEntryDataProvider.cs b/src/Pixeval/Pages/Tags/TagsEntryDataProvider.cs index a4561c4e..3de820d1 100644 --- a/src/Pixeval/Pages/Tags/TagsEntryDataProvider.cs +++ b/src/Pixeval/Pages/Tags/TagsEntryDataProvider.cs @@ -7,7 +7,7 @@ using Pixeval.Collections; namespace Pixeval.Pages.Tags; -public class TagsEntryDataProvider : ObservableObject, IDisposable +public partial class TagsEntryDataProvider : ObservableObject, IDisposable { public TagsEntryDataProvider() => View.ObserveFilterProperty(nameof(TagsEntryViewModel.Tags)); diff --git a/src/Pixeval/Pages/Tags/TagsPageViewModel.cs b/src/Pixeval/Pages/Tags/TagsPageViewModel.cs index eb6ecb48..5bf1d7a3 100644 --- a/src/Pixeval/Pages/Tags/TagsPageViewModel.cs +++ b/src/Pixeval/Pages/Tags/TagsPageViewModel.cs @@ -8,7 +8,7 @@ using WinUI3Utilities; namespace Pixeval.Pages.Tags; -public class TagsPageViewModel : ObservableObject, IDisposable +public partial class TagsPageViewModel : ObservableObject, IDisposable { public string WorkingDirectory { diff --git a/src/Pixeval/Pages/WorkViewer/CommentsPage.xaml.cs b/src/Pixeval/Pages/WorkViewer/CommentsPage.xaml.cs index 59356cf5..3c980f0e 100644 --- a/src/Pixeval/Pages/WorkViewer/CommentsPage.xaml.cs +++ b/src/Pixeval/Pages/WorkViewer/CommentsPage.xaml.cs @@ -24,6 +24,7 @@ using System.Net.Http.Json; using System.Threading.Tasks; using Microsoft.UI.Xaml.Navigation; using Pixeval.Controls; +using Pixeval.CoreApi; using Pixeval.CoreApi.Global.Enum; using Pixeval.CoreApi.Model; using Pixeval.CoreApi.Net.Response; @@ -101,7 +102,7 @@ public sealed partial class CommentsPage private async Task AddComment(HttpResponseMessage postCommentResponse) { - if (postCommentResponse.IsSuccessStatusCode && await postCommentResponse.Content.ReadFromJsonAsync() is { Comment: { } comment }) + if (postCommentResponse.IsSuccessStatusCode && await postCommentResponse.Content.ReadFromJsonAsync(typeof(PostCommentResponse), AppJsonSerializerContext.Default) is PostCommentResponse { Comment: { } comment }) _viewModel.AddComment(comment); } diff --git a/src/Pixeval/Pages/WorkViewer/CommentsPageViewModel.cs b/src/Pixeval/Pages/WorkViewer/CommentsPageViewModel.cs index dc55e42c..5788d676 100644 --- a/src/Pixeval/Pages/WorkViewer/CommentsPageViewModel.cs +++ b/src/Pixeval/Pages/WorkViewer/CommentsPageViewModel.cs @@ -31,7 +31,7 @@ using Pixeval.CoreApi.Model; namespace Pixeval.Pages; -public class CommentsPageViewModel : ObservableObject +public partial class CommentsPageViewModel : ObservableObject { public CommentsPageViewModel(IAsyncEnumerable engine, SimpleWorkType type, long entryId) { diff --git a/src/Pixeval/Pages/WorkViewer/PixivReplyEmojiViewModel.cs b/src/Pixeval/Pages/WorkViewer/PixivReplyEmojiViewModel.cs index 42184afa..bfe9dd35 100644 --- a/src/Pixeval/Pages/WorkViewer/PixivReplyEmojiViewModel.cs +++ b/src/Pixeval/Pages/WorkViewer/PixivReplyEmojiViewModel.cs @@ -24,11 +24,17 @@ using Pixeval.Util.UI; namespace Pixeval.Pages; -public class PixivReplyEmojiViewModel(PixivReplyEmoji emojiEnumValue, Stream imageStream) +public class PixivReplyEmojiViewModel() { - public PixivReplyEmoji EmojiEnumValue { get; } = emojiEnumValue; + public PixivReplyEmojiViewModel(PixivReplyEmoji emojiEnumValue, Stream imageStream) : this() + { + EmojiEnumValue = emojiEnumValue; + ImageStream = imageStream; + } - public Stream ImageStream { get; } = imageStream; + public PixivReplyEmoji EmojiEnumValue { get; } + + public Stream ImageStream { get; } = null!; public ImageSource? ImageSource { get; set; } } diff --git a/src/Pixeval/Pixeval.csproj b/src/Pixeval/Pixeval.csproj index c823cf9c..127c72b0 100644 --- a/src/Pixeval/Pixeval.csproj +++ b/src/Pixeval/Pixeval.csproj @@ -6,7 +6,6 @@ Pixeval x86;x64;ARM64 win-x86;win-x64;win-arm64 - win-$(Platform).pubxml app.manifest enable true @@ -14,7 +13,11 @@ false zh-cn preview + Debug;Release true + false + 10.0.22621.35-preview + win-$(Platform).pubxml @@ -22,16 +25,16 @@ - - + + - - + + - + @@ -41,20 +44,22 @@ - + - + + + - + - + - + @@ -62,15 +67,15 @@ - + { -@(TextFile->Metadata('Content'), ', -') + @(TextFile->Metadata('Content'), ', + ') } @@ -110,7 +115,7 @@ %UserProfile%\Desktop 0 True - x86|x64|arm64 + x64 diff --git a/src/Pixeval/Properties/PublishProfiles/win-arm64.pubxml b/src/Pixeval/Properties/PublishProfiles/win-arm64.pubxml new file mode 100644 index 00000000..7b5c0568 --- /dev/null +++ b/src/Pixeval/Properties/PublishProfiles/win-arm64.pubxml @@ -0,0 +1,18 @@ + + + + + FileSystem + ARM64 + win-arm64 + win10-arm64 + bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\ + true + False + False + True + True + + diff --git a/src/Pixeval/Properties/PublishProfiles/win-x64.pubxml b/src/Pixeval/Properties/PublishProfiles/win-x64.pubxml new file mode 100644 index 00000000..b25d8f3e --- /dev/null +++ b/src/Pixeval/Properties/PublishProfiles/win-x64.pubxml @@ -0,0 +1,18 @@ + + + + + FileSystem + x64 + win-x64 + win10-x64 + bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\ + true + False + False + True + True + + diff --git a/src/Pixeval/Settings/Models/BoolAppSettingsEntry.cs b/src/Pixeval/Settings/Models/BoolAppSettingsEntry.cs index 8cfdc257..31c1a14f 100644 --- a/src/Pixeval/Settings/Models/BoolAppSettingsEntry.cs +++ b/src/Pixeval/Settings/Models/BoolAppSettingsEntry.cs @@ -6,7 +6,7 @@ using Pixeval.Controls.Settings; namespace Pixeval.Settings.Models; -public class BoolAppSettingsEntry( +public partial class BoolAppSettingsEntry( AppSettings appSettings, Expression> property) : SingleValueSettingsEntry(appSettings, property) diff --git a/src/Pixeval/Settings/Models/ColorAppSettingsEntry.cs b/src/Pixeval/Settings/Models/ColorAppSettingsEntry.cs index 7d1067ba..93b11a66 100644 --- a/src/Pixeval/Settings/Models/ColorAppSettingsEntry.cs +++ b/src/Pixeval/Settings/Models/ColorAppSettingsEntry.cs @@ -27,7 +27,7 @@ using Pixeval.Controls.Settings; namespace Pixeval.Settings.Models; -public class ColorAppSettingsEntry( +public partial class ColorAppSettingsEntry( AppSettings appSettings, Expression> property) : SingleValueSettingsEntry(appSettings, property) diff --git a/src/Pixeval/Settings/Models/DateRangeWithSwitchAppSettingsEntry.cs b/src/Pixeval/Settings/Models/DateRangeWithSwitchAppSettingsEntry.cs index 3a84f52e..4e9f96b6 100644 --- a/src/Pixeval/Settings/Models/DateRangeWithSwitchAppSettingsEntry.cs +++ b/src/Pixeval/Settings/Models/DateRangeWithSwitchAppSettingsEntry.cs @@ -4,7 +4,7 @@ using Pixeval.Controls.Settings; namespace Pixeval.Settings.Models; -public class DateRangeWithSwitchAppSettingsEntry( +public partial class DateRangeWithSwitchAppSettingsEntry( AppSettings appSettings) : BoolAppSettingsEntry(appSettings, t => t.UsePreciseRangeForSearch) { diff --git a/src/Pixeval/Settings/Models/DownloadMacroAppSettingsEntry.cs b/src/Pixeval/Settings/Models/DownloadMacroAppSettingsEntry.cs index e21e82ad..8467c3a5 100644 --- a/src/Pixeval/Settings/Models/DownloadMacroAppSettingsEntry.cs +++ b/src/Pixeval/Settings/Models/DownloadMacroAppSettingsEntry.cs @@ -8,7 +8,7 @@ using Pixeval.Download.Macros; namespace Pixeval.Settings.Models; -public class DownloadMacroAppSettingsEntry( +public partial class DownloadMacroAppSettingsEntry( AppSettings appSettings) : StringAppSettingsEntry(appSettings, t => t.DownloadPathMacro) { @@ -34,7 +34,7 @@ public class DownloadMacroAppSettingsEntry( ["manga_index"] = SettingsPageResources.MangaIndexMacroTooltip }; - public static ICollection AvailableMacros { get; } = MetaPathMacroAttributeHelper.GetAttachedTypeInstances() + public static ICollection AvailableMacros { get; } = MetaPathMacroAttributeHelper.GetIWorkViewModelInstances() .Select(m => new StringRepresentableItem(_macroTooltips[m.Name], $"@{{{(m is IPredicate ? $"{m.Name}=" : m.Name)}}}")) .ToList(); } diff --git a/src/Pixeval/Settings/Models/EnumAppSettingsEntry.cs b/src/Pixeval/Settings/Models/EnumAppSettingsEntry.cs index a2c65d1a..9768714c 100644 --- a/src/Pixeval/Settings/Models/EnumAppSettingsEntry.cs +++ b/src/Pixeval/Settings/Models/EnumAppSettingsEntry.cs @@ -1,46 +1,31 @@ using System; +using System.Collections.Generic; using System.Linq.Expressions; using Microsoft.UI.Xaml; using Pixeval.AppManagement; +using Pixeval.Controls; using Pixeval.Controls.Settings; namespace Pixeval.Settings.Models; -public class EnumAppSettingsEntry( +public partial class EnumAppSettingsEntry( AppSettings appSettings, Expression> property, - Array array) + IReadOnlyList array) : SingleValueSettingsEntry(appSettings, property) { public override FrameworkElement Element => new EnumSettingsCard { Entry = this }; - public Array EnumValues { get; set; } = array; + public IReadOnlyList EnumItems { get; set; } = array; public EnumAppSettingsEntry( AppSettings appSettings, WorkTypeEnum workType, Expression> property, - Array array) + IReadOnlyList array) : this(appSettings, property, array) { Header = SubHeader(workType); HeaderIcon = SubHeaderIcon(workType); } } - -public class EnumAppSettingsEntry( - AppSettings appSettings, - Expression> property) - : EnumAppSettingsEntry(appSettings, property, Enum.GetValues()) - where TEnum : struct, Enum -{ - public EnumAppSettingsEntry( - AppSettings appSettings, - WorkTypeEnum workType, - Expression> property) - : this(appSettings, property) - { - Header = SubHeader(workType); - HeaderIcon = SubHeaderIcon(workType); - } -} diff --git a/src/Pixeval/Settings/Models/FontAppSettingsEntry.cs b/src/Pixeval/Settings/Models/FontAppSettingsEntry.cs index b2c51763..b51dee35 100644 --- a/src/Pixeval/Settings/Models/FontAppSettingsEntry.cs +++ b/src/Pixeval/Settings/Models/FontAppSettingsEntry.cs @@ -8,7 +8,7 @@ using Pixeval.Utilities; namespace Pixeval.Settings.Models; -public class FontAppSettingsEntry( +public partial class FontAppSettingsEntry( AppSettings appSettings, Expression> property) : StringAppSettingsEntry(appSettings, property) diff --git a/src/Pixeval/Settings/Models/IntAppSettingsEntry.cs b/src/Pixeval/Settings/Models/IntAppSettingsEntry.cs index 4b5dc1d1..a9fd0b50 100644 --- a/src/Pixeval/Settings/Models/IntAppSettingsEntry.cs +++ b/src/Pixeval/Settings/Models/IntAppSettingsEntry.cs @@ -5,7 +5,7 @@ using Pixeval.Controls.Settings; namespace Pixeval.Settings.Models; -public class IntAppSettingsEntry( +public partial class IntAppSettingsEntry( AppSettings appSettings, Expression> property) : SingleValueSettingsEntry(appSettings, property) diff --git a/src/Pixeval/Settings/Models/IpWithSwitchAppSettingsEntry.cs b/src/Pixeval/Settings/Models/IpWithSwitchAppSettingsEntry.cs index 750522af..e12cdf53 100644 --- a/src/Pixeval/Settings/Models/IpWithSwitchAppSettingsEntry.cs +++ b/src/Pixeval/Settings/Models/IpWithSwitchAppSettingsEntry.cs @@ -12,7 +12,7 @@ using Symbol = FluentIcons.Common.Symbol; namespace Pixeval.Settings.Models; -public class IpWithSwitchAppSettingsEntry : BoolAppSettingsEntry +public partial class IpWithSwitchAppSettingsEntry : BoolAppSettingsEntry { public IpWithSwitchAppSettingsEntry(AppSettings appSettings) : base(appSettings, t => t.EnableDomainFronting) { diff --git a/src/Pixeval/Settings/Models/LanguageAppSettingsEntry.cs b/src/Pixeval/Settings/Models/LanguageAppSettingsEntry.cs index fe622864..8c54b012 100644 --- a/src/Pixeval/Settings/Models/LanguageAppSettingsEntry.cs +++ b/src/Pixeval/Settings/Models/LanguageAppSettingsEntry.cs @@ -7,7 +7,7 @@ using Pixeval.Pages.Misc; namespace Pixeval.Settings.Models; -public class LanguageAppSettingsEntry( +public partial class LanguageAppSettingsEntry( AppSettings appSettings) : ObservableSettingsEntryBase(appSettings, "", "", default) { diff --git a/src/Pixeval/Settings/Models/ProxyAppSettingsEntry.cs b/src/Pixeval/Settings/Models/ProxyAppSettingsEntry.cs index a035a89c..1805fd90 100644 --- a/src/Pixeval/Settings/Models/ProxyAppSettingsEntry.cs +++ b/src/Pixeval/Settings/Models/ProxyAppSettingsEntry.cs @@ -30,9 +30,9 @@ using Symbol = FluentIcons.Common.Symbol; namespace Pixeval.Settings.Models; -public class ProxyAppSettingsEntry : EnumAppSettingsEntry +public partial class ProxyAppSettingsEntry : EnumAppSettingsEntry { - public ProxyAppSettingsEntry(AppSettings appSettings) : base(appSettings, t => t.ProxyType) + public ProxyAppSettingsEntry(AppSettings appSettings) : base(appSettings, t => t.ProxyType, ProxyTypeExtension.GetItems()) { var member = typeof(AppSettings).GetProperty(nameof(AppSettings.Proxy)); Attribute2 = member?.GetCustomAttribute(); diff --git a/src/Pixeval/Settings/Models/StringAppSettingsEntry.cs b/src/Pixeval/Settings/Models/StringAppSettingsEntry.cs index 14b14d0b..13c951e4 100644 --- a/src/Pixeval/Settings/Models/StringAppSettingsEntry.cs +++ b/src/Pixeval/Settings/Models/StringAppSettingsEntry.cs @@ -6,7 +6,7 @@ using Pixeval.Controls.Settings; namespace Pixeval.Settings.Models; -public class StringAppSettingsEntry( +public partial class StringAppSettingsEntry( AppSettings appSettings, Expression> property) : SingleValueSettingsEntry(appSettings, property) diff --git a/src/Pixeval/Settings/Models/TokenizingAppSettingsEntry.cs b/src/Pixeval/Settings/Models/TokenizingAppSettingsEntry.cs index b790be70..28b31854 100644 --- a/src/Pixeval/Settings/Models/TokenizingAppSettingsEntry.cs +++ b/src/Pixeval/Settings/Models/TokenizingAppSettingsEntry.cs @@ -4,7 +4,7 @@ using Pixeval.Controls.Settings; namespace Pixeval.Settings.Models; -public class TokenizingAppSettingsEntry( +public partial class TokenizingAppSettingsEntry( AppSettings appSettings) : ObservableSettingsEntryBase(appSettings, "", "", default) { diff --git a/src/Pixeval/Settings/SimpleSettingsGroup.cs b/src/Pixeval/Settings/SimpleSettingsGroup.cs index 6828069e..4fb99bde 100644 --- a/src/Pixeval/Settings/SimpleSettingsGroup.cs +++ b/src/Pixeval/Settings/SimpleSettingsGroup.cs @@ -1,12 +1,11 @@ -using System; using System.Collections.Generic; -using Pixeval.Util; +using Pixeval.Pages.Misc; namespace Pixeval.Settings; -public class SimpleSettingsGroup(Enum tag) : List, ISettingsGroup +public partial class SimpleSettingsGroup(SettingsEntryCategory tag) : List, ISettingsGroup { - public string Header { get; } = tag.GetLocalizedResourceContent() ?? ""; + public string Header { get; } = SettingsEntryCategoryExtension.GetResource(tag); - public Enum Tag { get; } = tag; + public SettingsEntryCategory Tag { get; } = tag; } diff --git a/src/Pixeval/Themes/Controls.xaml b/src/Pixeval/Themes/Controls.xaml index e90194aa..c28b64b4 100644 --- a/src/Pixeval/Themes/Controls.xaml +++ b/src/Pixeval/Themes/Controls.xaml @@ -2,9 +2,7 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:controls="using:Pixeval.Controls" - xmlns:enum="using:Pixeval.CoreApi.Global.Enum" - xmlns:numberFormatting="using:Windows.Globalization.NumberFormatting" - xmlns:winUi="using:CommunityToolkit.WinUI"> + xmlns:numberFormatting="using:Windows.Globalization.NumberFormatting"> @@ -62,7 +60,7 @@ x:Key="SimpleWorkTypeComboBoxStyle" BasedOn="{StaticResource DefaultComboBoxStyle}" TargetType="controls:EnumComboBox"> - + @@ -70,7 +68,7 @@ x:Key="PrivacyPolicyComboBoxStyle" BasedOn="{StaticResource DefaultComboBoxStyle}" TargetType="controls:EnumComboBox"> - + @@ -78,7 +76,7 @@ x:Key="WorkSortOptionComboBoxStyle" BasedOn="{StaticResource DefaultComboBoxStyle}" TargetType="controls:EnumComboBox"> - + diff --git a/src/Pixeval/Util/AttributeHelper.cs b/src/Pixeval/Util/AttributeHelper.cs deleted file mode 100644 index e3aaebca..00000000 --- a/src/Pixeval/Util/AttributeHelper.cs +++ /dev/null @@ -1,158 +0,0 @@ -#region Copyright (c) Pixeval/Pixeval -// GPL v3 License -// -// Pixeval/Pixeval -// Copyright (c) 2023 Pixeval/AttributeHelper.cs -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -#endregion - -using System; -using System.Collections.Generic; -using System.Reflection; -using Microsoft.UI.Xaml; -using Pixeval.Attributes; -using Pixeval.Controls; -using Pixeval.CoreApi.Global.Enum; -using Pixeval.Options; -using WinUI3Utilities; - -namespace Pixeval.Util; - -public static class AttributeHelper -{ - public static TAttribute? GetCustomAttribute(this Enum e) where TAttribute : Attribute - { - return e.GetType().GetField(e.ToString())?.GetCustomAttribute(typeof(TAttribute), false) as TAttribute; - } -} - -public static class LocalizedResourceAttributeHelper -{ - public static List GetLocalizedResourceContents() where T : struct, Enum - { - return GetLocalizedResourceContents(Enum.GetValues()); - } - - public static List GetLocalizedResourceContents(T[] enumType) - { - return GetLocalizedResourceContents(enumType2: enumType); - } - - public static List GetLocalizedResourceContents(Array enumType2) - { - var items = new List(); - foreach (var value in enumType2) - if (value is Enum t && t.GetLocalizedResourceContent() is { } content) - items.Add(new StringRepresentableItem(t, content)); - return items; - } - - public static string? GetLocalizedResourceContent(this Enum e) - { - if (_predefinedResources.TryGetValue(e, out var v)) - return v; - var attribute = e.GetCustomAttribute(); - return attribute?.GetLocalizedResourceContent(); - } - - public static LocalizedResource? GetLocalizedResource(this Enum e) - { - return e.GetCustomAttribute(); - } - - public static string? GetLocalizedResourceContent(this LocalizedResource attribute) - { - return GetLocalizedResourceContent(attribute.ResourceLoader, attribute.Key); - } - - public static string? GetLocalizedResourceContent(Type resourceLoader, string key) - { - return resourceLoader.GetMember(key, BindingFlags.Static | BindingFlags.Public) switch - { - [FieldInfo fi] => fi?.GetValue(null), - [PropertyInfo pi] => pi?.GetValue(null), - _ => null - } as string; - } - - private static readonly Dictionary _predefinedResources = new() - { - [ProxyType.Http] = "http/https", - [ProxyType.Socks4] = "socks4", - [ProxyType.Socks4A] = "socks4a", - [ProxyType.Socks5] = "socks5", - - [TargetFilter.ForAndroid] = MiscResources.TargetFilterForAndroid, - [TargetFilter.ForIos] = MiscResources.TargetFilterForIOS, - - [BackdropType.Acrylic] = MiscResources.AcrylicBackdrop, - [BackdropType.Mica] = MiscResources.MicaBackdrop, - [BackdropType.MicaAlt] = MiscResources.MicaAltBackdrop, - [BackdropType.None] = MiscResources.NoneBackdrop, - - [SearchIllustrationTagMatchOption.PartialMatchForTags] = MiscResources.SearchIllustrationTagMatchOptionPartialMatchForTags, - [SearchIllustrationTagMatchOption.ExactMatchForTags] = MiscResources.SearchIllustrationTagMatchOptionExactMatchForTags, - [SearchIllustrationTagMatchOption.TitleAndCaption] = MiscResources.SearchIllustrationTagMatchOptionTitleAndCaption, - - [SearchNovelTagMatchOption.PartialMatchForTags] = MiscResources.SearchNovelTagMatchOptionPartialMatchForTags, - [SearchNovelTagMatchOption.ExactMatchForTags] = MiscResources.SearchNovelTagMatchOptionExactMatchForTags, - [SearchNovelTagMatchOption.Text] = MiscResources.SearchNovelTagMatchOptionText, - [SearchNovelTagMatchOption.Keyword] = MiscResources.SearchNovelTagMatchOptionCaption, - - [ElementTheme.Dark] = MiscResources.AppThemeDark, - [ElementTheme.Light] = MiscResources.AppThemeLight, - [ElementTheme.Default] = MiscResources.AppThemeSystemDefault, - - [WorkSortOption.PopularityDescending] = MiscResources.WorkSortOptionPopularityDescending, - [WorkSortOption.PublishDateAscending] = MiscResources.WorkSortOptionPublishDateAscending, - [WorkSortOption.PublishDateDescending] = MiscResources.WorkSortOptionPublishDateDescending, - [WorkSortOption.DoNotSort] = MiscResources.WorkSortOptionDoNotSort, - - [SearchDuration.Undecided] = MiscResources.SearchDurationUndecided, - [SearchDuration.WithinLastDay] = MiscResources.SearchDurationWithinLastDay, - [SearchDuration.WithinLastWeek] = MiscResources.SearchDurationWithinLastWeek, - [SearchDuration.WithinLastMonth] = MiscResources.SearchDurationWithinLastMonth, - [SearchDuration.WithinLastHalfYear] = MiscResources.SearchDurationWithinLastHalfYear, - [SearchDuration.WithinLastYear] = MiscResources.SearchDurationWithinLastYear, - - [PrivacyPolicy.Public] = MiscResources.PrivacyPolicyPublic, - [PrivacyPolicy.Private] = MiscResources.PrivacyPolicyPrivate, - - [WorkType.Illust] = MiscResources.WorkTypeIllust, - [WorkType.Manga] = MiscResources.WorkTypeManga, - [WorkType.Novel] = MiscResources.WorkTypeNovel, - - [SimpleWorkType.IllustAndManga] = MiscResources.SimpleWorkTypeIllustAndManga, - [SimpleWorkType.Novel] = MiscResources.SimpleWorkTypeNovel, - - [RankOption.Day] = RankingsPageResources.RankOptionDay, - [RankOption.Week] = RankingsPageResources.RankOptionWeek, - [RankOption.Month] = RankingsPageResources.RankOptionMonth, - [RankOption.DayMale] = RankingsPageResources.RankOptionDayMale, - [RankOption.DayFemale] = RankingsPageResources.RankOptionDayFemale, - [RankOption.DayManga] = RankingsPageResources.RankOptionDayManga, - [RankOption.WeekManga] = RankingsPageResources.RankOptionWeekManga, - [RankOption.MonthManga] = RankingsPageResources.RankOptionMonthManga, - [RankOption.WeekOriginal] = RankingsPageResources.RankOptionWeekOriginal, - [RankOption.WeekRookie] = RankingsPageResources.RankOptionWeekRookie, - [RankOption.DayR18] = RankingsPageResources.RankOptionDayR18, - [RankOption.DayMaleR18] = RankingsPageResources.RankOptionDayMaleR18, - [RankOption.DayFemaleR18] = RankingsPageResources.RankOptionDayFemaleR18, - [RankOption.WeekR18] = RankingsPageResources.RankOptionWeekR18, - [RankOption.WeekR18G] = RankingsPageResources.RankOptionWeekR18G, - [RankOption.DayAi] = RankingsPageResources.RankOptionDayAi, - [RankOption.DayR18Ai] = RankingsPageResources.RankOptionDayR18Ai - }; -} diff --git a/src/Pixeval/Util/ComponentModels/DetailedObservableObject.cs b/src/Pixeval/Util/ComponentModels/DetailedObservableObject.cs index 8f10aa86..89edd6be 100644 --- a/src/Pixeval/Util/ComponentModels/DetailedObservableObject.cs +++ b/src/Pixeval/Util/ComponentModels/DetailedObservableObject.cs @@ -23,7 +23,7 @@ using CommunityToolkit.Mvvm.ComponentModel; namespace Pixeval.Util.ComponentModels; -public class DetailedObservableObject : ObservableObject, INotifyDetailedPropertyChanging, INotifyDetailedPropertyChanged +public partial class DetailedObservableObject : ObservableObject, INotifyDetailedPropertyChanging, INotifyDetailedPropertyChanged { public event DetailedPropertyChangedEventHandler? DetailedPropertyChanged; diff --git a/src/Pixeval/Util/ComponentModels/DetailedUiObservableObject.cs b/src/Pixeval/Util/ComponentModels/DetailedUiObservableObject.cs index b2d1cdfa..65bea516 100644 --- a/src/Pixeval/Util/ComponentModels/DetailedUiObservableObject.cs +++ b/src/Pixeval/Util/ComponentModels/DetailedUiObservableObject.cs @@ -3,7 +3,7 @@ using System.Runtime.CompilerServices; namespace Pixeval.Util.ComponentModels; -public class DetailedUiObservableObject(ulong hWnd) : UiObservableObject(hWnd), INotifyDetailedPropertyChanging, INotifyDetailedPropertyChanged +public partial class DetailedUiObservableObject(ulong hWnd) : UiObservableObject(hWnd), INotifyDetailedPropertyChanging, INotifyDetailedPropertyChanged { public event DetailedPropertyChangedEventHandler? DetailedPropertyChanged; diff --git a/src/Pixeval/Util/ComponentModels/UiObservableObject.cs b/src/Pixeval/Util/ComponentModels/UiObservableObject.cs index 612cf03d..c34cecde 100644 --- a/src/Pixeval/Util/ComponentModels/UiObservableObject.cs +++ b/src/Pixeval/Util/ComponentModels/UiObservableObject.cs @@ -4,7 +4,7 @@ using Pixeval.Controls.Windowing; namespace Pixeval.Util.ComponentModels; -public class UiObservableObject(ulong hWnd) : ObservableObject +public partial class UiObservableObject(ulong hWnd) : ObservableObject { public ulong HWnd { get; } = hWnd; diff --git a/src/Pixeval/Util/DelegatedHttpMessageHandler.cs b/src/Pixeval/Util/DelegatedHttpMessageHandler.cs index af8bed95..498d13c0 100644 --- a/src/Pixeval/Util/DelegatedHttpMessageHandler.cs +++ b/src/Pixeval/Util/DelegatedHttpMessageHandler.cs @@ -24,7 +24,7 @@ using System.Threading.Tasks; namespace Pixeval.Util; -public class DelegatedHttpMessageHandler(HttpMessageInvoker @delegate) : HttpMessageHandler +public partial class DelegatedHttpMessageHandler(HttpMessageInvoker @delegate) : HttpMessageHandler { protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { diff --git a/src/Pixeval/Util/IO/FileCache.cs b/src/Pixeval/Util/IO/FileCache.cs index 7ea5fc47..e7296644 100644 --- a/src/Pixeval/Util/IO/FileCache.cs +++ b/src/Pixeval/Util/IO/FileCache.cs @@ -27,6 +27,7 @@ using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Text; using System.Text.Json; +using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; using CommunityToolkit.Diagnostics; @@ -108,7 +109,7 @@ public class FileCache break; } default: - await File.WriteAllBytesAsync(filePath, JsonSerializer.SerializeToUtf8Bytes(data)); + ThrowHelper.NotSupported("AOT"); break; } @@ -398,11 +399,7 @@ public class FileCache { var type when type == typeof(Stream) || type.IsAssignableTo(typeof(Stream)) => (T)(object)await file.OpenRead().CopyToMemoryStreamAsync(true), var type when type == typeof(byte[]) => (T)(object)File.ReadAllBytesAsync(file.FullName), - _ => await Functions.Block(async () => - { - await using var stream = file.OpenRead(); - return (await JsonSerializer.DeserializeAsync(stream))!; - }) + _ => ThrowHelper.NotSupported("AOT") }; } } @@ -558,7 +555,7 @@ public class FileCache private async Task WriteIndexAsync() { - await File.WriteAllBytesAsync(_indexFile.FullName, JsonSerializer.SerializeToUtf8Bytes(_index)); + await File.WriteAllBytesAsync(_indexFile.FullName, JsonSerializer.SerializeToUtf8Bytes(_expireIndex, typeof(Dictionary), IndexContext.Default)); } private async Task LoadIndexAsync() @@ -566,13 +563,13 @@ public class FileCache if (_indexFile.Exists) { await using var openRead = _indexFile.OpenRead(); - _index = (await JsonSerializer.DeserializeAsync>(openRead))!; + _index = (Dictionary)(await JsonSerializer.DeserializeAsync(openRead, typeof(Dictionary), IndexContext.Default))!; } } private async Task WriteExpireIndexAsync() { - await File.WriteAllBytesAsync(_expireIndexFile.FullName, JsonSerializer.SerializeToUtf8Bytes(_expireIndex)); + await File.WriteAllBytesAsync(_expireIndexFile.FullName, JsonSerializer.SerializeToUtf8Bytes(_expireIndex, typeof(Dictionary), ExpireIndexContext.Default)); } private async Task LoadExpireIndexAsync() @@ -580,7 +577,7 @@ public class FileCache if (_expireIndexFile.Exists) { await using var openRead = _expireIndexFile.OpenRead(); - _expireIndex = (await JsonSerializer.DeserializeAsync>(openRead))!; + _expireIndex = (Dictionary)(await JsonSerializer.DeserializeAsync(openRead, typeof(Dictionary), ExpireIndexContext.Default))!; } } @@ -593,9 +590,15 @@ public class FileCache { string str => new Guid(HashAndTruncateTo128Bit(Encoding.UTF8.GetBytes(str))), byte[] bytes => new Guid(HashAndTruncateTo128Bit(bytes)), - var number and (int or uint or long or ulong) => Functions.Block(() => + var number and (int or uint) => Functions.Block(() => { - Span span = stackalloc byte[Marshal.SizeOf(number)]; + Span span = stackalloc byte[sizeof(int)]; + Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(span), number); + return new Guid(HashAndTruncateTo128Bit(span)); + }), + var number and (long or ulong) => Functions.Block(() => + { + Span span = stackalloc byte[sizeof(long)]; Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(span), number); return new Guid(HashAndTruncateTo128Bit(span)); }), @@ -644,3 +647,9 @@ public enum CacheState /// Active = 2 } + +[JsonSerializable(typeof(Dictionary))] +internal partial class IndexContext : JsonSerializerContext; + +[JsonSerializable(typeof(Dictionary))] +internal partial class ExpireIndexContext : JsonSerializerContext; diff --git a/src/Pixeval/Util/IO/IOHelper.cs b/src/Pixeval/Util/IO/IOHelper.cs index 83fb755f..9d9a3c74 100644 --- a/src/Pixeval/Util/IO/IOHelper.cs +++ b/src/Pixeval/Util/IO/IOHelper.cs @@ -161,20 +161,20 @@ public static partial class IoHelper return result; } - public static async Task WriteZipAsync(IReadOnlyDictionary streams, bool dispose) + public static async Task WriteZipAsync(IReadOnlyList names, IReadOnlyList streams, bool dispose) { var zipStream = _recyclableMemoryStreamManager.GetStream(); var zipArchive = new ZipArchive(zipStream, ZipArchiveMode.Create, true); - foreach (var (key, value) in streams) + for (var i = 0; i < streams.Count; i++) { // ReSharper disable once AccessToDisposedClosure - var entry = zipArchive.CreateEntry(key); + var entry = zipArchive.CreateEntry(names[i]); await using var entryStream = entry.Open(); - await value.CopyToAsync(entryStream); + await streams[i].CopyToAsync(entryStream); if (dispose) - await value.DisposeAsync(); + await streams[i].DisposeAsync(); } zipArchive.Dispose(); diff --git a/src/Pixeval/Util/RankOptionExtension.cs b/src/Pixeval/Util/RankOptionExtension.cs new file mode 100644 index 00000000..bbcefc06 --- /dev/null +++ b/src/Pixeval/Util/RankOptionExtension.cs @@ -0,0 +1,149 @@ +#region Copyright + +// GPL v3 License +// +// Pixeval/Pixeval +// Copyright (c) 2024 Pixeval/RankOptionExtension.cs +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#endregion + +using Microsoft.UI.Xaml; +using Pixeval.Attributes; +using WinUI3Utilities; + +namespace Pixeval.CoreApi.Global.Enum; + +[AttachedLocalizationMetadata(typeof(RankingsPageResources))] +[AttachedLocalizedResource(nameof(RankOption.Day), nameof(RankingsPageResources.RankOptionDay))] +[AttachedLocalizedResource(nameof(RankOption.Week), nameof(RankingsPageResources.RankOptionWeek))] +[AttachedLocalizedResource(nameof(RankOption.Month), nameof(RankingsPageResources.RankOptionMonth))] +[AttachedLocalizedResource(nameof(RankOption.DayMale), nameof(RankingsPageResources.RankOptionDayMale))] +[AttachedLocalizedResource(nameof(RankOption.DayFemale), nameof(RankingsPageResources.RankOptionDayFemale))] +[AttachedLocalizedResource(nameof(RankOption.DayManga), nameof(RankingsPageResources.RankOptionDayManga))] +[AttachedLocalizedResource(nameof(RankOption.WeekManga), nameof(RankingsPageResources.RankOptionWeekManga))] +[AttachedLocalizedResource(nameof(RankOption.MonthManga), nameof(RankingsPageResources.RankOptionMonthManga))] +[AttachedLocalizedResource(nameof(RankOption.WeekOriginal), nameof(RankingsPageResources.RankOptionWeekOriginal))] +[AttachedLocalizedResource(nameof(RankOption.WeekRookie), nameof(RankingsPageResources.RankOptionWeekRookie))] +[AttachedLocalizedResource(nameof(RankOption.DayR18), nameof(RankingsPageResources.RankOptionDayR18))] +[AttachedLocalizedResource(nameof(RankOption.DayMaleR18), nameof(RankingsPageResources.RankOptionDayMaleR18))] +[AttachedLocalizedResource(nameof(RankOption.DayFemaleR18), nameof(RankingsPageResources.RankOptionDayFemaleR18))] +[AttachedLocalizedResource(nameof(RankOption.WeekR18), nameof(RankingsPageResources.RankOptionWeekR18))] +[AttachedLocalizedResource(nameof(RankOption.WeekR18G), nameof(RankingsPageResources.RankOptionWeekR18G))] +[AttachedLocalizedResource(nameof(RankOption.DayAi), nameof(RankingsPageResources.RankOptionDayAi))] +[AttachedLocalizedResource(nameof(RankOption.DayR18Ai), nameof(RankingsPageResources.RankOptionDayR18Ai))] +public static partial class RankOptionExtension; + +[AttachedLocalizationMetadata(typeof(RankingsPageResources))] +[AttachedLocalizedResource(nameof(RankOption.Day), nameof(RankingsPageResources.RankOptionDay))] +[AttachedLocalizedResource(nameof(RankOption.Week), nameof(RankingsPageResources.RankOptionWeek))] +[AttachedLocalizedResource(nameof(RankOption.DayMale), nameof(RankingsPageResources.RankOptionDayMale))] +[AttachedLocalizedResource(nameof(RankOption.DayFemale), nameof(RankingsPageResources.RankOptionDayFemale))] +[AttachedLocalizedResource(nameof(RankOption.WeekOriginal), nameof(RankingsPageResources.RankOptionWeekOriginal))] +[AttachedLocalizedResource(nameof(RankOption.WeekRookie), nameof(RankingsPageResources.RankOptionWeekRookie))] +[AttachedLocalizedResource(nameof(RankOption.DayR18), nameof(RankingsPageResources.RankOptionDayR18))] +[AttachedLocalizedResource(nameof(RankOption.DayMaleR18), nameof(RankingsPageResources.RankOptionDayMaleR18))] +[AttachedLocalizedResource(nameof(RankOption.DayFemaleR18), nameof(RankingsPageResources.RankOptionDayFemaleR18))] +[AttachedLocalizedResource(nameof(RankOption.WeekR18), nameof(RankingsPageResources.RankOptionWeekR18))] +[AttachedLocalizedResource(nameof(RankOption.WeekR18G), nameof(RankingsPageResources.RankOptionWeekR18G))] +[AttachedLocalizedResource(nameof(RankOption.DayAi), nameof(RankingsPageResources.RankOptionDayAi))] +[AttachedLocalizedResource(nameof(RankOption.DayR18Ai), nameof(RankingsPageResources.RankOptionDayR18Ai))] +public static partial class NovelRankOptionExtension +{ + public static readonly RankOption[] NovelRankOptions = + [ + RankOption.Day, + RankOption.Week, + // RankOption.Month, + RankOption.DayMale, + RankOption.DayFemale, + // RankOption.DayManga, + // RankOption.WeekManga, + // RankOption.MonthManga, + RankOption.WeekOriginal, + RankOption.WeekRookie, + RankOption.DayR18, + RankOption.DayMaleR18, + RankOption.DayFemaleR18, + RankOption.WeekR18, + RankOption.WeekR18G, + RankOption.DayAi, + RankOption.DayR18Ai + ]; +} + + +[AttachedLocalizationMetadata(typeof(MiscResources))] +[AttachedLocalizedResource(nameof(TargetFilter.ForAndroid), nameof(MiscResources.TargetFilterForAndroid))] +[AttachedLocalizedResource(nameof(TargetFilter.ForIos), nameof(MiscResources.TargetFilterForIOS))] +public static partial class TargetFilterExtension; + +[AttachedLocalizationMetadata(typeof(MiscResources))] +[AttachedLocalizedResource(nameof(BackdropType.Acrylic), nameof(MiscResources.AcrylicBackdrop))] +[AttachedLocalizedResource(nameof(BackdropType.Mica), nameof(MiscResources.MicaBackdrop))] +[AttachedLocalizedResource(nameof(BackdropType.MicaAlt), nameof(MiscResources.MicaAltBackdrop))] +[AttachedLocalizedResource(nameof(BackdropType.None), nameof(MiscResources.NoneBackdrop))] +public static partial class BackdropTypeExtension; + +[AttachedLocalizationMetadata(typeof(MiscResources))] +[AttachedLocalizedResource(nameof(SearchIllustrationTagMatchOption.PartialMatchForTags), nameof(MiscResources.SearchIllustrationTagMatchOptionPartialMatchForTags))] +[AttachedLocalizedResource(nameof(SearchIllustrationTagMatchOption.ExactMatchForTags), nameof(MiscResources.SearchIllustrationTagMatchOptionExactMatchForTags))] +[AttachedLocalizedResource(nameof(SearchIllustrationTagMatchOption.TitleAndCaption), nameof(MiscResources.SearchIllustrationTagMatchOptionTitleAndCaption))] +public static partial class SearchIllustrationTagMatchOptionExtension; + +[AttachedLocalizationMetadata(typeof(MiscResources))] +[AttachedLocalizedResource(nameof(SearchNovelTagMatchOption.PartialMatchForTags), nameof(MiscResources.SearchNovelTagMatchOptionPartialMatchForTags))] +[AttachedLocalizedResource(nameof(SearchNovelTagMatchOption.ExactMatchForTags), nameof(MiscResources.SearchNovelTagMatchOptionExactMatchForTags))] +[AttachedLocalizedResource(nameof(SearchNovelTagMatchOption.Text), nameof(MiscResources.SearchNovelTagMatchOptionText))] +[AttachedLocalizedResource(nameof(SearchNovelTagMatchOption.Keyword), nameof(MiscResources.SearchNovelTagMatchOptionCaption))] +public static partial class SearchNovelTagMatchOptionExtension; + +[AttachedLocalizationMetadata(typeof(MiscResources))] +[AttachedLocalizedResource(nameof(ElementTheme.Dark), nameof(MiscResources.AppThemeDark))] +[AttachedLocalizedResource(nameof(ElementTheme.Light), nameof(MiscResources.AppThemeLight))] +[AttachedLocalizedResource(nameof(ElementTheme.Default), nameof(MiscResources.AppThemeSystemDefault))] +public static partial class ElementThemeExtension; + +[AttachedLocalizationMetadata(typeof(MiscResources))] +[AttachedLocalizedResource(nameof(WorkSortOption.PopularityDescending), nameof(MiscResources.WorkSortOptionPopularityDescending))] +[AttachedLocalizedResource(nameof(WorkSortOption.PublishDateAscending), nameof(MiscResources.WorkSortOptionPublishDateAscending))] +[AttachedLocalizedResource(nameof(WorkSortOption.PublishDateDescending), nameof(MiscResources.WorkSortOptionPublishDateDescending))] +[AttachedLocalizedResource(nameof(WorkSortOption.DoNotSort), nameof(MiscResources.WorkSortOptionDoNotSort))] +public static partial class WorkSortOptionExtension; + +[AttachedLocalizationMetadata(typeof(MiscResources))] +[AttachedLocalizedResource(nameof(SearchDuration.Undecided), nameof(MiscResources.SearchDurationUndecided))] +[AttachedLocalizedResource(nameof(SearchDuration.WithinLastDay), nameof(MiscResources.SearchDurationWithinLastDay))] +[AttachedLocalizedResource(nameof(SearchDuration.WithinLastWeek), nameof(MiscResources.SearchDurationWithinLastWeek))] +[AttachedLocalizedResource(nameof(SearchDuration.WithinLastMonth), nameof(MiscResources.SearchDurationWithinLastMonth))] +[AttachedLocalizedResource(nameof(SearchDuration.WithinLastHalfYear), nameof(MiscResources.SearchDurationWithinLastHalfYear))] +[AttachedLocalizedResource(nameof(SearchDuration.WithinLastYear), nameof(MiscResources.SearchDurationWithinLastYear))] +public static partial class SearchDurationExtension; + +[AttachedLocalizationMetadata(typeof(MiscResources))] +[AttachedLocalizedResource(nameof(PrivacyPolicy.Public), nameof(MiscResources.PrivacyPolicyPublic))] +[AttachedLocalizedResource(nameof(PrivacyPolicy.Private), nameof(MiscResources.PrivacyPolicyPrivate))] +public static partial class PrivacyPolicyExtension; + +[AttachedLocalizationMetadata(typeof(MiscResources))] +[AttachedLocalizedResource(nameof(WorkType.Illust), nameof(MiscResources.WorkTypeIllust))] +[AttachedLocalizedResource(nameof(WorkType.Manga), nameof(MiscResources.WorkTypeManga))] +[AttachedLocalizedResource(nameof(WorkType.Novel), nameof(MiscResources.WorkTypeNovel))] +public static partial class WorkTypeExtension; + +[AttachedLocalizationMetadata(typeof(MiscResources))] +[AttachedLocalizedResource(nameof(SimpleWorkType.IllustAndManga), nameof(MiscResources.SimpleWorkTypeIllustAndManga))] +[AttachedLocalizedResource(nameof(SimpleWorkType.Novel), nameof(MiscResources.SimpleWorkTypeNovel))] +public static partial class SimpleWorkTypeExtension; diff --git a/src/Pixeval/Util/UI/ReplyEmojiHelper.Enumerator.cs b/src/Pixeval/Util/UI/ReplyEmojiHelper.Enumerator.cs index 107783b2..fcc1a76d 100644 --- a/src/Pixeval/Util/UI/ReplyEmojiHelper.Enumerator.cs +++ b/src/Pixeval/Util/UI/ReplyEmojiHelper.Enumerator.cs @@ -19,8 +19,10 @@ #endregion using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using System.Text; +using System.Text.RegularExpressions; namespace Pixeval.Util.UI; @@ -28,7 +30,9 @@ public static partial class ReplyEmojiHelper { public static IEnumerable EnumerateTokens(string content) { - var table = BuildEmojiReplacementIndexTableOfReplyContent(content); + var table = Regex.Matches(content, string.Join("|", _stringToEmojiTable.Keys.Select(Regex.Escape))) + .ToImmutableDictionary(m => m.Index, m => (_stringToEmojiTable[m.Value], m.Value.Length)); + // var table = BuildEmojiReplacementIndexTableOfReplyContent(content); if (table.Count is 0) { yield return new ReplyContentToken.TextToken(content); diff --git a/src/Pixeval/Util/UI/ReplyEmojiHelper.cs b/src/Pixeval/Util/UI/ReplyEmojiHelper.cs index aace0863..61791398 100644 --- a/src/Pixeval/Util/UI/ReplyEmojiHelper.cs +++ b/src/Pixeval/Util/UI/ReplyEmojiHelper.cs @@ -1,4 +1,4 @@ -#region Copyright (c) Pixeval/Pixeval +#region Copyright (c) Pixeval/Pixeval // GPL v3 License // // Pixeval/Pixeval @@ -21,8 +21,6 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; -using System.Text.RegularExpressions; namespace Pixeval.Util.UI; @@ -57,9 +55,9 @@ public static partial class ReplyEmojiHelper return _stringToEmojiTable[content]; } - public static IReadOnlyDictionary BuildEmojiReplacementIndexTableOfReplyContent(string replyContent) - { - return Regex.Matches(replyContent, string.Join("|", _stringToEmojiTable.Keys.Select(Regex.Escape))) - .ToImmutableDictionary(m => m.Index, m => (_stringToEmojiTable[m.Value], m.Value.Length)); - } -} \ No newline at end of file + //public static IReadOnlyDictionary BuildEmojiReplacementIndexTableOfReplyContent(string replyContent) + //{ + // return Regex.Matches(replyContent, string.Join("|", _stringToEmojiTable.Keys.Select(Regex.Escape))) + // .ToImmutableDictionary(m => m.Index, m => (_stringToEmojiTable[m.Value], m.Value.Length)); + //} +} diff --git a/src/Pixeval/Util/WorkViewModelComparers.cs b/src/Pixeval/Util/WorkViewModelComparers.cs index c90e2cda..51c52a58 100644 --- a/src/Pixeval/Util/WorkViewModelComparers.cs +++ b/src/Pixeval/Util/WorkViewModelComparers.cs @@ -38,8 +38,8 @@ public class WorkViewModelPublishDateComparer : IComparer, IComp if (x is null || y is null) return 0; - // 比较Id以保证稳定排序 var result = x.PublishDate.CompareTo(y.PublishDate); + // 比较Id以保证稳定排序 return result is 0 ? x.Id.CompareTo(y.Id) : result; } } @@ -58,8 +58,8 @@ public class WorkViewModelBookmarkComparer : IComparer, ICompare if (x is null || y is null) return 0; - // 比较Id以保证稳定排序 var result = x.TotalBookmarks.CompareTo(y.TotalBookmarks); + // 比较Id以保证稳定排序 return result is 0 ? x.Id.CompareTo(y.Id) : result; } }