Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/Angor/Avalonia/AngorApp/App.axaml.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using AngorApp.Composition;
using AngorApp.Core;
using AngorApp.Sections.Shell;
using Avalonia;
Expand All @@ -17,15 +18,15 @@ public override void Initialize()
.WriteTo.Console()
.MinimumLevel.Debug()
.CreateLogger();

AvaloniaXamlLoader.Load(this);
}

public override void OnFrameworkInitializationCompleted()
{
IconProvider.Current
.Register<FontAwesomeIconProvider>();

this.Connect(() => new MainView(), CompositionRoot.CreateMainViewModel, () => new MainWindow());

base.OnFrameworkInitializationCompleted();
Expand Down
20 changes: 20 additions & 0 deletions src/Angor/Avalonia/AngorApp/Composition/CompositionRoot.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using AngorApp.Sections.Shell;
using Microsoft.Extensions.DependencyInjection;

namespace AngorApp.Composition;

public static class CompositionRoot
{
public static IMainViewModel CreateMainViewModel(Control topLevelView)
{
var services = new ServiceCollection();

services
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will also at some point have to add all the angor services, it is a good idea to do it this way, we can also add later a method .AddAngorServices()

.AddUIServices(topLevelView)
.AddUIModelServices()
.AddViewModels();

var serviceProvider = services.BuildServiceProvider();
return serviceProvider.GetRequiredService<IMainViewModel>();
}
}
9 changes: 9 additions & 0 deletions src/Angor/Avalonia/AngorApp/Composition/ISectionsFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using AngorApp.Sections.Shell;
using AngorApp.Sections.Shell.Sections;

namespace AngorApp.Composition;

public interface ISectionsFactory
{
IEnumerable<SectionBase> CreateSections();
}
40 changes: 40 additions & 0 deletions src/Angor/Avalonia/AngorApp/Composition/SectionsFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using AngorApp.Core;
using AngorApp.Sections;
using AngorApp.Sections.Founder;
using AngorApp.Sections.Home;
using AngorApp.Sections.Portfolio;
using AngorApp.Sections.Shell;
using AngorApp.Sections.Shell.Sections;
using AngorApp.Sections.Wallet;
using AngorApp.UI.Services;
using Microsoft.Extensions.DependencyInjection;
using Separator = AngorApp.Sections.Shell.Sections.Separator;

namespace AngorApp.Composition;

public class SectionsFactory(IServiceProvider serviceProvider) : ISectionsFactory
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what are sections exactly? is this like sections of the UI?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are the sections that appear in the Sidebar. Their have a ViewModel as content, and they are lazily loaded as needed.

public IEnumerable<SectionBase> CreateSections()
{
return new SectionBase[]
{
Section.Create("Home", Get<IHomeSectionViewModel>(), "svg:/Assets/angor-icon.svg"),
new Separator(),
Section.Create("Wallet", Get<IWalletSectionViewModel>(), "fa-wallet"),
Section.Create("Browse", Get<NavigationViewModel>(), "fa-magnifying-glass"),
Section.Create("Portfolio", Get<IPortfolioSectionViewModel>(), "fa-hand-holding-dollar"),
Section.Create("Founder", Get<IFounderSectionViewModel>(), "fa-money-bills"),
new Separator(),
Section.Create("Settings", () => new object(), "fa-gear"),
new CommandSection("Angor Hub",
ReactiveCommand.CreateFromTask(() =>
Get<UIServices>()().LauncherService.LaunchUri(Constants.AngorHubUri)),
"fa-magnifying-glass") { IsPrimary = false }
};

Func<T> Get<T>() where T : notnull
{
return serviceProvider.GetRequiredService<T>;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
using Angor.UI.Model;
using Angor.UI.Model.Implementation;
using Angor.UI.Model.Implementation.Projects;
using AngorApp.Core;
using AngorApp.Design;
using AngorApp.Sections;
using AngorApp.Sections.Browse;
using AngorApp.Sections.Founder;
using AngorApp.Sections.Home;
using AngorApp.Sections.Portfolio;
using AngorApp.Sections.Shell;
using AngorApp.Sections.Wallet;
using AngorApp.UI.Services;
using Avalonia.Controls.Notifications;
using Microsoft.Extensions.DependencyInjection;
using Zafiro.Avalonia.Controls.Navigation;
using Zafiro.Avalonia.Dialogs;
using Zafiro.Avalonia.Services;
using Zafiro.UI;

namespace AngorApp.Composition;

public static class ServiceCollectionExtensions
{
public static IServiceCollection AddUIServices(this IServiceCollection services, Control parent)
{
var topLevel = TopLevel.GetTopLevel(parent);

return services
.AddSingleton<ILauncherService>(_ => new LauncherService(topLevel!.Launcher))
.AddSingleton<IDialog, DesktopDialog>()
.AddSingleton<IActiveWallet, ActiveWallet>()
.AddSingleton<INotificationService>(_ => new NotificationService(
new WindowNotificationManager(topLevel)
{
Position = NotificationPosition.BottomRight
}
))
.AddSingleton<UIServices>(sp => new UIServices(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't all those services just get resolved automatically? why this hack?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're absolutely right. Leftover from previous code... Will fix!

sp.GetRequiredService<ILauncherService>(),
sp.GetRequiredService<IDialog>(),
sp.GetRequiredService<INotificationService>(),
sp.GetRequiredService<IActiveWallet>()
));
}

public static IServiceCollection AddUIModelServices(this IServiceCollection services)
{
return services
.AddSingleton<IWalletProvider, WalletProviderDesign>()
.AddSingleton<IWalletBuilder, WalletBuilderDesign>()
.AddSingleton<IWalletFactory, WalletFactory>()
.AddSingleton<IProjectService>(sp =>
{
var loggerFactory = LoggerConfig.CreateFactory();
return new ProjectService(
DependencyFactory.GetIndexerService(loggerFactory),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

normally we do not pass the logger factory to services do we?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DependencyFactory.GetRelayService(loggerFactory)
);
});
}

private delegate IBrowseSectionViewModel BrowseSectionViewModelFactory(INavigator navigator);

public static IServiceCollection AddViewModels(this IServiceCollection services)
{
return services
.AddTransient<BrowseSectionViewModelFactory>(sp =>
navigator => ActivatorUtilities.CreateInstance<BrowseSectionViewModel>(sp, navigator))
.AddTransient<NavigationViewModel>(sp =>
new NavigationViewModel(navigator =>
sp.GetRequiredService<BrowseSectionViewModelFactory>()(navigator)
))
.AddSingleton<ISectionsFactory, SectionsFactory>()
.AddSingleton<Lazy<IMainViewModel>>(sp => new Lazy<IMainViewModel>(sp.GetRequiredService<IMainViewModel>))
.AddTransient<IHomeSectionViewModel>(sp =>
new HomeSectionViewModel(
sp.GetRequiredService<IActiveWallet>(),
sp.GetRequiredService<UIServices>(),
() => sp.GetRequiredService<Lazy<IMainViewModel>>().Value
))
.AddTransient<IWalletSectionViewModel, WalletSectionViewModel>()

// // This registration could be maintained for alternative uses, but for navigation we will use the delegate
.AddTransient<IBrowseSectionViewModel, BrowseSectionViewModel>()
.AddTransient<IPortfolioSectionViewModel, PortfolioSectionViewModel>()
.AddTransient<IFounderSectionViewModel, FounderSectionViewModel>()
.AddSingleton<IMainViewModel>(sp =>
new MainViewModel(
sp.GetRequiredService<ISectionsFactory>().CreateSections(),
sp.GetRequiredService<UIServices>()
));
}
}
70 changes: 0 additions & 70 deletions src/Angor/Avalonia/AngorApp/Core/CompositionRoot.cs

This file was deleted.

3 changes: 0 additions & 3 deletions src/Angor/Avalonia/AngorApp/Core/LoggerConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ public class LoggerConfig
{
public static ILoggerFactory CreateFactory()
{
// Configurar Serilog
var logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
Expand All @@ -17,10 +16,8 @@ public static ILoggerFactory CreateFactory()
outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}")
.CreateLogger();

// Crear el factory
var factory = new LoggerFactory();

// Agregar Serilog al factory
factory.AddSerilog(logger);

return factory;
Expand Down
27 changes: 0 additions & 27 deletions src/Angor/Avalonia/AngorApp/Core/ViewLocator.cs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.Reactive.Linq;
using Angor.UI.Model;
using AngorApp.Sections.Browse.ProjectLookup;
using AngorApp.Services;
using AngorApp.UI.Services;
using CSharpFunctionalExtensions;
using ReactiveUI.SourceGenerators;
using Zafiro.Avalonia.Controls.Navigation;
Expand All @@ -16,16 +16,18 @@ public partial class BrowseSectionViewModel : ReactiveObject, IBrowseSectionView

[ObservableAsProperty] private IList<IProjectViewModel>? projects;

public BrowseSectionViewModel(IWalletProvider walletProvider, IProjectService projectService, INavigator navigator, UIServices uiServices)
public BrowseSectionViewModel(IWalletProvider walletProvider, IProjectService projectService, INavigator navigator,
UIServices uiServices)
{
ProjectLookupViewModel = new ProjectLookupViewModel(projectService, walletProvider, navigator, uiServices);

LoadLatestProjects = ReactiveCommand.CreateFromObservable(() => Observable.FromAsync(projectService.Latest)
.Flatten()
.Select(IProjectViewModel (project) => new ProjectViewModel(walletProvider, project, navigator, uiServices))
.ToList());

OpenHub = ReactiveCommand.CreateFromTask(() => uiServices.LauncherService.LaunchUri(new Uri("https://www.angor.io")));
OpenHub = ReactiveCommand.CreateFromTask(() =>
uiServices.LauncherService.LaunchUri(new Uri("https://www.angor.io")));
projectsHelper = LoadLatestProjects.ToProperty(this, x => x.Projects);
LoadLatestProjects.Execute().Subscribe();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using AngorApp.Sections.Browse.ProjectLookup;
using AngorApp.Sections.Shell;
using AngorApp.Sections.Wallet;
using AngorApp.Services;
using CSharpFunctionalExtensions;

namespace AngorApp.Sections.Browse;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,26 @@
using System.Windows.Input;
using Angor.UI.Model;
using AngorApp.Sections.Browse.Details.Invest.Amount;
using AngorApp.Services;
using AngorApp.UI.Controls.Common.Success;
using AngorApp.UI.Controls.Common.TransactionPreview;
using AngorApp.UI.Services;
using CSharpFunctionalExtensions;
using Zafiro.Avalonia.Controls.Wizards.Builder;
using Zafiro.Avalonia.Dialogs;

namespace AngorApp.Sections.Browse.Details;

public class ProjectDetailsViewModel(IWalletProvider walletProvider, IProject project, UIServices uiServices) : ReactiveObject, IProjectDetailsViewModel
public class ProjectDetailsViewModel(IWalletProvider walletProvider, IProject project, UIServices uiServices)
: ReactiveObject, IProjectDetailsViewModel
{
public object Icon => project.Icon;
public object Picture => project.Picture;

public ICommand Invest { get; } = ReactiveCommand.CreateFromTask(() =>
{
var maybeWallet = walletProvider.GetWallet();
return maybeWallet.Match(wallet => DoInvest(wallet, project, uiServices), () => uiServices.NotificationService.Show("You need to create a Wallet before investing", "No wallet"));
return maybeWallet.Match(wallet => DoInvest(wallet, project, uiServices),
() => uiServices.NotificationService.Show("You need to create a Wallet before investing", "No wallet"));
});

public IEnumerable<INostrRelay> Relays { get; } =
Expand Down
Loading