feat: Mostly completed bidirectional binding for settings page and configuration.

This commit is contained in:
real-zony 2024-07-03 23:38:33 +08:00
parent e66ef89e7a
commit 9ab9cd50e2
10 changed files with 74 additions and 30 deletions

View File

@ -8,4 +8,9 @@ public class TagInfoOptions
/// 屏蔽词功能相关配置。 /// 屏蔽词功能相关配置。
/// </summary> /// </summary>
public BlockWordOptions BlockWord { get; set; } = null!; public BlockWordOptions BlockWord { get; set; } = null!;
public TagInfoProviderOptions GetTagProviderOption(string name)
{
return Plugin.FirstOrDefault(x => x.Name == name)!;
}
} }

View File

@ -1,8 +1,14 @@
using System; using System;
using System.IO;
using Avalonia; using Avalonia;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using YamlDotNet.Core;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
using ZonyLrcTools.Common.Configuration;
using ZonyLrcTools.Common.Infrastructure.DependencyInject; using ZonyLrcTools.Common.Infrastructure.DependencyInject;
using ZonyLrcTools.Common.Infrastructure.Network; using ZonyLrcTools.Common.Infrastructure.Network;
using ZonyLrcTools.Desktop.ViewModels; using ZonyLrcTools.Desktop.ViewModels;
@ -15,6 +21,13 @@ public class App : Application
public new static App Current => (App)Application.Current!; public new static App Current => (App)Application.Current!;
public IServiceProvider Services { get; } = ConfigureServices(); public IServiceProvider Services { get; } = ConfigureServices();
public IOptions<GlobalOptions> GlobalOptions => Services.GetRequiredService<IOptions<GlobalOptions>>();
private Lazy<ISerializer> _yamlSerializer = new(() => new SerializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.WithDefaultScalarStyle(ScalarStyle.DoubleQuoted)
.Build());
private static IServiceProvider ConfigureServices() private static IServiceProvider ConfigureServices()
{ {
var services = new ServiceCollection(); var services = new ServiceCollection();
@ -44,4 +57,11 @@ public class App : Application
base.OnFrameworkInitializationCompleted(); base.OnFrameworkInitializationCompleted();
} }
public void UpdateConfiguration()
{
var configPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "config.yaml");
var yamlString = _yamlSerializer.Value.Serialize(GlobalOptions.Value);
File.WriteAllText(configPath, yamlString);
}
} }

View File

@ -1,5 +1,3 @@
using System;
using ReactiveUI;
using ReactiveUI.Fody.Helpers; using ReactiveUI.Fody.Helpers;
using ZonyLrcTools.Common.Configuration; using ZonyLrcTools.Common.Configuration;
@ -12,12 +10,8 @@ public class BlockWordViewModel : ViewModelBase
IsEnable = options.Provider.Tag.BlockWord.IsEnable; IsEnable = options.Provider.Tag.BlockWord.IsEnable;
FilePath = options.Provider.Tag.BlockWord.FilePath; FilePath = options.Provider.Tag.BlockWord.FilePath;
this.WhenAnyValue(x => x.IsEnable, x => x.FilePath) SubscribeToProperty(this, x => x.IsEnable, x => options.Provider.Tag.BlockWord.IsEnable = x);
.Subscribe(_ => SubscribeToProperty(this, x => x.FilePath, x => options.Provider.Tag.BlockWord.FilePath = x!);
{
options.Provider.Tag.BlockWord.IsEnable = IsEnable;
options.Provider.Tag.BlockWord.FilePath = FilePath;
});
} }
[Reactive] public bool IsEnable { get; set; } [Reactive] public bool IsEnable { get; set; }

View File

@ -21,14 +21,10 @@ public class GlobalConfigurationViewModel : ViewModelBase
IsSkipExistLyricFiles = globalConfig.IsSkipExistLyricFiles; IsSkipExistLyricFiles = globalConfig.IsSkipExistLyricFiles;
IsOnlyOutputTranslation = globalConfig.IsOnlyOutputTranslation; IsOnlyOutputTranslation = globalConfig.IsOnlyOutputTranslation;
this.WhenAnyValue(x => x.IsOneLine) SubscribeToProperty(this, x => x.IsOneLine, x => globalConfig.IsOneLine = x);
.Subscribe(x => globalConfig.IsOneLine = x); SubscribeToProperty(this, x => x.IsEnableTranslation, x => globalConfig.IsEnableTranslation = x);
this.WhenAnyValue(x => x.IsEnableTranslation) SubscribeToProperty(this, x => x.IsSkipExistLyricFiles, x => globalConfig.IsSkipExistLyricFiles = x);
.Subscribe(x => globalConfig.IsEnableTranslation = x); SubscribeToProperty(this, x => x.IsOnlyOutputTranslation, x => globalConfig.IsOnlyOutputTranslation = x);
this.WhenAnyValue(x => x.IsSkipExistLyricFiles)
.Subscribe(x => globalConfig.IsSkipExistLyricFiles = x);
this.WhenAnyValue(x => x.IsOnlyOutputTranslation)
.Subscribe(x => globalConfig.IsOnlyOutputTranslation = x);
} }
[Reactive] public bool IsOneLine { get; set; } [Reactive] public bool IsOneLine { get; set; }

View File

@ -4,7 +4,6 @@ using Avalonia.Data;
using Avalonia.Data.Converters; using Avalonia.Data.Converters;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using ReactiveUI;
using ReactiveUI.Fody.Helpers; using ReactiveUI.Fody.Helpers;
using ZonyLrcTools.Common.Configuration; using ZonyLrcTools.Common.Configuration;
@ -20,10 +19,8 @@ public class LyricsProviderViewModel : ViewModelBase
Priority = options.Priority; Priority = options.Priority;
Depth = options.Depth; Depth = options.Depth;
this.WhenAnyValue(x => x.Priority) SubscribeToProperty(this, x => x.Priority, x => globalOptions.Provider.Lyric.GetLyricProviderOption(Name).Priority = x);
.Subscribe(priority => globalOptions.Provider.Lyric.GetLyricProviderOption(Name).Priority = priority); SubscribeToProperty(this, x => x.Depth, x => globalOptions.Provider.Lyric.GetLyricProviderOption(Name).Depth = x);
this.WhenAnyValue(x => x.Depth)
.Subscribe(depth => globalOptions.Provider.Lyric.GetLyricProviderOption(Name).Depth = depth);
} }
public string Name { get; set; } public string Name { get; set; }

View File

@ -1,5 +1,7 @@
using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using ReactiveUI.Fody.Helpers; using ReactiveUI.Fody.Helpers;
using ZonyLrcTools.Common.Configuration; using ZonyLrcTools.Common.Configuration;
@ -9,14 +11,32 @@ public class TagInfoProviderViewModel : ViewModelBase
{ {
public TagInfoProviderViewModel(TagInfoProviderOptions options) public TagInfoProviderViewModel(TagInfoProviderOptions options)
{ {
var globalOptions = App.Current.Services.GetRequiredService<IOptions<GlobalOptions>>().Value;
Name = options.Name; Name = options.Name;
Priority = options.Priority; Priority = options.Priority;
Extensions = new ObservableCollection<KeyValuePair<string, string>>(options.Extensions ?? new Dictionary<string, string>()); Extensions = options.Extensions == null ? [] : new ObservableCollection<ObservableKeyValuePair>(options.Extensions.Select(x => new ObservableKeyValuePair(x.Key, x.Value)));
SubscribeToProperty(this, x => x.Priority, x => globalOptions.Provider.Tag.GetTagProviderOption(Name).Priority = x);
SubscribeToProperty(this, x => x.Extensions, x => globalOptions.Provider.Tag.GetTagProviderOption(Name).Extensions = x.ToDictionary(pair => pair.Key, pair => pair.Value));
} }
public string? Name { get; set; } public string? Name { get; set; }
[Reactive] public int Priority { get; set; } [Reactive] public int Priority { get; set; }
public ObservableCollection<KeyValuePair<string, string>> Extensions { get; } [Reactive] public ObservableCollection<ObservableKeyValuePair> Extensions { get; set; }
}
public class ObservableKeyValuePair : ViewModelBase
{
[Reactive] public string Key { get; set; }
[Reactive] public string Value { get; set; }
public ObservableKeyValuePair(string key, string value)
{
Key = key;
Value = value;
}
} }

View File

@ -6,11 +6,8 @@ namespace ZonyLrcTools.Desktop.ViewModels.Settings;
public class TagInfoViewModel : ViewModelBase public class TagInfoViewModel : ViewModelBase
{ {
private readonly GlobalOptions _options;
public TagInfoViewModel(GlobalOptions options) public TagInfoViewModel(GlobalOptions options)
{ {
_options = options;
BlockWord = new BlockWordViewModel(options); BlockWord = new BlockWordViewModel(options);
TagInfoProviders = new ObservableCollection<TagInfoProviderViewModel>(options.Provider.Tag.Plugin.Select(p => new TagInfoProviderViewModel(p))); TagInfoProviders = new ObservableCollection<TagInfoProviderViewModel>(options.Provider.Tag.Plugin.Select(p => new TagInfoProviderViewModel(p)));
} }

View File

@ -1,5 +1,18 @@
using ReactiveUI; using System;
using System.Linq.Expressions;
using ReactiveUI;
namespace ZonyLrcTools.Desktop.ViewModels; namespace ZonyLrcTools.Desktop.ViewModels;
public abstract class ViewModelBase : ReactiveObject; public abstract class ViewModelBase : ReactiveObject
{
protected void SubscribeToProperty<TSender, TRet>(TSender sender, Expression<Func<TSender, TRet>> property, Action<TRet> updateGlobalConfigAction)
{
sender.WhenAnyValue(property)
.Subscribe(x =>
{
updateGlobalConfigAction(x);
App.Current.UpdateConfiguration();
});
}
}

View File

@ -33,7 +33,7 @@
<ItemGroup> <ItemGroup>
<None Remove="config.yaml"/> <None Remove="config.yaml"/>
<Content Include="config.yaml"> <Content Include="config.yaml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -5,12 +5,14 @@ using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Shouldly; using Shouldly;
using Xunit;
using ZonyLrcTools.Common.Album; using ZonyLrcTools.Common.Album;
namespace ZonyLrcTools.Tests.Infrastructure.Album namespace ZonyLrcTools.Tests.Infrastructure.Album
{ {
public class QQMusicAlbumDownloaderTests : TestBase public class QQMusicAlbumDownloaderTests : TestBase
{ {
[Fact]
public async Task DownloadDataAsync_Test() public async Task DownloadDataAsync_Test()
{ {
var downloader = ServiceProvider.GetRequiredService<IEnumerable<IAlbumProvider>>() var downloader = ServiceProvider.GetRequiredService<IEnumerable<IAlbumProvider>>()