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
22 changes: 18 additions & 4 deletions src/ArcadePointsBot/App.axaml
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="AvaloniaApplication1.App"
xmlns:local="using:AvaloniaApplication1"
xmlns:oldLocal="using:AvaloniaApplication1"
xmlns:local="using:ArcadePointsBot"
xmlns:models="using:AvaloniaApplication1.Models"
RequestedThemeVariant="Dark">
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->

<Application.DataTemplates>
<local:ViewLocator/>
</Application.DataTemplates>
<Application.Resources>
<oldLocal:WorkerStatus x:Key="WorkerStatus1">Stopped</oldLocal:WorkerStatus>
<oldLocal:WorkerStatus x:Key="WorkerStatusErr">Errored</oldLocal:WorkerStatus>
<oldLocal:WorkerStatus x:Key="WorkerStatusStart">Starting</oldLocal:WorkerStatus>
<oldLocal:WorkerStatus x:Key="WorkerStatusRunning">Running</oldLocal:WorkerStatus>

<models:KeyboardActionType x:Key="ActionType1">Press</models:KeyboardActionType>
<models:ActionType x:Key="KeyboardAction">Keyboard</models:ActionType>
<models:ActionType x:Key="MouseAction">Mouse</models:ActionType>

<StreamGeometry x:Key="chevron_down_regular">M8.36612 16.1161C7.87796 16.6043 7.87796 17.3957 8.36612 17.8839L23.1161 32.6339C23.6043 33.122 24.3957 33.122 24.8839 32.6339L39.6339 17.8839C40.122 17.3957 40.122 16.6043 39.6339 16.1161C39.1457 15.628 38.3543 15.628 37.8661 16.1161L24 29.9822L10.1339 16.1161C9.64573 15.628 8.85427 15.628 8.36612 16.1161Z</StreamGeometry>
<StreamGeometry x:Key="chevron_up_regular">M39.6339 31.8839C39.1457 32.372 38.3543 32.372 37.8661 31.8839L24 18.0178L10.1339 31.8839C9.64573 32.372 8.85427 32.372 8.36612 31.8839C7.87796 31.3957 7.87796 30.6043 8.36612 30.1161L23.1161 15.3661C23.6043 14.878 24.3957 14.878 24.8839 15.3661L39.6339 30.1161C40.122 30.6043 40.122 31.3957 39.6339 31.8839Z</StreamGeometry>
<StreamGeometry x:Key="line_horizontal_1_regular">M2 9.5C2 9.22386 2.22386 9 2.5 9H17.5C17.7761 9 18 9.22386 18 9.5C18 9.77614 17.7761 10 17.5 10H2.5C2.22386 10 2 9.77614 2 9.5Z</StreamGeometry>

</Application.Resources>
<Application.Styles>
<FluentTheme>
<FluentTheme.Palettes>
Expand Down
107 changes: 89 additions & 18 deletions src/ArcadePointsBot/App.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,20 +31,11 @@
namespace AvaloniaApplication1;
public partial class App : Application
{
private readonly IServiceProvider _services;
public IHost? GlobalHost { get; private set; }


public App(IServiceProvider services)
{
_services = services;
if (!Design.IsDesignMode)
EnsureDb();
RxApp.DefaultExceptionHandler = _services.GetRequiredService<IObserver<Exception>>();
}

private void EnsureDb()
private void EnsureDb(IServiceProvider services)
{
using var scope = _services.CreateScope();
using var scope = services.CreateScope();
var loggerFactory = scope.ServiceProvider.GetRequiredService<ILoggerFactory>();
var logger = loggerFactory.CreateLogger("App");
logger.LogInformation("Checking for Db updates");
Expand All @@ -67,14 +58,13 @@ private void EnsureDb()

public override void Initialize()
{
AvaloniaXamlLoader.Load(_services, this);

Resources[typeof(IServiceProvider)] = _services;
DataTemplates.Add(_services.GetRequiredService<ViewLocator>());
AvaloniaXamlLoader.Load(this);
}

public override void OnFrameworkInitializationCompleted()
public override async void OnFrameworkInitializationCompleted()
{
GlobalHost = CreateAppBuilder().Build();
EnsureDb(GlobalHost.Services);
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
// Line below is needed to remove Avalonia data validation.
Expand All @@ -83,10 +73,91 @@ public override void OnFrameworkInitializationCompleted()

desktop.MainWindow = new MainWindow
{
DataContext = this.CreateInstance<MainWindowViewModel>(),
DataContext = GlobalHost.Services.GetRequiredService<MainWindowViewModel>(),
};
desktop.Exit += (_, _) =>
{
GlobalHost.StopAsync().GetAwaiter().GetResult();
GlobalHost.Dispose();
GlobalHost = null;
};
}

DataTemplates.Add(GlobalHost.Services.GetRequiredService<ViewLocator>());

base.OnFrameworkInitializationCompleted();

await GlobalHost.StartAsync();
}


public static IHostBuilder CreateAppBuilder() => Host
.CreateDefaultBuilder(Environment.GetCommandLineArgs())
.UseSerilog((ctx, svc, cfg) =>
{
cfg
.ReadFrom.Configuration(ctx.Configuration)
.ReadFrom.Services(svc)
.Enrich.FromLogContext();
})
.ConfigureAppConfiguration(WithApplicationConfiguration)
.ConfigureServices(WithApplicationServices);

private static void WithApplicationConfiguration(HostBuilderContext context, IConfigurationBuilder configurationBuilder)
{
if (Design.IsDesignMode)
return;
configurationBuilder.Sources.Clear();
configurationBuilder
.SetBasePath(Directory.GetCurrentDirectory())
.Add<WritableJsonConfigurationSource>(s =>
{
s.Path = "appsettings.json";
s.Optional = false;
s.ReloadOnChange = true;
s.FileProvider = null;
s.ResolveFileProvider();
})
.AddJsonFile("appsettings.Development.json", true, true);

if (context.HostingEnvironment.IsDevelopment())
{
configurationBuilder.AddUserSecrets(Assembly.GetExecutingAssembly());
}

configurationBuilder.AddEnvironmentVariables();
}
private static void WithApplicationServices(HostBuilderContext context, IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
{
var dbPath = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "ArcadePointsBot.db");
options.UseSqlite($"Data Source={dbPath}");
});

services.AddOptions<TwitchAuthConfig>().BindConfiguration("TwitchAuthConfig");

services.Configure<TwitchAuthConfig>(options =>
{
var rawConfig = context.Configuration;
options.PropertyChanged += (o, e) =>
{
if (e is not PropertyChangedEventArgsEx args) throw new InvalidOperationException();
rawConfig["TwitchAuthConfig:" + args.PropertyName!] = args.Value?.ToString();
};
});

services.AddSingleton<IObserver<Exception>, GlobalRxExceptionHandler>();
services.AddSingleton<IAuthenticationService, TwitchAuthenticationService>();
services.AddScoped<TwitchPointRewardService>();

services.AddScoped(typeof(IEntityRepository<,>), typeof(DataEntityRepository<,>));
services.AddScoped<IRewardRepository, RewardRepository>();

services.AddSingleton<TwitchWorker>();

services.AddTransient<ViewLocator>();
services.AddTransient<MainWindowViewModel>();
services.AddTransient<CreateRewardWindowViewModel>();
}
}
124 changes: 9 additions & 115 deletions src/ArcadePointsBot/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
using Microsoft.Extensions.Logging;
using Serilog;
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;

namespace AvaloniaApplication1
Expand All @@ -29,123 +31,15 @@ internal class Program
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break.
[STAThread]
public static async Task<int> Main(string[] args)
{
var host = CreateAppBuilder(args).Build();
await host.StartAsync();
ServiceProvider = host.Services;
var app = BuildAvaloniaAppWithServices(host.Services);
try
{
var res = app.StartWithClassicDesktopLifetime(args);
public static void Main(string[] args) => BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);

await host.StopAsync();

return res;
}
catch (Exception ex)
{
Log.Fatal(ex, "Something very bad happened, please send this log to the devs");
return -1;
}
//BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
}


public static IHostBuilder CreateAppBuilder(string[] args) => Host
.CreateDefaultBuilder(args)
.UseSerilog((ctx, svc, cfg) =>
{
cfg
.ReadFrom.Configuration(ctx.Configuration)
.ReadFrom.Services(svc)
.Enrich.FromLogContext();
})
.ConfigureAppConfiguration(WithApplicationConfiguration)
.ConfigureServices(WithApplicationServices);

private static void WithApplicationConfiguration(HostBuilderContext context, IConfigurationBuilder configurationBuilder)
{
if (Design.IsDesignMode)
return;
configurationBuilder.Sources.Clear();
configurationBuilder
.SetBasePath(Directory.GetCurrentDirectory())
.Add<WritableJsonConfigurationSource>(s =>
{
s.Path = "appsettings.json";
s.Optional = false;
s.ReloadOnChange = true;
s.FileProvider = null;
s.ResolveFileProvider();
})
.AddJsonFile("appsettings.Development.json", true, true);

if (context.HostingEnvironment.IsDevelopment())
{
configurationBuilder.AddUserSecrets(Assembly.GetExecutingAssembly());
}

configurationBuilder.AddEnvironmentVariables();
}
private static void WithApplicationServices(HostBuilderContext context, IServiceCollection services)
{
//if (context.HostingEnvironment.IsDevelopment())
//{
// services.AddDbContext<ApplicationDbContext>(options =>
// {
// var connString = "Server=192.168.178.28; User ID=root; Password=31401577; Database=twitchTest";
// options.UseMySql(connString, ServerVersion.AutoDetect(connString));
// });
//}
//else
//{
services.AddDbContext<ApplicationDbContext>(options =>
{
var dbPath = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "ArcadePointsBot.db");
options.UseSqlite($"Data Source={dbPath}");
});
//}

//services.Configure<TwitchAuthConfig>(section);
services.AddOptions<TwitchAuthConfig>().BindConfiguration("TwitchAuthConfig");

services.Configure<TwitchAuthConfig>(options =>
{
var rawConfig = context.Configuration;
options.PropertyChanged += (o, e) =>
{
if (e is not PropertyChangedEventArgsEx args) throw new InvalidOperationException();
rawConfig["TwitchAuthConfig:" + args.PropertyName!] = args.Value?.ToString();
};
});

services.AddSingleton<IObserver<Exception>, GlobalRxExceptionHandler>();
services.AddSingleton<ViewLocator>();
services.AddSingleton<IAuthenticationService, TwitchAuthenticationService>();
services.AddScoped<TwitchPointRewardService>();

services.AddScoped(typeof(IEntityRepository<,>), typeof(DataEntityRepository<,>));
services.AddScoped<IRewardRepository, RewardRepository>();

services.AddSingleton<TwitchWorker>();

services.AddScoped<CreateRewardWindowViewModel>();
services.AddScoped<MainWindowViewModel>();
}

public static AppBuilder BuildAvaloniaAppWithServices(IServiceProvider services) => AppBuilder
.Configure(() => new App(services))
.UsePlatformDetect()
.UseReactiveUI()
.WithInterFont()
.LogToTrace();
// Avalonia configuration, don't remove; also used by visual designer.
public static AppBuilder BuildAvaloniaApp()
{
var host = Host.CreateDefaultBuilder().ConfigureServices(WithApplicationServices).Build();
Program.ServiceProvider = host.Services;
return BuildAvaloniaAppWithServices(host.Services);
}
=> AppBuilder.Configure<App>()
.UsePlatformDetect()
.WithInterFont()
.LogToTrace()
.UseReactiveUI();
}
}
21 changes: 0 additions & 21 deletions src/ArcadePointsBot/Util/DesignTimeServices.cs

This file was deleted.

40 changes: 17 additions & 23 deletions src/ArcadePointsBot/ViewLocator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,32 @@
using AvaloniaApplication1.ViewModels;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;

namespace AvaloniaApplication1
{
public class ViewLocator : IDataTemplate
public static class ViewLocatorHelpers
{
private readonly IServiceProvider _serviceProvider;

public ViewLocator(IServiceProvider serviceProvider)
public static IServiceCollection AddView<TViewModel, TView>(this IServiceCollection services)
where TView : Control, new()
where TViewModel : ViewModelBase
{
_serviceProvider = serviceProvider;
services.AddSingleton(new ViewLocator.ViewLocationDescriptor(typeof(TViewModel), () => new TView()));
return services;
}
}
public class ViewLocator : IDataTemplate
{
private readonly Dictionary<Type, Func<Control>> _views;

public Control? Build(object? data)
public ViewLocator(IEnumerable<ViewLocationDescriptor> descriptors)
{
if(data is null)
return new TextBlock { Text = "Not Found" };

var name = data.GetType().FullName!.Replace("ViewModel", "View");
var type = Type.GetType(name);

if (type != null)
{
var scope = _serviceProvider.CreateScope();
return (Control)scope.ServiceProvider.GetRequiredService(type)!;
}

return new TextBlock { Text = "Not Found: " + name };
_views = descriptors.ToDictionary(x => x.ViewModel, x => x.Factory);
}

public bool Match(object? data)
{
return data is ViewModelBase;
}
public Control Build(object? param) => _views[param!.GetType()]();
public bool Match(object? param) => param is not null && _views.ContainsKey(param.GetType());
public record ViewLocationDescriptor(Type ViewModel, Func<Control> Factory);
}
}
Loading