search entries and categories are registered.
MODIFY
WalletWasabi.Fluent/ViewModels/NavBar/NavBarViewModel.cs
WalletWasabi.Fluent/ViewModels/NavBar/NavBarViewModel.cs
@@ -39,6 +39,10 @@ namespace WalletWasabi.Fluent.ViewModels.NavBar
var settingsPage = new SettingsPageViewModel(navigationState);
var searchPage = new SearchPageViewModel(navigationState, walletManager, addWalletPage, settingsPage, homePage);
+ // Register categories and entries here.
+
+ searchPage.Initialise();
+
_selectedItem = homePage;
_topItems.Add(SelectedItem);

MODIFY
WalletWasabi.Fluent/ViewModels/Search/SearchPageViewModel.cs
WalletWasabi.Fluent/ViewModels/Search/SearchPageViewModel.cs
@@ -16,44 +16,64 @@ namespace WalletWasabi.Fluent.ViewModels.Search
{
public class SearchPageViewModel : NavBarItemViewModel
{
- private readonly ReadOnlyObservableCollection<SearchResult> _searchResults;
+ private ReadOnlyObservableCollection<SearchResult> _searchResults;
+ private readonly Dictionary<string, SearchCategory> _categories;
+ private readonly Dictionary<SearchCategory, SourceList<SearchItemViewModel>> _categorySources;
+ private IObservable<IChangeSet<SearchItemViewModel>> _sourceObservable;
private string? _searchQuery;
- private readonly bool _showSettings;
- private readonly bool _showWallets;
+ private readonly bool _showWallets = false;
+ private readonly WalletManagerViewModel _walletManager;
public SearchPageViewModel(NavigationStateViewModel navigationState, WalletManagerViewModel walletManager, AddWalletPageViewModel addWalletPage, SettingsPageViewModel settingsPage, HomePageViewModel homePage) : base(navigationState)
{
Title = "Search";
+ _categories = new Dictionary<string, SearchCategory>();
+ _categorySources = new Dictionary<SearchCategory, SourceList<SearchItemViewModel>>();
+ _walletManager = walletManager;
+
+ RegisterCategory("General", 0);
+
+ RegisterSearchEntry(
+ "Home",
+ "Manage existing wallets",
+ 0,
+ "General",
+ "Home",
+ "home_regular",
+ () => homePage);
+
+ RegisterSearchEntry(
+ title: "Settings",
+ caption: "Manage appearance, privacy and other settings",
+ order: 1,
+ category: "General",
+ keywords: "Settings, General, User Interface, Privacy, Advanced",
+ iconName: "settings_regular",
+ createTargetView: () => settingsPage);
- _showSettings = true;
- _showWallets = false;
-
- var generalCategory = new SearchCategory("General", 0);
- var generalCategorySource = new SourceList<SearchItemViewModel>();
- generalCategorySource.Add(CreateHomeSearchItem(generalCategory, 0, homePage));
- generalCategorySource.Add(CreateSettingsSearchItem(generalCategory, 1, settingsPage));
- generalCategorySource.Add(CreateAddWalletSearchItem(generalCategory, 2, addWalletPage));
-
- var settingsCategory = new SearchCategory("Settings", 1);
- var settingsCategorySource = new SourceList<SearchItemViewModel>();
- settingsCategorySource.AddRange(CreateSettingsSearchItems(settingsCategory, settingsPage));
-
- var walletCategory = new SearchCategory("Wallets", 2);
- var wallets = walletManager.Items
- .ToObservableChangeSet()
- .Transform(x => CreateWalletSearchItem(walletCategory, 0, x))
- .Sort(SortExpressionComparer<SearchItemViewModel>.Ascending(i => i.Title));
+ RegisterSearchEntry(title: "Add Wallet",
+ caption: "Create, recover or import wallet",
+ order: 2,
+ category: "General",
+ keywords: "Wallet, Add Wallet, Create Wallet, Recover Wallet, Import Wallet, Connect Hardware Wallet",
+ iconName: "add_circle_regular",
+ createTargetView: () => addWalletPage);
- var searchItems = generalCategorySource.Connect();
+ RegisterCategory("Settings", 1);
+ RegisterSettingsSearchItems(settingsPage);
- if (_showSettings)
- {
- searchItems = searchItems.Merge(settingsCategorySource.Connect());
+ RegisterCategory("Wallets", 2);
}
+ public void Initialise()
+ {
if (_showWallets)
{
- searchItems = searchItems.Merge(wallets);
+ _walletManager.Items
+ .ToObservableChangeSet()
+ .OnItemAdded(x => RegisterWalletSearchItem(0, x))
+ .OnItemRemoved(x => { })
+ .Subscribe();
}
var queryFilter = this.WhenValueChanged(t => t.SearchQuery)
@@ -61,7 +81,7 @@ namespace WalletWasabi.Fluent.ViewModels.Search
.Select(SearchQueryFilter)
.DistinctUntilChanged();
- searchItems
+ _sourceObservable
.Filter(queryFilter)
.GroupWithImmutableState(x => x.Category)
.Transform(grouping => new SearchResult(grouping.Key, grouping.Items.OrderBy(x => x.Order).ThenBy(x => x.Title)))
@@ -71,6 +91,63 @@ namespace WalletWasabi.Fluent.ViewModels.Search
.AsObservableList();
}
+ public SearchCategory RegisterCategory(string title, int order)
+ {
+ if (!_categories.ContainsKey(title))
+ {
+ var category = new SearchCategory(title, order);
+
+ _categories.Add(title, category);
+
+ var sourceList = new SourceList<SearchItemViewModel>();
+
+ _categorySources.Add(category, sourceList);
+
+ if (_sourceObservable is null)
+ {
+ _sourceObservable = sourceList.Connect();
+ }
+ else
+ {
+ _sourceObservable = _sourceObservable.Merge(sourceList.Connect());
+ }
+
+ return category;
+ }
+
+ throw new Exception("Category already exists.");
+ }
+
+
+ public SearchItemViewModel RegisterSearchEntry(
+ string title,
+ string caption,
+ int order,
+ string category,
+ string keywords,
+ string iconName,
+ Func<RoutableViewModel> createTargetView)
+ {
+ if (_categories.TryGetValue(category, out var searchCategory))
+ {
+ var result = new SearchItemViewModel(
+ title,
+ caption,
+ order,
+ searchCategory,
+ keywords,
+ iconName,
+ NavigationState,
+ createTargetView);
+
+ _categorySources[searchCategory].Add(result);
+
+ return result;
+ }
+
+ throw new Exception("Category doesnt exist.");
+ }
+
public override string IconName => "search_regular";
public string? SearchQuery
@@ -95,84 +172,54 @@ namespace WalletWasabi.Fluent.ViewModels.Search
};
}
- private SearchItemViewModel CreateHomeSearchItem(SearchCategory category, int order, HomePageViewModel homePage)
- {
- return new(
- title: "Home",
- caption: "Manage existing wallets",
- order: order,
- category: category,
- keywords: "Home",
- iconName: "home_regular",
- navigationState: NavigationState,
- createTargetView: () => homePage);
- }
-
- private SearchItemViewModel CreateSettingsSearchItem(SearchCategory category, int order, SettingsPageViewModel settingsPage)
+ private void RegisterSettingsSearchItems(SettingsPageViewModel settingsPage)
{
- return new(
- title: "Settings",
- caption: "Manage appearance, privacy and other settings",
- order: order,
- category: category,
- keywords: "Settings, General, User Interface, Privacy, Advanced",
- iconName: "settings_regular",
- navigationState: NavigationState,
- createTargetView: () => settingsPage);
- }
-
- private IEnumerable<SearchItemViewModel> CreateSettingsSearchItems(SearchCategory category, SettingsPageViewModel settingsPage)
- {
- yield return new (
+ RegisterSearchEntry(
title: "General",
caption: "Manage general settings",
order: 0,
- category: category,
+ category: "Settings",
keywords: "Settings, General, Dark Mode, Bitcoin Addresses, Manual Entry Free, Custom Change Address, Fee Display Format, Dust Threshold, BTC",
iconName: "settings_general_regular",
- navigationState: NavigationState,
createTargetView: () =>
{
settingsPage.SelectedTab = 0;
return settingsPage;
});
- yield return new(
+ RegisterSearchEntry(
title: "Privacy",
caption: "Manage privacy settings",
order: 1,
- category: category,
+ category: "Settings",
keywords: "Settings, Privacy, Minimal, Medium, Strong, Anonymity Level",
iconName: "settings_privacy_regular",
- navigationState: NavigationState,
createTargetView: () =>
{
settingsPage.SelectedTab = 1;
return settingsPage;
});
- yield return new(
+ RegisterSearchEntry(
title: "Network",
caption: "Manage network settings",
order: 2,
- category: category,
+ category: "Settings",
keywords: "Settings, Network, Encryption, Tor, Terminate, Wasabi, Shutdown, SOCKS5, Endpoint",
iconName: "settings_network_regular",
- navigationState: NavigationState,
createTargetView: () =>
{
settingsPage.SelectedTab = 2;
return settingsPage;
});
- yield return new(
+ RegisterSearchEntry(
title: "Bitcoin",
caption: "Manage Bitcoin settings",
order: 3,
- category: category,
+ category: "Settings",
keywords: "Settings, Bitcoin, Network, Main, TestNet, RegTest, Run, Knots, Startup, P2P, Endpoint",
iconName: "settings_bitcoin_regular",
- navigationState: NavigationState,
createTargetView: () =>
{
settingsPage.SelectedTab = 3;
@@ -180,29 +227,15 @@ namespace WalletWasabi.Fluent.ViewModels.Search
});
}
- private SearchItemViewModel CreateAddWalletSearchItem(SearchCategory category, int order, AddWalletPageViewModel addWalletPage)
- {
- return new(
- title: "Add Wallet",
- caption: "Create, recover or import wallet",
- order: order,
- category: category,
- keywords: "Wallet, Add Wallet, Create Wallet, Recover Wallet, Import Wallet, Connect Hardware Wallet",
- iconName: "add_circle_regular",
- navigationState: NavigationState,
- createTargetView: () => addWalletPage);
- }
-
- private SearchItemViewModel CreateWalletSearchItem(SearchCategory category, int order, WalletViewModelBase wallet)
+ private void RegisterWalletSearchItem(int order, WalletViewModelBase wallet)
{
- return new(
+ RegisterSearchEntry(
title: wallet.WalletName,
caption: "",
order: order,
- category: category,
+ category: "Wallets",
keywords: $"Wallet, {wallet.WalletName}",
iconName: "web_asset_regular",
- navigationState: NavigationState,
createTargetView: () => wallet);
}
}

search entries and categories are registered.