[Preliminar] Ability to search settings
MODIFY
WalletWasabi.Fluent/ViewModels/SearchBar/ActionableItem.cs
WalletWasabi.Fluent/ViewModels/SearchBar/ActionableItem.cs
@@ -24,4 +24,5 @@ public class ActionableItem : IActionableItem
public string? Icon { get; set; }
public string Category { get; }
public IEnumerable<string> Keywords { get; }
+ public bool IsDefault { get; set; }
}
\ No newline at end of file

MODIFY
WalletWasabi.Fluent/ViewModels/SearchBar/AutocloseActionableItem.cs
WalletWasabi.Fluent/ViewModels/SearchBar/AutocloseActionableItem.cs
@@ -37,19 +37,5 @@ public class AutocloseActionableItem : IActionableItem
public string Category => _item.Category;
public IEnumerable<string> Keywords => _item.Keywords;
-}
-
-public interface IActionableItem : ISearchItem
-{
- Func<Task> OnExecution { get; }
-}
-
-public interface ISearchItem
-{
- string Name { get; }
- string Description { get; }
- ComposedKey Key { get; }
- string? Icon { get; set; }
- string Category { get; }
- IEnumerable<string> Keywords { get; }
+ public bool IsDefault => _item.IsDefault;
}
\ No newline at end of file

RENAME
WalletWasabi.Fluent/ViewModels/SearchBar/DarkThemeSelector.cs
WalletWasabi.Fluent/ViewModels/SearchBar/DarkThemeSetting.cs
@@ -6,14 +6,11 @@ using WalletWasabi.Fluent.Helpers;
namespace WalletWasabi.Fluent.ViewModels.SearchBar;
-public class DarkThemeSelector : ReactiveObject
+public class DarkThemeSetting : Setting<UiConfig, bool>
{
- private bool _isDarkThemeEnabled;
-
- public DarkThemeSelector()
+ public DarkThemeSetting(UiConfig uiConfig) : base(uiConfig, config => config.DarkModeEnabled)
{
- IsDarkThemeEnabled = ThemeHelper.CurrentTheme == Theme.Dark;
- this.WhenAnyValue(x => x.IsDarkThemeEnabled)
+ this.WhenAnyValue(x => x.Value)
.SelectMany(b =>
{
return Observable.FromAsync(() =>
@@ -24,10 +21,4 @@ public class DarkThemeSelector : ReactiveObject
})
.Subscribe();
}
-
- public bool IsDarkThemeEnabled
- {
- get => _isDarkThemeEnabled;
- set => this.RaiseAndSetIfChanged(ref _isDarkThemeEnabled, value);
- }
}
\ No newline at end of file

ADD
WalletWasabi.Fluent/ViewModels/SearchBar/IActionableItem.cs
+using System.Threading.Tasks;
+
+namespace WalletWasabi.Fluent.ViewModels.SearchBar;
+
+public interface IActionableItem : ISearchItem
+{
+ Func<Task> OnExecution { get; }
+}
\ No newline at end of file

ADD
WalletWasabi.Fluent/ViewModels/SearchBar/ISearchItem.cs
+using System.Collections.Generic;
+
+namespace WalletWasabi.Fluent.ViewModels.SearchBar;
+
+public interface ISearchItem
+{
+ string Name { get; }
+ string Description { get; }
+ ComposedKey Key { get; }
+ string? Icon { get; set; }
+ string Category { get; }
+ IEnumerable<string> Keywords { get; }
+ bool IsDefault { get; }
+}
\ No newline at end of file

MODIFY
WalletWasabi.Fluent/ViewModels/SearchBar/NonActionableSearchItem.cs
WalletWasabi.Fluent/ViewModels/SearchBar/NonActionableSearchItem.cs
@@ -21,4 +21,5 @@ public class NonActionableSearchItem : ISearchItem
public string? Icon { get; set; }
public string Category { get; }
public IEnumerable<string> Keywords { get; }
+ public bool IsDefault { get; set; }
}
\ No newline at end of file

ADD
WalletWasabi.Fluent/ViewModels/SearchBar/PropertyHelper.cs
+using System.Linq.Expressions;
+using System.Reflection;
+
+namespace WalletWasabi.Fluent.ViewModels.SearchBar;
+
+public static class PropertyHelper<T>
+{
+ public static PropertyInfo GetProperty<TValue>(
+ Expression<Func<T, TValue>> selector)
+ {
+ Expression body = selector;
+ if (body is LambdaExpression)
+ {
+ body = ((LambdaExpression)body).Body;
+ }
+
+ return body.NodeType switch
+ {
+ ExpressionType.MemberAccess => (PropertyInfo) ((MemberExpression) body).Member,
+ _ => throw new InvalidOperationException()
+ };
+ }
+}
\ No newline at end of file

MODIFY
WalletWasabi.Fluent/ViewModels/SearchBar/SearchBarDesignViewModel.cs
WalletWasabi.Fluent/ViewModels/SearchBar/SearchBarDesignViewModel.cs
@@ -33,7 +33,7 @@ public class SearchBarDesignViewModel : ReactiveObject
var nonActionable = new ISearchItem[]
{
- new NonActionableSearchItem(new DarkThemeSelector(), "Dark theme", "Appearance", new List<string>(), null)
+ new NonActionableSearchItem(new DarkThemeSetting(Services.UiConfig), "Dark theme", "Appearance", new List<string>(), null)
};
_items = actionable.Concat(nonActionable).ToList();

MODIFY
WalletWasabi.Fluent/ViewModels/SearchBar/SearchBarViewModel.cs
WalletWasabi.Fluent/ViewModels/SearchBar/SearchBarViewModel.cs
@@ -39,6 +39,10 @@ public partial class SearchBarViewModel : ReactiveObject
.Subscribe();
ShowListCommand = ReactiveCommand.Create(() => IsSearchListVisible = true);
+
+ //this.WhenAnyValue(x => x.IsSearchListVisible)
+ // .Where(x => x == false)
+ // .Subscribe(_ => SearchText = "");
}
public ICommand ShowListCommand { get; }
@@ -49,9 +53,9 @@ public partial class SearchBarViewModel : ReactiveObject
{
return searchItem =>
{
- if (text is null)
+ if (string.IsNullOrWhiteSpace(text))
{
- return true;
+ return searchItem.IsDefault;
}
var containsName = searchItem.Name.Contains(text, StringComparison.InvariantCultureIgnoreCase);

MODIFY
WalletWasabi.Fluent/ViewModels/SearchBar/SearchItemProvider.cs
WalletWasabi.Fluent/ViewModels/SearchBar/SearchItemProvider.cs
@@ -12,9 +12,24 @@ public static class SearchItemProvider
public static IObservable<ISearchItem> GetSearchItems()
{
return GetItemsFromMetadata()
+ .Concat(GetAdditionalItems())
.ToObservable();
}
+ private static IEnumerable<ISearchItem> GetAdditionalItems()
+ {
+ return new ISearchItem[]
+ {
+ new NonActionableSearchItem(new DarkThemeSetting(Services.UiConfig), "Dark theme", "Appearance", new List<string>(), null){ IsDefault = false},
+ new NonActionableSearchItem(new Setting<UiConfig, bool>(Services.UiConfig, b => b.Autocopy), "Autocopy", "Settings", new List<string>(), null) { IsDefault = false },
+ new NonActionableSearchItem(new Setting<UiConfig, bool>(Services.UiConfig, b => b.AutoPaste), "Autopaste", "Settings", new List<string>(), null) { IsDefault = false },
+ new NonActionableSearchItem(new Setting<UiConfig, bool>(Services.UiConfig, b => b.HideOnClose), "Hide on close", "Settings", new List<string>(), null) { IsDefault = false },
+ new NonActionableSearchItem(new Setting<UiConfig, bool>(Services.UiConfig, b => b.RunOnSystemStartup), "Run on system startup", "Settings", new List<string>(), null) { IsDefault = false },
+ new NonActionableSearchItem(new Setting<Config, bool>(Services.Config, b => b.UseTor), "User Tor", "Settings", new List<string>(), null) { IsDefault = false },
+ new NonActionableSearchItem(new Setting<Config, bool>(Services.Config, b => b.TerminateTorOnExit), "Terminate Tor on exit", "Settings", new List<string>(), null) { IsDefault = false },
+ };
+ }
+
private static IEnumerable<ActionableItem> GetItemsFromMetadata()
{
return NavigationManager.MetaData
@@ -24,7 +39,8 @@ public static class SearchItemProvider
var onActivate = CreateOnActivateFunction(m);
var searchItem = new ActionableItem(m.Title, m.Caption, onActivate, m.Category ?? "No category", m.Keywords)
{
- Icon = m.IconName
+ Icon = m.IconName,
+ IsDefault = true,
};
return searchItem;
});

ADD
WalletWasabi.Fluent/ViewModels/SearchBar/Setting.cs
+using System.Linq.Expressions;
+using System.Reactive;
+using System.Reactive.Linq;
+using ReactiveUI;
+
+namespace WalletWasabi.Fluent.ViewModels.SearchBar;
+
+public class Setting<TTarget, TProperty> : ReactiveObject
+{
+ private TProperty _value;
+
+ public Setting(TTarget target, Expression<Func<TTarget, TProperty>> selector)
+ {
+ if (selector == null)
+ {
+ throw new ArgumentNullException(nameof(selector));
+ }
+
+ if (PropertyHelper<TTarget>.GetProperty(selector) is not { } pr)
+ {
+ throw new InvalidOperationException("The expression {selector} is not a valid property selector");
+ }
+
+ Value = (TProperty)pr.GetValue(target);
+
+ SetIsActiveCommand = ReactiveCommand.Create(() => pr.SetValue(target, Value));
+
+ this.WhenAnyValue(x => x.Value)
+ .ObserveOn(RxApp.TaskpoolScheduler)
+ .Skip(1)
+ .Select(_ => Unit.Default)
+ .InvokeCommand(SetIsActiveCommand);
+ }
+
+ public TProperty Value
+ {
+ get => _value;
+ set => this.RaiseAndSetIfChanged(ref _value, value);
+ }
+
+ public ReactiveCommand<Unit, Unit> SetIsActiveCommand { get; }
+}
\ No newline at end of file

ADD
WalletWasabi.Fluent/ViewModels/SearchBar/SettingSelector.cs
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using Avalonia.Controls;
+using Avalonia.Controls.Templates;
+
+namespace WalletWasabi.Fluent.ViewModels.SearchBar;
+
+public class SettingSelector : IDataTemplate
+{
+ public List<IDataTemplate> DataTemplates { get; set; } = new();
+
+ public IControl Build(object param)
+ {
+ var prop = param.GetType().GetProperty("Value");
+ var template = DataTemplates.FirstOrDefault(d => d.Match(prop.GetValue(param)));
+ if (template is not null)
+ {
+ return template.Build(param);
+ }
+
+ return new TextBlock {Text = "Not found"};
+ }
+
+ public bool Match(object data)
+ {
+ return data.GetType().Name.Contains("Setting");
+ }
+}
\ No newline at end of file

MODIFY
WalletWasabi.Fluent/Views/SearchBar/SearchBarDropdown.axaml
WalletWasabi.Fluent/Views/SearchBar/SearchBarDropdown.axaml
<UserControl.DataTemplates>
+ <sb:SettingSelector>
+ <sb:SettingSelector.DataTemplates>
+ <DataTemplate DataType="x:Boolean">
+ <ToggleSwitch HorizontalAlignment="Right" IsChecked="{Binding Value, Mode=TwoWay}" />
+ </DataTemplate>
+ </sb:SettingSelector.DataTemplates>
+ </sb:SettingSelector>
+
<DataTemplate DataType="sb:NonActionableSearchItem">
<DockPanel Background="Transparent" Margin="10">
</DockPanel>
</DataTemplate>
- <DataTemplate DataType="sb:DarkThemeSelector">
- <ToggleSwitch HorizontalAlignment="Right" IsChecked="{Binding IsDarkThemeEnabled, Mode=TwoWay}" />
- </DataTemplate>
-
<DataTemplate DataType="sb:AutocloseActionableItem">
<Button Command="{Binding Command}" Classes="searchItem" Background="Transparent" Foreground="{DynamicResource AcrylicTrimForeground}" HorizontalAlignment="Stretch" Padding="0" Cursor="Hand">

[Preliminar] Ability to search settings