Compare commits

..

23 Commits

Author SHA1 Message Date
qhy040404
df7f685a3c prompt user dailynote auto-refresh is not effective while notify icon is disabled 2024-05-23 10:55:43 +08:00
qhy040404
9d47082f47 fix #1631 2024-05-22 22:59:53 +08:00
DismissedLight
cd4516d9a7 Merge pull request #1630 from DGP-Studio/feat/1595 2024-05-22 17:02:13 +08:00
Lightczx
f7e94fe2f2 refine view 2024-05-22 16:46:06 +08:00
qhy040404
e93802d5a5 partial impl #1595 2024-05-22 15:37:04 +08:00
Lightczx
370f2fe1f7 impl #1542 2024-05-22 12:44:20 +08:00
qhy040404
c6f747a89b fix wrap layout 2024-05-21 23:53:23 +08:00
DismissedLight
cf431719df temp fix wrap layout 2024-05-21 23:14:51 +08:00
DismissedLight
c36c15f9be Merge pull request #1628 from DGP-Studio/fix/1609
fix #1609
2024-05-21 22:07:27 +08:00
DismissedLight
f80a63f557 Merge pull request #1627 from DGP-Studio/fix/1608
fix #1608
2024-05-21 22:03:48 +08:00
DismissedLight
36376f5af6 Update SH.resx 2024-05-21 22:03:10 +08:00
DismissedLight
2c057458a3 Merge pull request #1626 from DGP-Studio/fix/1613
fix #1613
2024-05-21 21:59:38 +08:00
DismissedLight
f0f50e0e30 Merge pull request #1625 from DGP-Studio/fix/1588
fix #1588
2024-05-21 21:48:25 +08:00
DismissedLight
b834daef93 Merge pull request #1619 from DGP-Studio/fix/new_dailynote_archon 2024-05-21 21:34:04 +08:00
DismissedLight
ff10543c21 Merge pull request #1615 from DGP-Studio/build/alpha 2024-05-21 21:12:58 +08:00
qhy040404
a55b25ae53 add alpha build constant 2024-05-21 17:34:45 +08:00
qhy040404
c0fbb823d4 fix empty archon quest in new added dailynote 2024-05-21 17:34:29 +08:00
qhy040404
4306af94be fix #1588 2024-05-21 17:34:03 +08:00
qhy040404
dfc83d4a34 fix #1613 2024-05-21 17:33:44 +08:00
qhy040404
c6a47eb7be fix #1608 2024-05-21 17:33:20 +08:00
qhy040404
7413a81ff4 fix #1609 2024-05-21 17:32:59 +08:00
Lightczx
24d143ea9f add France 2024-05-21 17:15:58 +08:00
DismissedLight
9f6611cd20 Merge pull request #1621 from DGP-Studio/feat/ShellNotifyIcon 2024-05-21 17:07:46 +08:00
24 changed files with 539 additions and 69 deletions

View File

@@ -11,6 +11,15 @@ var version = "version";
var repoDir = "repoDir"; var repoDir = "repoDir";
var outputPath = "outputPath"; var outputPath = "outputPath";
// Extension
static ProcessArgumentBuilder AppendIf(this ProcessArgumentBuilder builder, string text, bool condition)
{
return condition ? builder.Append(text) : builder;
}
// Properties
string solution string solution
{ {
get => System.IO.Path.Combine(repoDir, "src", "Snap.Hutao", "Snap.Hutao.sln"); get => System.IO.Path.Combine(repoDir, "src", "Snap.Hutao", "Snap.Hutao.sln");
@@ -157,6 +166,7 @@ Task("Build binary package")
.Append("/p:AppxPackageSigningEnabled=false") .Append("/p:AppxPackageSigningEnabled=false")
.Append("/p:AppxBundle=Never") .Append("/p:AppxBundle=Never")
.Append("/p:AppxPackageOutput=" + outputPath) .Append("/p:AppxPackageOutput=" + outputPath)
.AppendIf("/p:AlphaConstants=IS_ALPHA_BUILD", !AppVeyor.IsRunningOnAppVeyor)
}; };
DotNetBuild(project, settings); DotNetBuild(project, settings);

View File

@@ -85,7 +85,7 @@ public sealed partial class App : Application
} }
catch (Exception ex) catch (Exception ex)
{ {
System.Diagnostics.Debug.WriteLine(ex); Debug.WriteLine(ex);
Process.GetCurrentProcess().Kill(); Process.GetCurrentProcess().Kill();
} }
} }

View File

@@ -63,12 +63,12 @@ internal sealed partial class UniformStaggeredLayout : VirtualizingLayout
/// <inheritdoc/> /// <inheritdoc/>
protected override Size MeasureOverride(VirtualizingLayoutContext context, Size availableSize) protected override Size MeasureOverride(VirtualizingLayoutContext context, Size availableSize)
{ {
if (context.ItemCount == 0) if (context.ItemCount is 0)
{ {
return new Size(availableSize.Width, 0); return new Size(availableSize.Width, 0);
} }
if ((context.RealizationRect.Width == 0) && (context.RealizationRect.Height == 0)) if ((context.RealizationRect.Width is 0) && (context.RealizationRect.Height is 0))
{ {
return new Size(availableSize.Width, 0.0f); return new Size(availableSize.Width, 0.0f);
} }

View File

@@ -0,0 +1,25 @@
// Licensed to the .NET Fou// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Xaml;
using Windows.Foundation;
namespace Snap.Hutao.Control.Layout;
internal sealed class WrapItem
{
public static Point EmptyPosition { get; } = new(float.NegativeInfinity, float.NegativeInfinity);
public WrapItem(int index)
{
Index = index;
}
public int Index { get; }
public Size Size { get; set; } = Size.Empty;
public Point Position { get; set; } = EmptyPosition;
public UIElement? Element { get; set; }
}

View File

@@ -0,0 +1,220 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System.Collections.Specialized;
using Windows.Foundation;
namespace Snap.Hutao.Control.Layout;
[DependencyProperty("HorizontalSpacing", typeof(double), 0D, nameof(LayoutPropertyChanged))]
[DependencyProperty("VerticalSpacing", typeof(double), 0D, nameof(LayoutPropertyChanged))]
internal sealed partial class WrapLayout : VirtualizingLayout
{
protected override void InitializeForContextCore(VirtualizingLayoutContext context)
{
context.LayoutState = new WrapLayoutState(context);
}
protected override void UninitializeForContextCore(VirtualizingLayoutContext context)
{
context.LayoutState = default;
}
protected override void OnItemsChangedCore(VirtualizingLayoutContext context, object source, NotifyCollectionChangedEventArgs args)
{
WrapLayoutState state = (WrapLayoutState)context.LayoutState;
switch (args.Action)
{
case NotifyCollectionChangedAction.Add:
state.RemoveFromIndex(args.NewStartingIndex);
break;
case NotifyCollectionChangedAction.Move:
int minIndex = Math.Min(args.NewStartingIndex, args.OldStartingIndex);
state.RemoveFromIndex(minIndex);
state.RecycleElementAt(args.OldStartingIndex);
state.RecycleElementAt(args.NewStartingIndex);
break;
case NotifyCollectionChangedAction.Remove:
state.RemoveFromIndex(args.OldStartingIndex);
break;
case NotifyCollectionChangedAction.Replace:
state.RemoveFromIndex(args.NewStartingIndex);
state.RecycleElementAt(args.NewStartingIndex);
break;
case NotifyCollectionChangedAction.Reset:
state.Clear();
break;
}
base.OnItemsChangedCore(context, source, args);
}
protected override Size MeasureOverride(VirtualizingLayoutContext context, Size availableSize)
{
if (context.ItemCount is 0)
{
return new Size(availableSize.Width, 0);
}
if ((context.RealizationRect.Width is 0) && (context.RealizationRect.Height is 0))
{
return new Size(availableSize.Width, 0.0f);
}
Size spacing = new(HorizontalSpacing, VerticalSpacing);
WrapLayoutState state = (WrapLayoutState)context.LayoutState;
if (spacing != state.Spacing || state.AvailableWidth != availableSize.Width)
{
state.ClearPositions();
state.Spacing = spacing;
state.AvailableWidth = availableSize.Width;
}
double currentHeight = 0;
Point itemPosition = default;
for (int i = 0; i < context.ItemCount; ++i)
{
bool itemMeasured = false;
WrapItem item = state.GetItemAt(i);
if (item.Size == Size.Empty)
{
item.Element = context.GetOrCreateElementAt(i);
item.Element.Measure(availableSize);
item.Size = item.Element.DesiredSize;
itemMeasured = true;
}
Size itemSize = item.Size;
if (item.Position == WrapItem.EmptyPosition)
{
if (availableSize.Width < itemPosition.X + itemSize.Width)
{
// New Row
itemPosition.X = 0;
itemPosition.Y += currentHeight + spacing.Height;
currentHeight = 0;
}
item.Position = itemPosition;
}
itemPosition = item.Position;
double bottom = itemPosition.Y + itemSize.Height;
if (bottom < context.RealizationRect.Top)
{
// Item is "above" the bounds
if (item.Element is not null)
{
context.RecycleElement(item.Element);
item.Element = default;
}
continue;
}
else if (itemPosition.Y > context.RealizationRect.Bottom)
{
// Item is "below" the bounds.
if (item.Element is not null)
{
context.RecycleElement(item.Element);
item.Element = default;
}
// We don't need to measure anything below the bounds
break;
}
else if (!itemMeasured)
{
// Always measure elements that are within the bounds
item.Element = context.GetOrCreateElementAt(i);
item.Element.Measure(availableSize);
itemSize = item.Element.DesiredSize;
if (itemSize != item.Size)
{
// this item changed size; we need to recalculate layout for everything after this
state.RemoveFromIndex(i + 1);
item.Size = itemSize;
// did the change make it go into the new row?
if (availableSize.Width < itemPosition.X + itemSize.Width)
{
// New Row
itemPosition.X = 0;
itemPosition.Y += currentHeight + spacing.Height;
currentHeight = 0;
}
item.Position = itemPosition;
}
}
itemPosition.X += itemSize.Width + spacing.Width;
currentHeight = Math.Max(itemSize.Height, currentHeight);
}
return new Size(double.IsInfinity(availableSize.Width) ? 0 : Math.Ceiling(availableSize.Width), state.GetHeight());
}
protected override Size ArrangeOverride(VirtualizingLayoutContext context, Size finalSize)
{
if (context.ItemCount > 0)
{
WrapLayoutState state = (WrapLayoutState)context.LayoutState;
for (int i = 0; i < context.ItemCount; ++i)
{
if (!ArrangeItem(context, state.GetItemAt(i)))
{
break;
}
}
}
return finalSize;
static bool ArrangeItem(VirtualizingLayoutContext context, WrapItem item)
{
if (item.Size == Size.Empty || item.Position == WrapItem.EmptyPosition)
{
return false;
}
Size size = item.Size;
Point position = item.Position;
if (context.RealizationRect.Top <= position.Y + size.Height && position.Y <= context.RealizationRect.Bottom)
{
// place the item
UIElement child = context.GetOrCreateElementAt(item.Index);
child.Arrange(new Rect(position, size));
}
else if (position.Y > context.RealizationRect.Bottom)
{
return false;
}
return true;
}
}
private static void LayoutPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is WrapLayout layout)
{
layout.InvalidateMeasure();
layout.InvalidateArrange();
}
}
}

View File

@@ -0,0 +1,112 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Xaml.Controls;
using System.Runtime.InteropServices;
using Windows.Foundation;
namespace Snap.Hutao.Control.Layout;
internal sealed class WrapLayoutState
{
private readonly List<WrapItem> items = [];
private readonly VirtualizingLayoutContext context;
public WrapLayoutState(VirtualizingLayoutContext context)
{
this.context = context;
}
public Orientation Orientation { get; private set; }
public Size Spacing { get; set; }
public double AvailableWidth { get; set; }
public WrapItem GetItemAt(int index)
{
if (index < 0)
{
throw new IndexOutOfRangeException();
}
if (index <= (items.Count - 1))
{
return items[index];
}
else
{
WrapItem item = new(index);
items.Add(item);
return item;
}
}
public void Clear()
{
for (int i = 0; i < items.Count; i++)
{
RecycleElementAt(i);
}
items.Clear();
}
public void RemoveFromIndex(int index)
{
if (index >= items.Count)
{
// Item was added/removed but we haven't realized that far yet
return;
}
int numToRemove = items.Count - index;
items.RemoveRange(index, numToRemove);
}
public void ClearPositions()
{
foreach (ref readonly WrapItem item in CollectionsMarshal.AsSpan(items))
{
item.Position = WrapItem.EmptyPosition;
}
}
public double GetHeight()
{
if (items.Count is 0)
{
return 0;
}
Point? lastPosition = default;
double maxHeight = 0;
Span<WrapItem> itemSpan = CollectionsMarshal.AsSpan(items);
for (int i = items.Count - 1; i >= 0; --i)
{
ref readonly WrapItem item = ref itemSpan[i];
if (item.Position == WrapItem.EmptyPosition || item.Size == Size.Empty)
{
continue;
}
if (lastPosition is not null && lastPosition.Value.Y > item.Position.Y)
{
// This is a row above the last item.
break;
}
lastPosition = item.Position;
maxHeight = Math.Max(maxHeight, item.Size.Height);
}
return lastPosition?.Y + maxHeight ?? 0;
}
public void RecycleElementAt(int index)
{
context.RecycleElement(context.GetOrCreateElementAt(index));
}
}

View File

@@ -44,6 +44,7 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi
private readonly IServiceProvider serviceProvider; private readonly IServiceProvider serviceProvider;
private readonly ICurrentXamlWindowReference currentWindowReference; private readonly ICurrentXamlWindowReference currentWindowReference;
private readonly ITaskContext taskContext; private readonly ITaskContext taskContext;
private readonly SemaphoreSlim activateSemaphore = new(1); private readonly SemaphoreSlim activateSemaphore = new(1);
/// <inheritdoc/> /// <inheritdoc/>
@@ -65,16 +66,24 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi
serviceProvider.GetRequiredService<PrivateNamedPipeServer>().RunAsync().SafeForget(); serviceProvider.GetRequiredService<PrivateNamedPipeServer>().RunAsync().SafeForget();
ToastNotificationManagerCompat.OnActivated += NotificationActivate; ToastNotificationManagerCompat.OnActivated += NotificationActivate;
serviceProvider.GetRequiredService<HotKeyOptions>().RegisterAll(); using (activateSemaphore.Enter())
if (serviceProvider.GetRequiredService<AppOptions>().IsNotifyIconEnabled)
{ {
XamlWindowLifetime.ApplicationLaunchedWithNotifyIcon = true; serviceProvider.GetRequiredService<HotKeyOptions>().RegisterAll();
serviceProvider.GetRequiredService<App>().DispatcherShutdownMode = DispatcherShutdownMode.OnExplicitShutdown; if (UnsafeLocalSetting.Get(SettingKeys.Major1Minor10Revision0GuideState, GuideState.Language) < GuideState.Completed)
_ = serviceProvider.GetRequiredService<NotifyIconController>(); {
} return;
}
serviceProvider.GetRequiredService<IScheduleTaskInterop>().UnregisterAllTasks(); if (serviceProvider.GetRequiredService<AppOptions>().IsNotifyIconEnabled)
serviceProvider.GetRequiredService<IQuartzService>().StartAsync(default).SafeForget(); {
XamlWindowLifetime.ApplicationLaunchedWithNotifyIcon = true;
serviceProvider.GetRequiredService<App>().DispatcherShutdownMode = DispatcherShutdownMode.OnExplicitShutdown;
_ = serviceProvider.GetRequiredService<NotifyIconController>();
}
serviceProvider.GetRequiredService<IScheduleTaskInterop>().UnregisterAllTasks();
serviceProvider.GetRequiredService<IQuartzService>().StartAsync(default).SafeForget();
}
} }
public void Dispose() public void Dispose()
@@ -172,7 +181,6 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi
} }
} }
// If it's the first time launch, show the guide window anyway.
if (UnsafeLocalSetting.Get(SettingKeys.Major1Minor10Revision0GuideState, GuideState.Language) < GuideState.Completed) if (UnsafeLocalSetting.Get(SettingKeys.Major1Minor10Revision0GuideState, GuideState.Language) < GuideState.Completed)
{ {
await taskContext.SwitchToMainThreadAsync(); await taskContext.SwitchToMainThreadAsync();

View File

@@ -15,7 +15,7 @@ internal static class SemaphoreSlimExtension
} }
catch (ObjectDisposedException ex) catch (ObjectDisposedException ex)
{ {
ThrowHelper.OperationCanceled(SH.CoreThreadingSemaphoreSlimDisposed, ex); HutaoException.OperationCanceled(SH.CoreThreadingSemaphoreSlimDisposed, ex);
} }
return new SemaphoreSlimToken(semaphoreSlim); return new SemaphoreSlimToken(semaphoreSlim);
@@ -29,7 +29,7 @@ internal static class SemaphoreSlimExtension
} }
catch (ObjectDisposedException ex) catch (ObjectDisposedException ex)
{ {
ThrowHelper.OperationCanceled(SH.CoreThreadingSemaphoreSlimDisposed, ex); HutaoException.OperationCanceled(SH.CoreThreadingSemaphoreSlimDisposed, ex);
} }
return new SemaphoreSlimToken(semaphoreSlim); return new SemaphoreSlimToken(semaphoreSlim);

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@@ -1568,6 +1568,12 @@
<data name="ViewModelCultivationProjectInvalidName" xml:space="preserve"> <data name="ViewModelCultivationProjectInvalidName" xml:space="preserve">
<value>不能添加名称无效的计划</value> <value>不能添加名称无效的计划</value>
</data> </data>
<data name="ViewModelCultivationRemoveProjectContent" xml:space="preserve">
<value>此操作不可逆,此计划的养成物品与背包材料将会丢失</value>
</data>
<data name="ViewModelCultivationRemoveProjectTitle" xml:space="preserve">
<value>确认要删除当前计划吗?</value>
</data>
<data name="ViewModelDailyNoteConfigWebhookUrlComplete" xml:space="preserve"> <data name="ViewModelDailyNoteConfigWebhookUrlComplete" xml:space="preserve">
<value>实时便笺 Webhook Url 配置成功</value> <value>实时便笺 Webhook Url 配置成功</value>
</data> </data>
@@ -1988,6 +1994,9 @@
<data name="ViewPageDailyNoteSettingRefreshHeader" xml:space="preserve"> <data name="ViewPageDailyNoteSettingRefreshHeader" xml:space="preserve">
<value>刷新</value> <value>刷新</value>
</data> </data>
<data name="ViewPageDailyNoteSettingRefreshNotifyIconDisabledHint" xml:space="preserve">
<value>未启用通知区域图标,自动刷新将不会执行</value>
</data>
<data name="ViewPageDailyNoteSlientModeDescription" xml:space="preserve"> <data name="ViewPageDailyNoteSlientModeDescription" xml:space="preserve">
<value>在我游玩原神时不通知我</value> <value>在我游玩原神时不通知我</value>
</data> </data>
@@ -3024,7 +3033,7 @@
<value>武器资料</value> <value>武器资料</value>
</data> </data>
<data name="WebAnnouncementMatchPermanentActivityTime" xml:space="preserve"> <data name="WebAnnouncementMatchPermanentActivityTime" xml:space="preserve">
<value>(?:〓活动时间〓|〓任务开放时间〓).*?(\d\.\d)版本更新(?:完成|)后永久开放</value> <value>(?:(?:〓活动时间〓|〓任务开放时间〓).*?(\d\.\d)版本更新(?:完成|)|&amp;lt;t class=\"t_(?:gl|lc)\".*?&amp;gt;(.*?)&amp;lt;/t&amp;gt;)后永久开放</value>
</data> </data>
<data name="WebAnnouncementMatchPersistentActivityTime" xml:space="preserve"> <data name="WebAnnouncementMatchPersistentActivityTime" xml:space="preserve">
<value>〓活动时间〓.*?(\d\.\d)版本期间持续开放</value> <value>〓活动时间〓.*?(\d\.\d)版本期间持续开放</value>
@@ -3245,6 +3254,9 @@
<data name="WebResponseRequestExceptionFormat" xml:space="preserve"> <data name="WebResponseRequestExceptionFormat" xml:space="preserve">
<value>[{0}] 中的 [{1}] 网络请求异常,请稍后再试</value> <value>[{0}] 中的 [{1}] 网络请求异常,请稍后再试</value>
</data> </data>
<data name="WebResponseSignInErrorHint" xml:space="preserve">
<value>登录失败,请前往 HoYoLAB 初始化账号,原始消息:{0}</value>
</data>
<data name="WindowIdentifyMonitorHeader" xml:space="preserve"> <data name="WindowIdentifyMonitorHeader" xml:space="preserve">
<value>显示器编号</value> <value>显示器编号</value>
</data> </data>

View File

@@ -152,6 +152,8 @@ internal sealed partial class AnnouncementService : IAnnouncementService
announcement.StartTime = versionStartTime; announcement.StartTime = versionStartTime;
continue; continue;
} }
announcement.StartTime = UnsafeDateTimeOffset.ParseDateTime(permanent.Groups[2].ValueSpan, offset);
} }
if (AnnouncementRegex.PersistentActivityAfterUpdateTimeRegex.Match(announcement.Content) is { Success: true } persistent) if (AnnouncementRegex.PersistentActivityAfterUpdateTimeRegex.Match(announcement.Content) is { Success: true } persistent)

View File

@@ -54,6 +54,7 @@ internal sealed partial class DailyNoteService : IDailyNoteService, IRecipient<U
DailyNoteEntry newEntry = DailyNoteEntry.From(userAndUid); DailyNoteEntry newEntry = DailyNoteEntry.From(userAndUid);
Web.Response.Response<WebDailyNote> dailyNoteResponse; Web.Response.Response<WebDailyNote> dailyNoteResponse;
DailyNoteMetadataContext context;
using (IServiceScope scope = serviceProvider.CreateScope()) using (IServiceScope scope = serviceProvider.CreateScope())
{ {
IGameRecordClient gameRecordClient = scope.ServiceProvider IGameRecordClient gameRecordClient = scope.ServiceProvider
@@ -63,6 +64,8 @@ internal sealed partial class DailyNoteService : IDailyNoteService, IRecipient<U
dailyNoteResponse = await gameRecordClient dailyNoteResponse = await gameRecordClient
.GetDailyNoteAsync(userAndUid, token) .GetDailyNoteAsync(userAndUid, token)
.ConfigureAwait(false); .ConfigureAwait(false);
context = await scope.GetRequiredService<IMetadataService>().GetContextAsync<DailyNoteMetadataContext>(token).ConfigureAwait(false);
} }
if (dailyNoteResponse.IsOk()) if (dailyNoteResponse.IsOk())
@@ -71,6 +74,7 @@ internal sealed partial class DailyNoteService : IDailyNoteService, IRecipient<U
} }
newEntry.UserGameRole = userService.GetUserGameRoleByUid(roleUid); newEntry.UserGameRole = userService.GetUserGameRoleByUid(roleUid);
newEntry.ArchonQuestView = DailyNoteArchonQuestView.Create(newEntry.DailyNote, context.Chapters);
await dailyNoteDbService.AddDailyNoteEntryAsync(newEntry, token).ConfigureAwait(false); await dailyNoteDbService.AddDailyNoteEntryAsync(newEntry, token).ConfigureAwait(false);
newEntry.User = userAndUid.User; newEntry.User = userAndUid.User;

View File

@@ -15,7 +15,7 @@ internal static class SupportedCultures
/*ToNameValue(CultureInfo.GetCultureInfo("de")),*/ /*ToNameValue(CultureInfo.GetCultureInfo("de")),*/
ToNameValue(CultureInfo.GetCultureInfo("en")), ToNameValue(CultureInfo.GetCultureInfo("en")),
/*ToNameValue(CultureInfo.GetCultureInfo("es")),*/ /*ToNameValue(CultureInfo.GetCultureInfo("es")),*/
/*ToNameValue(CultureInfo.GetCultureInfo("fr")),*/ ToNameValue(CultureInfo.GetCultureInfo("fr")),
ToNameValue(CultureInfo.GetCultureInfo("id")), ToNameValue(CultureInfo.GetCultureInfo("id")),
/*ToNameValue(CultureInfo.GetCultureInfo("it")),*/ /*ToNameValue(CultureInfo.GetCultureInfo("it")),*/
ToNameValue(CultureInfo.GetCultureInfo("ja")), ToNameValue(CultureInfo.GetCultureInfo("ja")),

View File

@@ -25,7 +25,7 @@
<AppxBundle>Never</AppxBundle> <AppxBundle>Never</AppxBundle>
<HoursBetweenUpdateChecks>0</HoursBetweenUpdateChecks> <HoursBetweenUpdateChecks>0</HoursBetweenUpdateChecks>
<StartupObject>Snap.Hutao.Program</StartupObject> <StartupObject>Snap.Hutao.Program</StartupObject>
<DefineConstants>$(DefineConstants);DISABLE_XAML_GENERATED_MAIN;DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION;DISABLE_XAML_GENERATED_BINDING_DEBUG_OUTPUT</DefineConstants> <DefineConstants>$(DefineConstants);DISABLE_XAML_GENERATED_MAIN;DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION;DISABLE_XAML_GENERATED_BINDING_DEBUG_OUTPUT;$(AlphaConstants)</DefineConstants>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks> <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
<DebugType>embedded</DebugType> <DebugType>embedded</DebugType>
@@ -117,6 +117,7 @@
<None Remove="Resource\BlurBackground.png" /> <None Remove="Resource\BlurBackground.png" />
<None Remove="Resource\Font\CascadiaMono.ttf" /> <None Remove="Resource\Font\CascadiaMono.ttf" />
<None Remove="Resource\Font\MiSans-Regular.ttf" /> <None Remove="Resource\Font\MiSans-Regular.ttf" />
<None Remove="Resource\GuideStaticResourceQualityComparison.png" />
<None Remove="Resource\HutaoIconSourceTransparentBackgroundGradient1.png" /> <None Remove="Resource\HutaoIconSourceTransparentBackgroundGradient1.png" />
<None Remove="Resource\Icon\UI_AchievementIcon_3_3.png" /> <None Remove="Resource\Icon\UI_AchievementIcon_3_3.png" />
<None Remove="Resource\Icon\UI_GachaShowPanel_Bg_Weapon.png" /> <None Remove="Resource\Icon\UI_GachaShowPanel_Bg_Weapon.png" />
@@ -216,6 +217,7 @@
<AdditionalFiles Include="stylecop.json" /> <AdditionalFiles Include="stylecop.json" />
<AdditionalFiles Include="Resource\Localization\SH.resx" /> <AdditionalFiles Include="Resource\Localization\SH.resx" />
<AdditionalFiles Include="Resource\Localization\SH.en.resx" /> <AdditionalFiles Include="Resource\Localization\SH.en.resx" />
<AdditionalFiles Include="Resource\Localization\SH.fr.resx" />
<AdditionalFiles Include="Resource\Localization\SH.id.resx" /> <AdditionalFiles Include="Resource\Localization\SH.id.resx" />
<AdditionalFiles Include="Resource\Localization\SH.ja.resx" /> <AdditionalFiles Include="Resource\Localization\SH.ja.resx" />
<AdditionalFiles Include="Resource\Localization\SH.ko.resx" /> <AdditionalFiles Include="Resource\Localization\SH.ko.resx" />
@@ -265,6 +267,7 @@
<Content Include="Resource\BlurBackground.png" /> <Content Include="Resource\BlurBackground.png" />
<Content Include="Resource\Font\CascadiaMono.ttf" /> <Content Include="Resource\Font\CascadiaMono.ttf" />
<Content Include="Resource\Font\MiSans-Regular.ttf" /> <Content Include="Resource\Font\MiSans-Regular.ttf" />
<Content Include="Resource\GuideStaticResourceQualityComparison.png" />
<Content Include="Resource\HutaoIconSourceTransparentBackgroundGradient1.png" /> <Content Include="Resource\HutaoIconSourceTransparentBackgroundGradient1.png" />
<Content Include="Resource\Icon\UI_AchievementIcon_3_3.png" /> <Content Include="Resource\Icon\UI_AchievementIcon_3_3.png" />
<Content Include="Resource\Icon\UI_GachaShowPanel_Bg_Weapon.png" /> <Content Include="Resource\Icon\UI_GachaShowPanel_Bg_Weapon.png" />

View File

@@ -2,6 +2,7 @@
x:Class="Snap.Hutao.View.Guide.GuideView" x:Class="Snap.Hutao.View.Guide.GuideView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cw="using:CommunityToolkit.WinUI"
xmlns:cwc="using:CommunityToolkit.WinUI.Controls" xmlns:cwc="using:CommunityToolkit.WinUI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
@@ -210,28 +211,75 @@
<RowDefinition Height="auto"/> <RowDefinition Height="auto"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<StackPanel <Grid
Grid.Row="0" Grid.Row="0"
Margin="16" Margin="72"
HorizontalAlignment="Center" HorizontalAlignment="Center"
VerticalAlignment="Center"> ColumnSpacing="32">
<TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="{shcm:ResourceString Name=ViewGuideStepStaticResourceSettingQualityHeader}"/> <Grid.ColumnDefinitions>
<ListView <ColumnDefinition/>
MinWidth="320" <ColumnDefinition/>
Margin="0,8,0,32" </Grid.ColumnDefinitions>
DisplayMemberPath="Name" <cwc:ConstrainedBox Grid.Column="0" AspectRatio="1:1">
ItemsSource="{Binding StaticResourceOptions.ImageQualities}" <Border cw:Effects.Shadow="{ThemeResource CompatCardShadow}">
SelectedItem="{Binding StaticResourceOptions.ImageQuality, Mode=TwoWay}"/> <Grid
<TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="{shcm:ResourceString Name=ViewGuideStepStaticResourceSettingMinimumHeader}"/> BorderBrush="{x:Null}"
<ListView BorderThickness="0"
MinWidth="320" Style="{ThemeResource GridCardStyle}">
Margin="0,8,0,32" <Grid.RowDefinitions>
DisplayMemberPath="Name" <RowDefinition/>
ItemsSource="{Binding StaticResourceOptions.ImageArchives}" <RowDefinition Height="auto"/>
SelectedItem="{Binding StaticResourceOptions.ImageArchive, Mode=TwoWay}"/> </Grid.RowDefinitions>
<Grid Grid.Row="0">
<TextBlock Margin="0,16,0,0" Text="{Binding StaticResourceOptions.SizeInformationText, Mode=OneWay}"/> <Image
</StackPanel> HorizontalAlignment="Center"
VerticalAlignment="Center"
Source="ms-appx:///Resource/GuideStaticResourceQualityComparison.png"/>
<Rectangle
Width="2"
HorizontalAlignment="Center"
VerticalAlignment="Stretch"
Fill="White"/>
</Grid>
<Grid
Grid.Row="1"
Padding="16"
HorizontalAlignment="Stretch"
VerticalAlignment="Bottom"
Background="{ThemeResource ContentDialogBackground}"
BorderThickness="0,1,0,0"
CornerRadius="{ThemeResource ControlCornerRadiusBottom}"
Style="{ThemeResource GridCardStyle}">
<StackPanel HorizontalAlignment="Left" Orientation="Vertical">
<TextBlock Text="{shcm:ResourceString Name=ViewModelGuideStaticResourceQualityHigh}" TextAlignment="Left"/>
<TextBlock Text="233 KB" TextAlignment="Left"/>
</StackPanel>
<StackPanel HorizontalAlignment="Right" Orientation="Vertical">
<TextBlock Text="{shcm:ResourceString Name=ViewModelGuideStaticResourceQualityRaw}" TextAlignment="Right"/>
<TextBlock Text="1030 KB" TextAlignment="Right"/>
</StackPanel>
</Grid>
</Grid>
</Border>
</cwc:ConstrainedBox>
<StackPanel Grid.Column="1" VerticalAlignment="Top">
<TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="{shcm:ResourceString Name=ViewGuideStepStaticResourceSettingQualityHeader}"/>
<ListView
MinWidth="320"
Margin="0,8,0,32"
DisplayMemberPath="Name"
ItemsSource="{Binding StaticResourceOptions.ImageQualities}"
SelectedItem="{Binding StaticResourceOptions.ImageQuality, Mode=TwoWay}"/>
<TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="{shcm:ResourceString Name=ViewGuideStepStaticResourceSettingMinimumHeader}"/>
<ListView
MinWidth="320"
Margin="0,8,0,32"
DisplayMemberPath="Name"
ItemsSource="{Binding StaticResourceOptions.ImageArchives}"
SelectedItem="{Binding StaticResourceOptions.ImageArchive, Mode=TwoWay}"/>
<TextBlock Margin="0,16,0,0" Text="{Binding StaticResourceOptions.SizeInformationText, Mode=OneWay}"/>
</StackPanel>
</Grid>
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"

View File

@@ -381,7 +381,7 @@
ItemTemplate="{StaticResource InventoryItemTemplate}" ItemTemplate="{StaticResource InventoryItemTemplate}"
ItemsSource="{Binding InventoryItems}"> ItemsSource="{Binding InventoryItems}">
<ItemsRepeater.Layout> <ItemsRepeater.Layout>
<cwcont:WrapLayout HorizontalSpacing="12" VerticalSpacing="12"/> <shcl:WrapLayout HorizontalSpacing="12" VerticalSpacing="12"/>
</ItemsRepeater.Layout> </ItemsRepeater.Layout>
</ItemsRepeater> </ItemsRepeater>
</ScrollView> </ScrollView>

View File

@@ -507,6 +507,12 @@
Text="{shcm:ResourceString Name=ViewPageDailyNoteSettingRefreshHeader}"/> Text="{shcm:ResourceString Name=ViewPageDailyNoteSettingRefreshHeader}"/>
</cwcont:HeaderedContentControl.Header> </cwcont:HeaderedContentControl.Header>
<StackPanel Spacing="{StaticResource SettingsCardSpacing}"> <StackPanel Spacing="{StaticResource SettingsCardSpacing}">
<InfoBar
Title="{shcm:ResourceString Name=ViewPageDailyNoteSettingRefreshNotifyIconDisabledHint}"
IsClosable="False"
IsOpen="True"
Severity="Warning"
Visibility="{Binding AppOptions.IsNotifyIconEnabled, Converter={StaticResource BoolToVisibilityRevertConverter}}"/>
<cwcont:SettingsCard <cwcont:SettingsCard
Description="{shcm:ResourceString Name=ViewPageDailyNoteSettingAutoRefreshDescription}" Description="{shcm:ResourceString Name=ViewPageDailyNoteSettingAutoRefreshDescription}"
Header="{shcm:ResourceString Name=ViewPageDailyNoteSettingAutoRefresh}" Header="{shcm:ResourceString Name=ViewPageDailyNoteSettingAutoRefresh}"

View File

@@ -104,8 +104,10 @@
</Border> </Border>
</Border> </Border>
<cwc:SwitchPresenter Grid.Row="1" Value="{Binding ElementName=ItemsPanelSelector, Path=Current}"> <cwc:SwitchPresenter
Grid.Row="1"
ContentTransitions="{ThemeResource EntranceThemeTransitions}"
Value="{Binding ElementName=ItemsPanelSelector, Path=Current}">
<cwc:Case Value="List"> <cwc:Case Value="List">
<Border Margin="16,0,16,16" cw:Effects.Shadow="{ThemeResource CompatCardShadow}"> <Border Margin="16,0,16,16" cw:Effects.Shadow="{ThemeResource CompatCardShadow}">
<Border Style="{ThemeResource AcrylicBorderCardStyle}"> <Border Style="{ThemeResource AcrylicBorderCardStyle}">
@@ -113,7 +115,7 @@
DisplayMode="Inline" DisplayMode="Inline"
IsPaneOpen="True" IsPaneOpen="True"
OpenPaneLength="{StaticResource CompatSplitViewOpenPaneLength2}" OpenPaneLength="{StaticResource CompatSplitViewOpenPaneLength2}"
PaneBackground="{StaticResource CardBackgroundFillColorSecondary}"> PaneBackground="{ThemeResource CardBackgroundFillColorSecondaryBrush}">
<SplitView.Pane> <SplitView.Pane>
<ListView <ListView
Grid.Row="1" Grid.Row="1"

View File

@@ -218,7 +218,7 @@
DisplayMode="Inline" DisplayMode="Inline"
IsPaneOpen="True" IsPaneOpen="True"
OpenPaneLength="{StaticResource CompatSplitViewOpenPaneLength}" OpenPaneLength="{StaticResource CompatSplitViewOpenPaneLength}"
PaneBackground="{StaticResource CardBackgroundFillColorSecondary}"> PaneBackground="{ThemeResource CardBackgroundFillColorSecondaryBrush}">
<SplitView.Pane> <SplitView.Pane>
<ListView <ListView
Grid.Row="1" Grid.Row="1"

View File

@@ -372,8 +372,13 @@ internal sealed partial class AchievementViewModel : Abstraction.ViewModel, INav
private void UpdateAchievementsFinishPercent() private void UpdateAchievementsFinishPercent()
{ {
// 保存成就状态时,需要保持当前选择的成就分类
AchievementGoalView? currentSelectedAchievementGoal = SelectedAchievementGoal;
// 仅 读取成就列表 与 保存成就状态 时需要刷新成就进度 // 仅 读取成就列表 与 保存成就状态 时需要刷新成就进度
AchievementFinishPercent.Update(this); AchievementFinishPercent.Update(this);
SelectedAchievementGoal = currentSelectedAchievementGoal;
} }
[Command("SaveAchievementCommand")] [Command("SaveAchievementCommand")]

View File

@@ -1,6 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved. // Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license. // Licensed under the MIT license.
using Microsoft.UI.Xaml.Controls;
using Snap.Hutao.Factory.ContentDialog; using Snap.Hutao.Factory.ContentDialog;
using Snap.Hutao.Model.Entity; using Snap.Hutao.Model.Entity;
using Snap.Hutao.Service.Cultivation; using Snap.Hutao.Service.Cultivation;
@@ -110,10 +111,17 @@ internal sealed partial class CultivationViewModel : Abstraction.ViewModel
return; return;
} }
await cultivationService.RemoveProjectAsync(project).ConfigureAwait(false); ContentDialogResult result = await contentDialogFactory
await taskContext.SwitchToMainThreadAsync(); .CreateForConfirmCancelAsync(SH.ViewModelCultivationRemoveProjectTitle, SH.ViewModelCultivationRemoveProjectContent)
ArgumentNullException.ThrowIfNull(Projects); .ConfigureAwait(false);
SelectedProject = Projects.FirstOrDefault();
if (result is ContentDialogResult.Primary)
{
await cultivationService.RemoveProjectAsync(project).ConfigureAwait(false);
await taskContext.SwitchToMainThreadAsync();
ArgumentNullException.ThrowIfNull(Projects);
SelectedProject = Projects.FirstOrDefault();
}
} }
private async ValueTask UpdateEntryCollectionAsync(CultivateProject? project) private async ValueTask UpdateEntryCollectionAsync(CultivateProject? project)

View File

@@ -6,6 +6,7 @@ using Snap.Hutao.Control.Extension;
using Snap.Hutao.Core; using Snap.Hutao.Core;
using Snap.Hutao.Factory.ContentDialog; using Snap.Hutao.Factory.ContentDialog;
using Snap.Hutao.Model.Entity; using Snap.Hutao.Model.Entity;
using Snap.Hutao.Service;
using Snap.Hutao.Service.DailyNote; using Snap.Hutao.Service.DailyNote;
using Snap.Hutao.Service.Metadata; using Snap.Hutao.Service.Metadata;
using Snap.Hutao.Service.Notification; using Snap.Hutao.Service.Notification;
@@ -32,12 +33,15 @@ internal sealed partial class DailyNoteViewModel : Abstraction.ViewModel
private readonly IInfoBarService infoBarService; private readonly IInfoBarService infoBarService;
private readonly ITaskContext taskContext; private readonly ITaskContext taskContext;
private readonly IUserService userService; private readonly IUserService userService;
private readonly AppOptions appOptions;
private ObservableCollection<UserAndUid>? userAndUids; private ObservableCollection<UserAndUid>? userAndUids;
private ObservableCollection<DailyNoteEntry>? dailyNoteEntries; private ObservableCollection<DailyNoteEntry>? dailyNoteEntries;
public DailyNoteOptions DailyNoteOptions { get => dailyNoteOptions; } public DailyNoteOptions DailyNoteOptions { get => dailyNoteOptions; }
public AppOptions AppOptions { get => appOptions; }
public IWebViewerSource VerifyUrlSource { get; } = new DailyNoteWebViewerSource(); public IWebViewerSource VerifyUrlSource { get; } = new DailyNoteWebViewerSource();
/// <summary> /// <summary>

View File

@@ -66,7 +66,7 @@ internal sealed partial class TypedWishSummary : Wish
/// </summary> /// </summary>
public string TotalOrangeFormatted public string TotalOrangeFormatted
{ {
get => $"{TotalOrangePull} [{TotalOrangePercent,6:p2}]"; get => $"{TotalOrangePull} [{(TotalOrangePercent is double.NaN ? 0D : TotalOrangePercent),6:p2}]";
} }
/// <summary> /// <summary>
@@ -74,7 +74,7 @@ internal sealed partial class TypedWishSummary : Wish
/// </summary> /// </summary>
public string TotalPurpleFormatted public string TotalPurpleFormatted
{ {
get => $"{TotalPurplePull} [{TotalPurplePercent,6:p2}]"; get => $"{TotalPurplePull} [{(TotalPurplePercent is double.NaN ? 0D : TotalPurplePercent),6:p2}]";
} }
/// <summary> /// <summary>
@@ -82,7 +82,7 @@ internal sealed partial class TypedWishSummary : Wish
/// </summary> /// </summary>
public string TotalBlueFormatted public string TotalBlueFormatted
{ {
get => $"{TotalBluePull} [{TotalBluePercent,6:p2}]"; get => $"{TotalBluePull} [{(TotalBluePercent is double.NaN ? 0D : TotalBluePercent),6:p2}]";
} }
public ColorSegmentCollection PullPercentSegmentSource public ColorSegmentCollection PullPercentSegmentSource

View File

@@ -49,10 +49,21 @@ internal class Response : ICommonResponse<Response>
response.Message = SH.FormatWebResponseRefreshCookieHintFormat(response.Message); response.Message = SH.FormatWebResponseRefreshCookieHintFormat(response.Message);
} }
switch ((KnownReturnCode)response.ReturnCode)
{
case KnownReturnCode.PleaseLogin:
case KnownReturnCode.RET_TOKEN_INVALID:
response.Message = SH.FormatWebResponseRefreshCookieHintFormat(response.Message);
break;
case KnownReturnCode.SignInError:
response.Message = SH.FormatWebResponseSignInErrorHint(response.Message);
break;
}
return response; return response;
} }
public static Response<TData> CloneReturnCodeAndMessage<TData, TOther>(Response<TOther> response, [CallerMemberName] string callerName = default!) public static Response<TData> CloneReturnCodeAndMessage<TData, TOther>(Response<TOther> response)
{ {
return new(response.ReturnCode, response.Message, default); return new(response.ReturnCode, response.Message, default);
} }
@@ -63,16 +74,14 @@ internal class Response : ICommonResponse<Response>
{ {
return true; return true;
} }
else
{
if (showInfoBar)
{
serviceProvider ??= Ioc.Default;
serviceProvider.GetRequiredService<IInfoBarService>().Error(ToString());
}
return false; if (showInfoBar)
{
serviceProvider ??= Ioc.Default;
serviceProvider.GetRequiredService<IInfoBarService>().Error(ToString());
} }
return false;
} }
public override string ToString() public override string ToString()
@@ -102,20 +111,12 @@ internal class Response<TData> : Response, ICommonResponse<Response<TData>>, IJs
[MemberNotNullWhen(true, nameof(Data))] [MemberNotNullWhen(true, nameof(Data))]
public override bool IsOk(bool showInfoBar = true, IServiceProvider? serviceProvider = null) public override bool IsOk(bool showInfoBar = true, IServiceProvider? serviceProvider = null)
{ {
if (ReturnCode == 0) bool result = base.IsOk(showInfoBar, serviceProvider);
if (result)
{ {
ArgumentNullException.ThrowIfNull(Data); ArgumentNullException.ThrowIfNull(Data);
return true;
} }
else
{
if (showInfoBar)
{
serviceProvider ??= Ioc.Default;
serviceProvider.GetRequiredService<IInfoBarService>().Error(ToString());
}
return false; return result;
}
} }
} }