diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.en.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.en.resx
index 3c70037..4190042 100644
--- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.en.resx
+++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.en.resx
@@ -2453,6 +2453,9 @@ Space Available: {2}
Go
+
+ Sync All Characters and Weapons
+
Sync Inventory Items
diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx
index 1701d4e..9b55ffb 100644
--- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx
+++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx
@@ -2504,6 +2504,9 @@
前往
+
+ 同步所有角色与武器
+
同步背包物品
diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.zh-Hant.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.zh-Hant.resx
index d5d5541..17c10c8 100644
--- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.zh-Hant.resx
+++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.zh-Hant.resx
@@ -2429,6 +2429,9 @@
前往
+
+ 同步所有角色與武器
+
同步背包物品
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/AvatarPropertyBatchCultivateService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/AvatarPropertyBatchCultivateService.cs
new file mode 100644
index 0000000..a86e938
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/AvatarPropertyBatchCultivateService.cs
@@ -0,0 +1,179 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Microsoft.UI.Xaml.Controls;
+using Snap.Hutao.Factory.ContentDialog;
+using Snap.Hutao.Model.Calculable;
+using Snap.Hutao.Model.Cultivation;
+using Snap.Hutao.Model.Entity.Primitive;
+using Snap.Hutao.Service.AvatarInfo.Factory;
+using Snap.Hutao.Service.Cultivation.Consumption;
+using Snap.Hutao.Service.Cultivation.Offline;
+using Snap.Hutao.UI.Xaml.View.Dialog;
+using Snap.Hutao.ViewModel.AvatarProperty;
+using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
+using System.Collections.Immutable;
+using CalculatorAvatarPromotionDelta = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.AvatarPromotionDelta;
+using CalculatorBatchConsumption = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.BatchConsumption;
+using CalculatorConsumption = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.Consumption;
+using CalculatorItemHelper = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.ItemHelper;
+
+namespace Snap.Hutao.Service.Cultivation;
+
+[Service(ServiceLifetime.Singleton, typeof(IAvatarPropertyBatchCultivateService))]
+internal sealed partial class AvatarPropertyBatchCultivateService : IAvatarPropertyBatchCultivateService
+{
+ private readonly IContentDialogFactory contentDialogFactory;
+ private readonly ICultivationService cultivationService;
+ private readonly IServiceProvider serviceProvider;
+
+ [GeneratedConstructor]
+ public partial AvatarPropertyBatchCultivateService(IServiceProvider serviceProvider);
+
+ public async ValueTask ExecuteAsync(SummaryFactoryMetadataContext metadataContext, ImmutableArray targetAvatars, CancellationToken cancellationToken)
+ {
+ CultivateProjectAvatarPropertyBatchPreferences? batchPrefs = await cultivationService
+ .GetAvatarPropertyBatchCultivatePreferencesForCurrentProjectAsync()
+ .ConfigureAwait(false);
+
+ CultivatePromotionDeltaBatchDialog dialog = batchPrefs is null
+ ? await contentDialogFactory
+ .CreateInstanceAsync(serviceProvider)
+ .ConfigureAwait(false)
+ : await contentDialogFactory
+ .CreateInstanceAsync(serviceProvider, batchPrefs)
+ .ConfigureAwait(false);
+
+ if (await dialog.GetPromotionDeltaBaselineAsync().ConfigureAwait(false) is not (true, { } baseline))
+ {
+ return null;
+ }
+
+ await cultivationService
+ .SaveAvatarPropertyBatchCultivatePreferencesForCurrentProjectAsync(ToAvatarPropertyBatchPreferences(baseline))
+ .ConfigureAwait(false);
+
+ ArgumentNullException.ThrowIfNull(baseline.Delta.Weapon);
+
+ ContentDialog progressDialog = await contentDialogFactory
+ .CreateForIndeterminateProgressAsync(SH.ViewModelAvatarPropertyBatchCultivateProgressTitle)
+ .ConfigureAwait(false);
+
+ BatchCultivateResult result = default;
+ using (await contentDialogFactory.BlockAsync(progressDialog).ConfigureAwait(false))
+ {
+ if (baseline.ClearAvatarAndWeaponEntriesBeforeSync)
+ {
+ await cultivationService.RemoveAvatarAndWeaponEntriesForCurrentProjectAsync().ConfigureAwait(false);
+ }
+
+ ImmutableArray.Builder deltasBuilder = ImmutableArray.CreateBuilder();
+ foreach (AvatarView avatar in targetAvatars)
+ {
+ if (!baseline.Delta.TryGetNonErrorCopy(avatar, out CalculatorAvatarPromotionDelta? copy))
+ {
+ ++result.SkippedCount;
+ continue;
+ }
+
+ deltasBuilder.Add(copy);
+ }
+
+ ImmutableArray deltas = deltasBuilder.ToImmutable();
+
+ CalculatorBatchConsumption batchConsumption = OfflineCalculator.CalculateBatchConsumption(deltas, metadataContext);
+
+ foreach ((CalculatorConsumption consumption, CalculatorAvatarPromotionDelta delta) in batchConsumption.Items.Zip(deltas))
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ if (!await SaveCultivationAsync(consumption, new CultivatePromotionDeltaOptions(delta, baseline.Strategy)).ConfigureAwait(false))
+ {
+ break;
+ }
+
+ ++result.SucceedCount;
+ }
+ }
+
+ return result;
+ }
+
+ private static CultivateProjectAvatarPropertyBatchPreferences ToAvatarPropertyBatchPreferences(CultivatePromotionDeltaOptions baseline)
+ {
+ CalculatorAvatarPromotionDelta d = baseline.Delta;
+ uint sa = 10;
+ uint se = 10;
+ uint sq = 10;
+ if (d.SkillList is [{ } a, { } e, { } q, ..])
+ {
+ sa = a.LevelTarget;
+ se = e.LevelTarget;
+ sq = q.LevelTarget;
+ }
+
+ return new CultivateProjectAvatarPropertyBatchPreferences
+ {
+ AvatarLevelTarget = d.AvatarLevelTarget,
+ SkillATarget = sa,
+ SkillETarget = se,
+ SkillQTarget = sq,
+ WeaponLevelTarget = d.Weapon?.LevelTarget ?? 90U,
+ ConsumptionSaveStrategyIndex = (int)baseline.Strategy,
+ ClearAvatarAndWeaponEntriesBeforeSync = baseline.ClearAvatarAndWeaponEntriesBeforeSync,
+ };
+ }
+
+ private async ValueTask SaveCultivationAsync(CalculatorConsumption consumption, CultivatePromotionDeltaOptions options)
+ {
+ LevelInformation levelInformation = LevelInformation.From(options.Delta);
+
+ InputConsumption avatarInput = new()
+ {
+ Type = CultivateType.AvatarAndSkill,
+ ItemId = options.Delta.AvatarId,
+ Items = CalculatorItemHelper.Merge(consumption.AvatarConsume, consumption.AvatarSkillConsume),
+ LevelInformation = levelInformation,
+ Strategy = options.Strategy,
+ };
+
+ ConsumptionSaveResult avatarSave = await cultivationService.SaveConsumptionAsync(avatarInput).ConfigureAwait(false);
+
+ if (avatarSave.Kind is ConsumptionSaveResultKind.NoProject)
+ {
+ return false;
+ }
+
+ ArgumentNullException.ThrowIfNull(options.Delta.Weapon);
+
+ Guid? relatedAvatarEntryId = avatarSave.CreatedEntryInnerId;
+ if (relatedAvatarEntryId is null && avatarSave.Kind is ConsumptionSaveResultKind.Skipped)
+ {
+ relatedAvatarEntryId = await cultivationService.TryGetAvatarCultivateEntryInnerIdAsync(options.Delta.AvatarId).ConfigureAwait(false);
+ }
+
+ if (relatedAvatarEntryId is null && avatarSave.Kind is ConsumptionSaveResultKind.NoItem)
+ {
+ relatedAvatarEntryId = await cultivationService.TryGetAvatarCultivateEntryInnerIdAsync(options.Delta.AvatarId).ConfigureAwait(false);
+ }
+
+ if (relatedAvatarEntryId is null && !consumption.WeaponConsume.IsEmpty)
+ {
+ relatedAvatarEntryId = await cultivationService.EnsureAvatarAssociationStubAsync(options.Delta.AvatarId, levelInformation).ConfigureAwait(false);
+ }
+
+ InputConsumption weaponInput = new()
+ {
+ Type = CultivateType.Weapon,
+ ItemId = options.Delta.Weapon.Id,
+ Items = consumption.WeaponConsume,
+ LevelInformation = levelInformation,
+ Strategy = options.Strategy,
+ RelatedEntryId = relatedAvatarEntryId,
+ };
+
+ ConsumptionSaveResult weaponSave = await cultivationService.SaveConsumptionAsync(weaponInput).ConfigureAwait(false);
+
+ return weaponSave.Kind is not ConsumptionSaveResultKind.NoProject;
+ }
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/BatchCultivateResult.cs b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/BatchCultivateResult.cs
similarity index 79%
rename from src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/BatchCultivateResult.cs
rename to src/Snap.Hutao/Snap.Hutao/Service/Cultivation/BatchCultivateResult.cs
index 266f052..9fea17f 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/BatchCultivateResult.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/BatchCultivateResult.cs
@@ -1,10 +1,10 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
-namespace Snap.Hutao.ViewModel.AvatarProperty;
+namespace Snap.Hutao.Service.Cultivation;
internal struct BatchCultivateResult
{
public int SucceedCount;
public int SkippedCount;
-}
\ No newline at end of file
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/IAvatarPropertyBatchCultivateService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/IAvatarPropertyBatchCultivateService.cs
new file mode 100644
index 0000000..c5b269c
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/IAvatarPropertyBatchCultivateService.cs
@@ -0,0 +1,16 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Service.AvatarInfo.Factory;
+using Snap.Hutao.ViewModel.AvatarProperty;
+using System.Collections.Immutable;
+
+namespace Snap.Hutao.Service.Cultivation;
+
+internal interface IAvatarPropertyBatchCultivateService
+{
+ ///
+ /// when the baseline dialog is dismissed without confirmation.
+ ///
+ ValueTask ExecuteAsync(SummaryFactoryMetadataContext metadataContext, ImmutableArray targetAvatars, CancellationToken cancellationToken);
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/UI/Xaml/View/Page/CultivationPage.xaml b/src/Snap.Hutao/Snap.Hutao/UI/Xaml/View/Page/CultivationPage.xaml
index f32349c..2e87130 100644
--- a/src/Snap.Hutao/Snap.Hutao/UI/Xaml/View/Page/CultivationPage.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/UI/Xaml/View/Page/CultivationPage.xaml
@@ -467,6 +467,10 @@
CommandParameter="{Binding Projects.CurrentItem, Mode=OneWay}"
Icon="{shuxm:FontIcon Glyph=}"
Label="{shuxm:ResourceString Name=ViewPageCultivationClearInventory}"/>
+
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/AvatarPropertyViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/AvatarPropertyViewModel.cs
index a2358dc..efc0584 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/AvatarPropertyViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/AvatarPropertyViewModel.cs
@@ -16,6 +16,7 @@ using Snap.Hutao.Service.AvatarInfo;
using Snap.Hutao.Service.AvatarInfo.Factory;
using Snap.Hutao.Service.Cultivation;
using Snap.Hutao.Service.Cultivation.Consumption;
+using BatchCultivateResult = Snap.Hutao.Service.Cultivation.BatchCultivateResult;
using Snap.Hutao.Service.Cultivation.Offline;
using Snap.Hutao.Service.Metadata.ContextAbstraction;
using Snap.Hutao.Service.Notification;
@@ -28,7 +29,6 @@ using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
using System.Collections.Immutable;
using System.Globalization;
using System.Runtime.CompilerServices;
-using CalculatorAvatarPromotionDelta = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.AvatarPromotionDelta;
using CalculatorBatchConsumption = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.BatchConsumption;
using CalculatorConsumption = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.Consumption;
using CalculatorItemHelper = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.ItemHelper;
@@ -41,6 +41,7 @@ internal sealed partial class AvatarPropertyViewModel : Abstraction.ViewModel, I
{
private readonly ExclusiveTokenProvider refreshTokenProvider = new();
private readonly AvatarPropertyViewModelScopeContext scopeContext;
+ private readonly IAvatarPropertyBatchCultivateService avatarPropertyBatchCultivateService;
private SummaryFactoryMetadataContext? metadataContext;
@@ -270,69 +271,16 @@ internal sealed partial class AvatarPropertyViewModel : Abstraction.ViewModel, I
targetAvatars = [.. avatars.Source];
}
- CultivateProjectAvatarPropertyBatchPreferences? batchPrefs = await scopeContext.CultivationService
- .GetAvatarPropertyBatchCultivatePreferencesForCurrentProjectAsync()
+ ArgumentNullException.ThrowIfNull(metadataContext);
+ BatchCultivateResult? batchResult = await avatarPropertyBatchCultivateService
+ .ExecuteAsync(metadataContext, targetAvatars, CancellationToken)
.ConfigureAwait(false);
- CultivatePromotionDeltaBatchDialog dialog = batchPrefs is null
- ? await scopeContext.ContentDialogFactory
- .CreateInstanceAsync(scopeContext.ServiceProvider)
- .ConfigureAwait(false)
- : await scopeContext.ContentDialogFactory
- .CreateInstanceAsync(scopeContext.ServiceProvider, batchPrefs)
- .ConfigureAwait(false);
-
- if (await dialog.GetPromotionDeltaBaselineAsync().ConfigureAwait(false) is not (true, { } baseline))
+ if (batchResult is not { } result)
{
return;
}
- await scopeContext.CultivationService
- .SaveAvatarPropertyBatchCultivatePreferencesForCurrentProjectAsync(ToAvatarPropertyBatchPreferences(baseline))
- .ConfigureAwait(false);
-
- ArgumentNullException.ThrowIfNull(baseline.Delta.Weapon);
-
- ContentDialog progressDialog = await scopeContext.ContentDialogFactory
- .CreateForIndeterminateProgressAsync(SH.ViewModelAvatarPropertyBatchCultivateProgressTitle)
- .ConfigureAwait(false);
-
- BatchCultivateResult result = default;
- using (await scopeContext.ContentDialogFactory.BlockAsync(progressDialog).ConfigureAwait(false))
- {
- if (baseline.ClearAvatarAndWeaponEntriesBeforeSync)
- {
- await scopeContext.CultivationService.RemoveAvatarAndWeaponEntriesForCurrentProjectAsync().ConfigureAwait(false);
- }
-
- ImmutableArray.Builder deltasBuilder = ImmutableArray.CreateBuilder();
- foreach (AvatarView avatar in targetAvatars)
- {
- if (!baseline.Delta.TryGetNonErrorCopy(avatar, out CalculatorAvatarPromotionDelta? copy))
- {
- ++result.SkippedCount;
- continue;
- }
-
- deltasBuilder.Add(copy);
- }
-
- ImmutableArray deltas = deltasBuilder.ToImmutable();
-
- ArgumentNullException.ThrowIfNull(metadataContext);
- CalculatorBatchConsumption batchConsumption = OfflineCalculator.CalculateBatchConsumption(deltas, metadataContext);
-
- foreach ((CalculatorConsumption consumption, CalculatorAvatarPromotionDelta delta) in batchConsumption.Items.Zip(deltas))
- {
- if (!await SaveCultivationAsync(consumption, new CultivatePromotionDeltaOptions(delta, baseline.Strategy), true).ConfigureAwait(false))
- {
- break;
- }
-
- ++result.SucceedCount;
- }
- }
-
scopeContext.Messenger.Send(CultivationProjectEntriesChangedMessage.Empty);
InfoBarMessage message = result.SkippedCount > 0
@@ -342,31 +290,6 @@ internal sealed partial class AvatarPropertyViewModel : Abstraction.ViewModel, I
scopeContext.Messenger.Send(message);
}
- private static CultivateProjectAvatarPropertyBatchPreferences ToAvatarPropertyBatchPreferences(CultivatePromotionDeltaOptions baseline)
- {
- CalculatorAvatarPromotionDelta d = baseline.Delta;
- uint sa = 10;
- uint se = 10;
- uint sq = 10;
- if (d.SkillList is [{ } a, { } e, { } q, ..])
- {
- sa = a.LevelTarget;
- se = e.LevelTarget;
- sq = q.LevelTarget;
- }
-
- return new CultivateProjectAvatarPropertyBatchPreferences
- {
- AvatarLevelTarget = d.AvatarLevelTarget,
- SkillATarget = sa,
- SkillETarget = se,
- SkillQTarget = sq,
- WeaponLevelTarget = d.Weapon?.LevelTarget ?? 90U,
- ConsumptionSaveStrategyIndex = (int)baseline.Strategy,
- ClearAvatarAndWeaponEntriesBeforeSync = baseline.ClearAvatarAndWeaponEntriesBeforeSync,
- };
- }
-
/// if we can continue saving consumptions, otherwise .
private async ValueTask SaveCultivationAsync(CalculatorConsumption consumption, CultivatePromotionDeltaOptions options, bool isBatch = false)
{
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Cultivation/CultivationViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Cultivation/CultivationViewModel.cs
index e4d79e0..8c16b39 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Cultivation/CultivationViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Cultivation/CultivationViewModel.cs
@@ -11,19 +11,24 @@ using Snap.Hutao.Core.Logging;
using Snap.Hutao.Core.Setting;
using Snap.Hutao.Factory.ContentDialog;
using Snap.Hutao.Model.Entity;
+using Snap.Hutao.Service.AvatarInfo;
+using Snap.Hutao.Service.AvatarInfo.Factory;
using Snap.Hutao.Service.Cultivation;
using Snap.Hutao.Service.Inventory;
using Snap.Hutao.Service.Metadata;
using Snap.Hutao.Service.Metadata.ContextAbstraction;
using Snap.Hutao.Service.Navigation;
using Snap.Hutao.Service.Notification;
+using Snap.Hutao.Service.User;
using Snap.Hutao.Service.Yae;
+using Snap.Hutao.ViewModel.AvatarProperty;
using Snap.Hutao.UI.Xaml.Control.AutoSuggestBox;
using Snap.Hutao.UI.Xaml.Data;
using Snap.Hutao.UI.Xaml.View.Dialog;
using Snap.Hutao.ViewModel.Game;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
+using BatchCultivateResult = Snap.Hutao.Service.Cultivation.BatchCultivateResult;
namespace Snap.Hutao.ViewModel.Cultivation;
@@ -38,6 +43,9 @@ internal sealed partial class CultivationViewModel : Abstraction.ViewModel, IRec
private readonly ICultivationService cultivationService;
private readonly INavigationService navigationService;
private readonly IInventoryService inventoryService;
+ private readonly IAvatarInfoService avatarInfoService;
+ private readonly IAvatarPropertyBatchCultivateService avatarPropertyBatchCultivateService;
+ private readonly IUserService userService;
private readonly IServiceProvider serviceProvider;
private readonly IMetadataService metadataService;
private readonly ITaskContext taskContext;
@@ -322,6 +330,57 @@ internal sealed partial class CultivationViewModel : Abstraction.ViewModel, IRec
}
}
+ [Command("SyncAllAvatarsAndWeaponsCommand")]
+ private async Task SyncAllAvatarsAndWeaponsAsync()
+ {
+ SentrySdk.AddBreadcrumb(BreadcrumbFactory2.CreateUI("Sync all avatars and weapons to cultivation", "CultivationViewModel.Command", []));
+
+ if (Projects?.CurrentItem is null)
+ {
+ return;
+ }
+
+ if (await userService.GetCurrentUserAndUidAsync().ConfigureAwait(false) is not { } userAndUid)
+ {
+ messenger.Send(InfoBarMessage.Warning(SH.MustSelectUserAndUid));
+ return;
+ }
+
+ SummaryFactoryMetadataContext summaryContext = await metadataService.GetContextAsync(CancellationToken).ConfigureAwait(false);
+ Summary? summary = await avatarInfoService.GetSummaryAsync(summaryContext, userAndUid, global::Snap.Hutao.Service.AvatarInfo.RefreshOptionKind.None, CancellationToken).ConfigureAwait(false);
+
+ if (summary is not { Avatars: { } avatars })
+ {
+ messenger.Send(InfoBarMessage.Warning(SH.ViewPageAvatarPropertyDefaultDescription));
+ return;
+ }
+
+ if (avatars.Source.Count < 1)
+ {
+ messenger.Send(InfoBarMessage.Warning(SH.ViewPageAvatarPropertyDefaultDescription));
+ return;
+ }
+
+ ImmutableArray targetAvatars = [.. avatars.Source];
+
+ BatchCultivateResult? batchResult = await avatarPropertyBatchCultivateService
+ .ExecuteAsync(summaryContext, targetAvatars, CancellationToken)
+ .ConfigureAwait(false);
+
+ if (batchResult is not { } result)
+ {
+ return;
+ }
+
+ messenger.Send(CultivationProjectEntriesChangedMessage.Empty);
+
+ InfoBarMessage message = result.SkippedCount > 0
+ ? InfoBarMessage.Warning(SH.FormatViewModelCultivationBatchAddIncompleted(result.SucceedCount, result.SkippedCount))
+ : InfoBarMessage.Success(SH.FormatViewModelCultivationBatchAddCompleted(result.SucceedCount, result.SkippedCount));
+
+ messenger.Send(message);
+ }
+
[Command("ClearInventoryCommand")]
private async Task ClearInventoryAsync(CultivateProject? project)
{