Compare commits

..

1 Commits

Author SHA1 Message Date
DismissedLight
7a572631e9 Merge pull request #1415 from DGP-Studio/develop 2024-02-21 20:35:56 +08:00
51 changed files with 163 additions and 499 deletions

View File

@@ -15,7 +15,7 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="MSTest.TestAdapter" Version="3.2.1" />
<PackageReference Include="MSTest.TestFramework" Version="3.2.1" />
<PackageReference Include="coverlet.collector" Version="6.0.1">
<PackageReference Include="coverlet.collector" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

@@ -5,6 +5,7 @@ using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Shapes;
using System.Collections.Specialized;
using System.Runtime.InteropServices;
namespace Snap.Hutao.Control.Brush;

View File

@@ -4,6 +4,7 @@
using CommunityToolkit.WinUI.Collections;
using CommunityToolkit.WinUI.Helpers;
using Microsoft.UI.Xaml.Data;
using Snap.Hutao.Core.ExceptionService;
using System.Collections;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
@@ -207,11 +208,13 @@ internal sealed class AdvancedCollectionView<T> : IAdvancedCollectionView<T>, IN
public void Add(T item)
{
ThrowHelper.NotSupportedIf(IsReadOnly, "Collection is read-only.");
source.Add(item);
}
public void Clear()
{
ThrowHelper.NotSupportedIf(IsReadOnly, "Collection is read-only.");
source.Clear();
}
@@ -227,6 +230,7 @@ internal sealed class AdvancedCollectionView<T> : IAdvancedCollectionView<T>, IN
public bool Remove(T item)
{
ThrowHelper.NotSupportedIf(IsReadOnly, "Collection is read-only.");
source.Remove(item);
return true;
}
@@ -239,6 +243,7 @@ internal sealed class AdvancedCollectionView<T> : IAdvancedCollectionView<T>, IN
public void Insert(int index, T item)
{
ThrowHelper.NotSupportedIf(IsReadOnly, "Collection is read-only.");
source.Insert(index, item);
}

View File

@@ -1,6 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Control;
internal interface IXamlElementAccessor;

View File

@@ -1,6 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core.DependencyInjection.Abstraction;
using Snap.Hutao.Core.IO;
namespace Snap.Hutao.Core.Caching;
@@ -9,7 +10,7 @@ namespace Snap.Hutao.Core.Caching;
/// 为图像缓存提供抽象
/// </summary>
[HighQuality]
internal interface IImageCache
internal interface IImageCache : ICastService
{
/// <summary>
/// Gets the file path containing cached item for given Uri

View File

@@ -25,7 +25,6 @@ internal static partial class IocHttpClientConfiguration
.ConfigurePrimaryHttpMessageHandler((handler, provider) =>
{
HttpClientHandler clientHandler = (HttpClientHandler)handler;
clientHandler.AllowAutoRedirect = true;
clientHandler.UseProxy = true;
clientHandler.Proxy = provider.GetRequiredService<DynamicHttpProxy>();
});

View File

@@ -23,16 +23,8 @@ internal sealed partial class ExceptionRecorder
public void Record(Application app)
{
app.UnhandledException += OnAppUnhandledException;
app.DebugSettings.FailFastOnErrors = false;
app.DebugSettings.IsBindingTracingEnabled = true;
app.DebugSettings.BindingFailed += OnXamlBindingFailed;
app.DebugSettings.IsXamlResourceReferenceTracingEnabled = true;
app.DebugSettings.XamlResourceReferenceFailed += OnXamlResourceReferenceFailed;
app.DebugSettings.LayoutCycleTracingLevel = LayoutCycleTracingLevel.High;
app.DebugSettings.LayoutCycleDebugBreakLevel = LayoutCycleDebugBreakLevel.High;
}
[SuppressMessage("", "CA2012")]

View File

@@ -7,6 +7,4 @@ internal enum HutaoExceptionKind
{
None,
ServiceTypeCastFailed,
FileSystemCreateFileInsufficientPermissions,
PrivateNamedPipeContentHashIncorrect,
}

View File

@@ -29,7 +29,7 @@ internal readonly struct TempFile : IDisposable
}
catch (UnauthorizedAccessException ex)
{
HutaoException.Throw(HutaoExceptionKind.FileSystemCreateFileInsufficientPermissions, SH.CoreIOTempFileCreateFail, ex);
ThrowHelper.RuntimeEnvironment(SH.CoreIOTempFileCreateFail, ex);
}
if (delete)

View File

@@ -148,15 +148,17 @@ internal sealed partial class Activation : IActivation
await taskContext.SwitchToBackgroundAsync();
if (serviceProvider.GetRequiredService<IMetadataService>() is IMetadataServiceInitialization metadataServiceInitialization)
{
metadataServiceInitialization.InitializeInternalAsync().SafeForget();
}
serviceProvider
.GetRequiredService<IMetadataService>()
.As<IMetadataServiceInitialization>()?
.InitializeInternalAsync()
.SafeForget();
if (serviceProvider.GetRequiredService<IHutaoUserService>() is IHutaoUserServiceInitialization hutaoUserServiceInitialization)
{
hutaoUserServiceInitialization.InitializeInternalAsync().SafeForget();
}
serviceProvider
.GetRequiredService<IHutaoUserService>()
.As<IHutaoUserServiceInitialization>()?
.InitializeInternalAsync()
.SafeForget();
serviceProvider
.GetRequiredService<IDiscordService>()

View File

@@ -49,7 +49,7 @@ internal sealed partial class PrivateNamedPipeServer : IDisposable
{
byte[] content = new byte[header->ContentLength];
serverStream.ReadAtLeast(content, header->ContentLength, false);
HutaoException.ThrowIf(XxHash64.HashToUInt64(content) != header->Checksum, HutaoExceptionKind.PrivateNamedPipeContentHashIncorrect, "PipePacket Content Hash incorrect");
ThrowHelper.InvalidDataIf(XxHash64.HashToUInt64(content) != header->Checksum, "PipePacket Content Hash incorrect");
return content;
}

View File

@@ -5,7 +5,6 @@ using CommunityToolkit.Mvvm.ComponentModel;
using Snap.Hutao.Core.LifeCycle;
using Snap.Hutao.Core.Setting;
using Snap.Hutao.Model;
using Snap.Hutao.Service.Notification;
using Snap.Hutao.Win32.Foundation;
using Snap.Hutao.Win32.UI.Input.KeyboardAndMouse;
using System.Text;
@@ -18,7 +17,6 @@ namespace Snap.Hutao.Core.Windowing.HotKey;
internal sealed class HotKeyCombination : ObservableObject
{
private readonly ICurrentWindowReference currentWindowReference;
private readonly IInfoBarService infoBarService;
private readonly RuntimeOptions runtimeOptions;
private readonly string settingKey;
@@ -39,7 +37,6 @@ internal sealed class HotKeyCombination : ObservableObject
public HotKeyCombination(IServiceProvider serviceProvider, string settingKey, int hotKeyId, HOT_KEY_MODIFIERS defaultModifiers, VirtualKey defaultKey)
{
currentWindowReference = serviceProvider.GetRequiredService<ICurrentWindowReference>();
infoBarService = serviceProvider.GetRequiredService<IInfoBarService>();
runtimeOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
this.settingKey = settingKey;
@@ -189,12 +186,6 @@ internal sealed class HotKeyCombination : ObservableObject
HWND hwnd = currentWindowReference.GetWindowHandle();
BOOL result = RegisterHotKey(hwnd, hotKeyId, Modifiers, (uint)Key);
registered = result;
if (!result)
{
infoBarService.Warning(SH.FormatCoreWindowHotkeyCombinationRegisterFailed(SH.ViewPageSettingKeyShortcutAutoClickingHeader, DisplayName));
}
return result;
}

View File

@@ -18,11 +18,20 @@ internal static class DateTimeOffsetExtension
return defaultValue;
}
return value switch
try
{
>= -62135596800 and <= 253402300799 => DateTimeOffset.FromUnixTimeSeconds(value),
>= -62135596800000 and <= 253402300799999 => DateTimeOffset.FromUnixTimeMilliseconds(value),
_ => defaultValue,
};
return DateTimeOffset.FromUnixTimeSeconds(value);
}
catch (ArgumentOutOfRangeException)
{
try
{
return DateTimeOffset.FromUnixTimeMilliseconds(value);
}
catch (ArgumentOutOfRangeException)
{
return defaultValue;
}
}
}
}

View File

@@ -46,6 +46,34 @@ internal static partial class EnumerableExtension
return first;
}
public static string JoinToString<T>(this IEnumerable<T> source, char separator, Action<StringBuilder, T> selector)
{
StringBuilder resultBuilder = new();
IEnumerator<T> enumerator = source.GetEnumerator();
if (!enumerator.MoveNext())
{
return string.Empty;
}
T first = enumerator.Current;
selector(resultBuilder, first);
if (!enumerator.MoveNext())
{
return resultBuilder.ToString();
}
do
{
resultBuilder.Append(separator);
selector(resultBuilder, enumerator.Current);
}
while (enumerator.MoveNext());
return resultBuilder.ToString();
}
public static string JoinToString<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> source, char separator, Action<StringBuilder, TKey, TValue> selector)
{
StringBuilder resultBuilder = new();

View File

@@ -41,30 +41,6 @@
"Equatable": true,
"EqualityOperators": true
},
{
"Name": "FurnitureId",
"Documentation": "家具 Id",
"Equatable": true,
"EqualityOperators": true
},
{
"Name": "FurnitureMakeId",
"Documentation": "家具配方 Id",
"Equatable": true,
"EqualityOperators": true
},
{
"Name": "FurnitureSuiteId",
"Documentation": "家具套装 Id",
"Equatable": true,
"EqualityOperators": true
},
{
"Name": "FurnitureTypeId",
"Documentation": "家具分类 Id",
"Equatable": true,
"EqualityOperators": true
},
{
"Name": "Level",
"Documentation": "等级 1 - 90",

View File

@@ -27,7 +27,7 @@ internal sealed partial class MainWindow : Window, IWindowOptionsSource, IMinMax
public MainWindow(IServiceProvider serviceProvider)
{
InitializeComponent();
windowOptions = new(this, TitleBarView.DragArea, new(1200, 741), true);
windowOptions = new(this, TitleBarView.DragArea, new(1200, 741), true, false);
this.InitializeController(serviceProvider);
}

View File

@@ -1,6 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Message;
internal sealed class BackgroundImageTypeChangedMessage;

View File

@@ -1,25 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Model.Intrinsic;
internal enum FurnitureDeploySurfaceType
{
Ground = 0,
Wall = 1,
Ceil = 2,
StackObjPlane = 3,
Door = 4,
Chandelier = 5,
Floor = 6,
WallBody = 7,
Carpet = 8,
LegoRockery = 9,
Stair = 10,
NPC = 11,
Animal = 12,
Apartment = 13,
FurnitureSuite = 14,
Road = 15,
Terrain = 16,
}

View File

@@ -1,13 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Model.Intrinsic;
internal enum FurnitureDeployType
{
Interior,
Exterior,
InteriorRoom,
InteriorHall,
Skybox,
}

View File

@@ -1,14 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Model.Intrinsic;
internal enum GroupRecordType
{
None,
Racing,
Balloon,
Stake,
Seek,
Explosion,
}

View File

@@ -1,26 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Model.Intrinsic;
internal enum SpecialFurnitureType
{
NormalFurniture,
BlockDependent,
FarmField,
TeleportPoint,
Fishpond,
Npc,
Apartment,
FurnitureSuite,
Paimon,
Fish,
CustomBaseFurniture,
CustomNodeFurniture,
VirtualFurniture,
GroupFurniture,
CoopPictureFrame,
ChangeBgmFurniture,
ServerGadget,
Fishtank,
}

View File

@@ -8,4 +8,15 @@ namespace Snap.Hutao.Model.Metadata.Achievement;
/// <summary>
/// 奖励
/// </summary>
internal sealed class Reward : IdCount;
internal sealed class Reward
{
/// <summary>
/// Id
/// </summary>
public MaterialId Id { get; set; }
/// <summary>
/// 数量
/// </summary>
public uint Count { get; set; }
}

View File

@@ -1,50 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Primitive;
namespace Snap.Hutao.Model.Metadata.Furniture;
internal sealed class Furniture
{
public List<FurnitureTypeId> Types { get; set; } = default!;
public FurnitureDeploySurfaceType SurfaceType { get; set; }
public bool IsSpecial { get; set; }
public SpecialFurnitureType SpecialType { get; set; }
public uint Comfort { get; set; }
public uint Cost { get; set; }
public uint DiscountCost { get; set; }
public bool CanFloat { get; set; }
public bool IsUnique { get; set; }
public string? ItemIcon { get; set; }
public string? EffectIcon { get; set; }
public QualityType RankLevel { get; set; }
public List<FurnitureId> GruopUnits { get; set; } = default!;
public GroupRecordType GroupRecordType { get; set; }
public List<string> SourceTexts { get; set; } = default!;
public FurnitureId Id { get; set; }
public string Name { get; set; } = default!;
public string Description { get; set; } = default!;
public string? Icon { get; set; }
public uint Rank { get; set; }
}

View File

@@ -1,18 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Primitive;
namespace Snap.Hutao.Model.Metadata.Furniture;
internal sealed class FurnitureMake
{
public FurnitureMakeId Id { get; set; }
public FurnitureId ItemId { get; set; }
public uint Experience { get; set; }
public List<IdCount> Materials { get; set; } = default!;
}

View File

@@ -1,26 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Primitive;
namespace Snap.Hutao.Model.Metadata.Furniture;
internal sealed class FurnitureSuite
{
public FurnitureSuiteId Id { get; set; }
public List<FurnitureTypeId> Types { get; set; } = default!;
public string Name { get; set; } = default!;
public string Description { get; set; } = default!;
public string ItemIcon { get; set; } = default!;
public string? MapIcon { get; set; }
public List<AvatarId>? FavoriteNpcs { get; set; }
public List<FurnitureId> Units { get; set; } = default!;
}

View File

@@ -1,28 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Primitive;
namespace Snap.Hutao.Model.Metadata.Furniture;
internal sealed class FurnitureType
{
public FurnitureTypeId Id { get; set; }
public uint Category { get; set; }
public string Name { get; set; } = default!;
public string Name2 { get; set; } = default!;
public string TabIcon { get; set; } = default!;
public FurnitureDeployType SceneType { get; set; }
public bool BagPageOnly { get; set; }
public bool IsShowInBag { get; set; }
public uint Sort { get; set; }
}

View File

@@ -1,20 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Primitive;
namespace Snap.Hutao.Model.Metadata;
internal class IdCount
{
/// <summary>
/// Id
/// </summary>
public MaterialId Id { get; set; }
/// <summary>
/// 数量
/// </summary>
public uint Count { get; set; }
}

View File

@@ -186,9 +186,6 @@
<data name="CoreWebView2HelperVersionUndetected" xml:space="preserve">
<value>未检测到 WebView2 运行时</value>
</data>
<data name="CoreWindowHotkeyCombinationRegisterFailed" xml:space="preserve">
<value>[{0}] 热键 [{1}] 注册失败</value>
</data>
<data name="FilePickerExportCommit" xml:space="preserve">
<value>导出</value>
</data>
@@ -2895,7 +2892,7 @@
<value>〓活动时间〓.*?\d\.\d版本期间持续开放</value>
</data>
<data name="WebAnnouncementMatchTransientActivityTime" xml:space="preserve">
<value>(?:〓活动时间〓|祈愿时间|【上架时间】|〓折扣时间〓).*?(\d\.\d版本更新后).*?~.*?&amp;lt;t class="t_(?:gl|lc)".*?&amp;gt;(.*?)&amp;lt;/t&amp;gt;</value>
<value>(?:〓活动时间〓|祈愿时间|【上架时间】).*?(\d\.\d版本更新后).*?~.*?&amp;lt;t class="t_(?:gl|lc)".*?&amp;gt;(.*?)&amp;lt;/t&amp;gt;</value>
</data>
<data name="WebAnnouncementMatchVersionUpdateTime" xml:space="preserve">
<value>〓更新时间〓.+?&amp;lt;t class=\"t_(?:gl|lc)\".*?&amp;gt;(.*?)&amp;lt;/t&amp;gt;</value>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@@ -85,13 +85,8 @@ internal sealed partial class AnnouncementService : IAnnouncementService
{
foreach (ref readonly Announcement item in CollectionsMarshal.AsSpan(listWrapper.List))
{
item.Subtitle = new StringBuilder(item.Subtitle)
.Replace("\r<br>", string.Empty)
.Replace("<br />", string.Empty)
.ToString();
item.Content = AnnouncementRegex
.XmlTimeTagRegex()
.Replace(item.Content, x => x.Groups[1].Value);
item.Subtitle = new StringBuilder(item.Subtitle).Replace("\r<br>", string.Empty).ToString();
item.Content = AnnouncementRegex.XmlTimeTagRegex.Replace(item.Content, x => x.Groups[1].Value);
}
}
}
@@ -138,7 +133,7 @@ internal sealed partial class AnnouncementService : IAnnouncementService
continue;
}
MatchCollection matches = AnnouncementRegex.XmlTimeTagRegex().Matches(announcement.Content);
MatchCollection matches = AnnouncementRegex.XmlTimeTagRegex.Matches(announcement.Content);
if (matches.Count < 2)
{
continue;

View File

@@ -28,13 +28,13 @@ internal sealed partial class BackgroundImageService : IBackgroundImageService
private HashSet<string> currentBackgroundPathSet;
public async ValueTask<ValueResult<bool, BackgroundImage?>> GetNextBackgroundImageAsync(BackgroundImage? previous)
public async ValueTask<ValueResult<bool, BackgroundImage>> GetNextBackgroundImageAsync(BackgroundImage? previous)
{
HashSet<string> backgroundSet = await SkipOrInitBackgroundAsync().ConfigureAwait(false);
if (backgroundSet.Count <= 0)
{
return new(true, default!);
return new(false, default!);
}
string path = System.Random.Shared.GetItems([..backgroundSet], 1)[0];
@@ -109,9 +109,6 @@ internal sealed partial class BackgroundImageService : IBackgroundImageService
case BackgroundImageType.HutaoOfficialLauncher:
await SetCurrentBackgroundPathSetAsync(client => client.GetLauncherWallpaperAsync()).ConfigureAwait(false);
break;
default:
currentBackgroundPathSet = [];
break;
}
currentBackgroundPathSet ??= [];

View File

@@ -5,5 +5,5 @@ namespace Snap.Hutao.Service.BackgroundImage;
internal interface IBackgroundImageService
{
ValueTask<ValueResult<bool, BackgroundImage?>> GetNextBackgroundImageAsync(BackgroundImage? previous);
ValueTask<ValueResult<bool, BackgroundImage>> GetNextBackgroundImageAsync(BackgroundImage? previous);
}

View File

@@ -1,12 +1,14 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core.DependencyInjection.Abstraction;
namespace Snap.Hutao.Service.Hutao;
/// <summary>
/// 胡桃用户服务
/// </summary>
internal interface IHutaoUserService
internal interface IHutaoUserService : ICastService
{
/// <summary>
/// 异步初始化

View File

@@ -10,7 +10,7 @@ namespace Snap.Hutao.Service.Metadata;
/// 元数据服务
/// </summary>
[HighQuality]
internal interface IMetadataService
internal interface IMetadataService : ICastService
{
IMemoryCache MemoryCache { get; }

View File

@@ -1,9 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Service.Navigation;
internal interface INavigationCurrent
{
Type? Current { get; }
}

View File

@@ -1,6 +1,8 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Xaml.Controls;
namespace Snap.Hutao.Service.Navigation;
/// <summary>
@@ -8,5 +10,10 @@ namespace Snap.Hutao.Service.Navigation;
/// </summary>
internal interface INavigationInitialization
{
void Initialize(INavigationViewAccessor accessor);
/// <summary>
/// 使用指定的对象进行初始化
/// </summary>
/// <param name="navigationView">管理的 <see cref="NavigationView"/></param>
/// <param name="frame">管理的 <see cref="Frame"/></param>
void Initialize(NavigationView navigationView, Frame frame);
}

View File

@@ -10,7 +10,7 @@ namespace Snap.Hutao.Service.Navigation;
/// 导航服务
/// </summary>
[HighQuality]
internal interface INavigationService : INavigationCurrent
internal interface INavigationService : ICastService, INavigationCurrent
{
/// <summary>
/// 导航到指定类型的页面
@@ -47,4 +47,9 @@ internal interface INavigationService : INavigationCurrent
/// 尽可能尝试返回
/// </summary>
void GoBack();
}
internal interface INavigationCurrent
{
Type? Current { get; }
}

View File

@@ -1,14 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Xaml.Controls;
using Snap.Hutao.Control;
namespace Snap.Hutao.Service.Navigation;
internal interface INavigationViewAccessor : IXamlElementAccessor
{
NavigationView NavigationView { get; }
Frame Frame { get; }
}

View File

@@ -147,10 +147,10 @@ internal sealed class NavigationService : INavigationService, INavigationInitial
}
/// <inheritdoc/>
public void Initialize(INavigationViewAccessor accessor)
public void Initialize(NavigationView navigationView, Frame frame)
{
NavigationView = accessor.NavigationView;
frame = accessor.Frame;
NavigationView = navigationView;
this.frame = frame;
NavigationView.IsPaneOpen = LocalSetting.Get(SettingKeys.IsNavPaneOpen, true);
}

View File

@@ -315,8 +315,8 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Validation" Version="17.8.8" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.3233" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240227000" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.4.240211001" />
<PackageReference Include="QRCoder" Version="1.4.3" />
<PackageReference Include="Snap.Discord.GameSDK" Version="1.6.0" />
<PackageReference Include="Snap.Hutao.Deployment.Runtime" Version="1.15.3">

View File

@@ -1,15 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Control;
using Snap.Hutao.Core.Windowing;
namespace Snap.Hutao.View.Converter.Specialized;
internal sealed class BackdropTypeToOpacityConverter : ValueConverter<BackdropType, double>
{
public override double Convert(BackdropType from)
{
return from is BackdropType.None ? 1 : 0;
}
}

View File

@@ -9,7 +9,6 @@
xmlns:shch="using:Snap.Hutao.Control.Helper"
xmlns:shcm="using:Snap.Hutao.Control.Markup"
xmlns:shv="using:Snap.Hutao.View"
xmlns:shvcs="using:Snap.Hutao.View.Converter.Specialized"
xmlns:shvh="using:Snap.Hutao.View.Helper"
xmlns:shvm="using:Snap.Hutao.ViewModel"
xmlns:shvp="using:Snap.Hutao.View.Page"
@@ -19,7 +18,7 @@
<mxi:Interaction.Behaviors>
<shcb:PeriodicInvokeCommandOrOnActualThemeChangedBehavior
Command="{Binding UpdateBackgroundCommand}"
CommandParameter="{x:Bind BackgroundImagePresenter}"
CommandParameter="{x:Bind BackdroundImagePresenter}"
Period="0:5:0"/>
</mxi:Interaction.Behaviors>
@@ -28,18 +27,10 @@
<Thickness x:Key="NavigationViewContentGridBorderThickness">0,1,0,0</Thickness>
<x:Double x:Key="NavigationViewItemOnLeftIconBoxHeight">24</x:Double>
<SolidColorBrush x:Key="NavigationViewExpandedPaneBackground" Color="Transparent"/>
<shvcs:BackdropTypeToOpacityConverter x:Key="BackdropTypeToOpacityConverter"/>
</UserControl.Resources>
<Grid Transitions="{ThemeResource EntranceThemeTransitions}">
<Border Background="{ThemeResource SolidBackgroundFillColorBaseBrush}" Opacity="{Binding AppOptions.BackdropType, Converter={StaticResource BackdropTypeToOpacityConverter}, Mode=OneWay}">
<Border.OpacityTransition>
<ScalarTransition Duration="0:0:1"/>
</Border.OpacityTransition>
</Border>
<Grid Background="{ThemeResource SolidBackgroundFillColorBaseBrush}" Transitions="{ThemeResource EntranceThemeTransitions}">
<Image
x:Name="BackgroundImagePresenter"
x:Name="BackdroundImagePresenter"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Opacity="0"

View File

@@ -1,7 +1,13 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.WinUI.Animations;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media.Animation;
using Snap.Hutao.Control.Animation;
using Snap.Hutao.Control.Theme;
using Snap.Hutao.Service.BackgroundImage;
using Snap.Hutao.Service.Navigation;
using Snap.Hutao.View.Page;
using Snap.Hutao.ViewModel;
@@ -21,44 +27,17 @@ internal sealed partial class MainView : UserControl
/// </summary>
public MainView()
{
IServiceProvider serviceProvider = Ioc.Default;
MainViewModel mainViewModel = serviceProvider.GetRequiredService<MainViewModel>();
DataContext = mainViewModel;
DataContext = Ioc.Default.GetRequiredService<MainViewModel>();
InitializeComponent();
mainViewModel.Initialize(new BackgroundImagePresenterAccessor(BackgroundImagePresenter));
IServiceProvider serviceProvider = Ioc.Default;
navigationService = serviceProvider.GetRequiredService<INavigationService>();
if (navigationService is INavigationInitialization navigationInitialization)
{
navigationInitialization.Initialize(new NavigationViewAccessor(NavView, ContentFrame));
navigationInitialization.Initialize(NavView, ContentFrame);
}
navigationService.Navigate<AnnouncementPage>(INavigationAwaiter.Default, true);
}
private class NavigationViewAccessor : INavigationViewAccessor
{
public NavigationViewAccessor(NavigationView navigationView, Frame frame)
{
NavigationView = navigationView;
Frame = frame;
}
public NavigationView NavigationView { get; private set; }
public Frame Frame { get; private set; }
}
private class BackgroundImagePresenterAccessor : IBackgroundImagePresenterAccessor
{
public BackgroundImagePresenterAccessor(Image backgroundImagePresenter)
{
BackgroundImagePresenter = backgroundImagePresenter;
}
public Image BackgroundImagePresenter { get; private set; }
}
}

View File

@@ -506,33 +506,39 @@
<DataTemplate x:Key="TeamItemTemplate" d:DataType="shvcom:Team">
<Border Margin="0,0,16,8" Style="{StaticResource BorderCardStyle}">
<Grid Margin="6">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="36"/>
</Grid.RowDefinitions>
<Viewbox Stretch="Uniform">
<ItemsControl HorizontalAlignment="Left" ItemsSource="{Binding}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Spacing="6"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<shvcon:ItemIcon
Width="40"
Height="40"
Icon="{Binding Icon}"
Quality="{Binding Quality}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="184"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ItemsControl
MinWidth="210"
HorizontalAlignment="Left"
ItemsSource="{Binding}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Spacing="6"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<shvcon:ItemIcon
Width="40"
Height="40"
Icon="{Binding Icon}"
Quality="{Binding Quality}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Viewbox
Grid.Column="1"
Height="40"
Stretch="Uniform">
<TextBlock
Margin="4,8"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding Rate}"/>
</Viewbox>
<TextBlock
Grid.Row="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding Rate}"/>
</Grid>
</Border>
</DataTemplate>

View File

@@ -29,8 +29,6 @@ internal sealed class Team : List<AvatarView>
Add(new(idAvatarMap[id]));
}
AddRange(new AvatarView[4 - Count]);
Rate = SH.FormatModelBindingHutaoTeamUpCountFormat(team.Rate);
}

View File

@@ -76,10 +76,9 @@ internal sealed class DownloadSummary : ObservableObject
{
HttpResponseMessage response = await httpClient.GetAsync(fileUrl, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
if (response.Content.Headers.ContentType?.MediaType is not ("application/octet-stream" or "application/zip"))
if (response.Content.Headers.ContentType?.MediaType is not "application/octet-stream")
{
logger.LogWarning("Download Static Zip failed, Content-Type is {Type}", response.Content.Headers.ContentType);
await taskContext.SwitchToMainThreadAsync();
Description = SH.ViewModelWelcomeDownloadSummaryContentTypeNotMatch;
return false;
}

View File

@@ -1,12 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Xaml.Controls;
using Snap.Hutao.Control;
namespace Snap.Hutao.ViewModel;
internal interface IBackgroundImagePresenterAccessor : IXamlElementAccessor
{
Image BackgroundImagePresenter { get; }
}

View File

@@ -1,9 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.ViewModel;
internal interface IMainViewModelInitialization
{
void Initialize(IBackgroundImagePresenterAccessor accessor);
}

View File

@@ -1,54 +1,30 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.WinUI.Animations;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media.Animation;
using Snap.Hutao.Control.Animation;
using Snap.Hutao.Control.Theme;
using Snap.Hutao.Message;
using Snap.Hutao.Service;
using Snap.Hutao.Service.BackgroundImage;
using System.Globalization;
namespace Snap.Hutao.ViewModel;
[ConstructorGenerated]
[Injection(InjectAs.Singleton)]
internal sealed partial class MainViewModel : Abstraction.ViewModel, IMainViewModelInitialization, IRecipient<BackgroundImageTypeChangedMessage>
internal sealed partial class MainViewModel : Abstraction.ViewModel
{
private readonly IBackgroundImageService backgroundImageService;
private readonly ILogger<MainViewModel> logger;
private readonly ITaskContext taskContext;
private readonly AppOptions appOptions;
private BackgroundImage? previousBackgroundImage;
private Image? backgroundImagePresenter;
public AppOptions AppOptions { get => appOptions; }
public void Initialize(IBackgroundImagePresenterAccessor accessor)
{
backgroundImagePresenter = accessor.BackgroundImagePresenter;
}
public void Receive(BackgroundImageTypeChangedMessage message)
{
UpdateBackgroundAsync().SafeForget();
}
[Command("UpdateBackgroundCommand")]
private async Task UpdateBackgroundAsync()
private async Task UpdateBackgroundAsync(Image presenter)
{
if (backgroundImagePresenter is null)
{
return;
}
(bool isOk, BackgroundImage backgroundImage) = await backgroundImageService.GetNextBackgroundImageAsync(previousBackgroundImage).ConfigureAwait(false);
(bool shouldRefresh, BackgroundImage? backgroundImage) = await backgroundImageService.GetNextBackgroundImageAsync(previousBackgroundImage).ConfigureAwait(false);
if (shouldRefresh)
if (isOk)
{
previousBackgroundImage = backgroundImage;
await taskContext.SwitchToMainThreadAsync();
@@ -60,21 +36,11 @@ internal sealed partial class MainViewModel : Abstraction.ViewModel, IMainViewMo
duration: ControlAnimationConstants.ImageOpacityFadeInOut,
easingType: EasingType.Quartic,
easingMode: EasingMode.EaseInOut)
.StartAsync(backgroundImagePresenter)
.StartAsync(presenter)
.ConfigureAwait(true);
backgroundImagePresenter.Source = backgroundImage?.ImageSource;
double targetOpacity = backgroundImage is not null
? ThemeHelper.IsDarkMode(backgroundImagePresenter.ActualTheme)
? 1 - backgroundImage.Luminance
: backgroundImage.Luminance
: 0;
logger.LogInformation(
"Background image: [Accent color: {AccentColor}] [Luminance: {Luminance}] [Opacity: {TargetOpacity}]",
backgroundImage?.AccentColor.ToString(CultureInfo.CurrentCulture),
backgroundImage?.Luminance,
targetOpacity);
presenter.Source = backgroundImage.ImageSource;
double targetOpacity = ThemeHelper.IsDarkMode(presenter.ActualTheme) ? 1 - backgroundImage.Luminance : backgroundImage.Luminance;
await AnimationBuilder
.Create()
@@ -83,7 +49,7 @@ internal sealed partial class MainViewModel : Abstraction.ViewModel, IMainViewMo
duration: ControlAnimationConstants.ImageOpacityFadeInOut,
easingType: EasingType.Quartic,
easingMode: EasingMode.EaseInOut)
.StartAsync(backgroundImagePresenter)
.StartAsync(presenter)
.ConfigureAwait(true);
}
}

View File

@@ -1,7 +1,6 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.UI.Xaml.Controls;
using Microsoft.Windows.AppLifecycle;
using Snap.Hutao.Core;
@@ -55,7 +54,6 @@ internal sealed partial class SettingViewModel : Abstraction.ViewModel
private readonly IUserService userService;
private readonly ITaskContext taskContext;
private readonly AppOptions appOptions;
private readonly IMessenger messenger;
private NameValue<BackdropType>? selectedBackdropType;
private NameValue<BackgroundImageType>? selectedBackgroundImageType;
@@ -102,7 +100,6 @@ internal sealed partial class SettingViewModel : Abstraction.ViewModel
if (SetProperty(ref selectedBackgroundImageType, value) && value is not null)
{
AppOptions.BackgroundImageType = value.Value;
messenger.Send(new Message.BackgroundImageTypeChangedMessage());
}
}
}

View File

@@ -22,6 +22,9 @@ internal static partial class AnnouncementRegex
/// <inheritdoc cref="SH.WebAnnouncementMatchPermanentActivityTime"/>
public static readonly Regex PermanentActivityAfterUpdateTimeRegex = new(SH.WebAnnouncementMatchPermanentActivityTime, RegexOptions.Compiled);
/// <inheritdoc cref="XmlTagRegex"/>
public static readonly Regex XmlTimeTagRegex = XmlTagRegex();
[GeneratedRegex("&lt;t class=\"t_(?:gl|lc)\".*?&gt;(.*?)&lt;/t&gt;", RegexOptions.Multiline)]
public static partial Regex XmlTimeTagRegex();
private static partial Regex XmlTagRegex();
}