Merge pull request #3 from jahnspohrer/dispatcher-service
idea dispatcher service
This commit is contained in:
commit
35818c7915
46
App.axaml.cs
46
App.axaml.cs
@ -1,8 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls.ApplicationLifetimes;
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
using Avalonia.Data.Core.Plugins;
|
using Avalonia.Data.Core.Plugins;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Avalonia.Threading;
|
||||||
using BetterRaid.Extensions;
|
using BetterRaid.Extensions;
|
||||||
using BetterRaid.Services;
|
using BetterRaid.Services;
|
||||||
using BetterRaid.ViewModels;
|
using BetterRaid.ViewModels;
|
||||||
@ -13,45 +15,51 @@ namespace BetterRaid;
|
|||||||
|
|
||||||
public class App : Application
|
public class App : Application
|
||||||
{
|
{
|
||||||
private static readonly ServiceCollection Services = [];
|
private ServiceProvider? _serviceProvider;
|
||||||
private static ServiceProvider? _serviceProvider;
|
|
||||||
|
|
||||||
public static IServiceProvider? ServiceProvider => _serviceProvider;
|
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
InitializeServices();
|
_serviceProvider = InitializeServices();
|
||||||
|
|
||||||
AvaloniaXamlLoader.Load(_serviceProvider, this);
|
AvaloniaXamlLoader.Load(_serviceProvider, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeServices()
|
private ServiceProvider InitializeServices()
|
||||||
{
|
{
|
||||||
|
var Services = new ServiceCollection();
|
||||||
Services.AddSingleton<ITwitchService, TwitchService>();
|
Services.AddSingleton<ITwitchService, TwitchService>();
|
||||||
Services.AddTransient<MainWindowViewModel>();
|
Services.AddSingleton<ISynchronizaionService, DispatcherService>(serviceProvider => new DispatcherService(Dispatcher.UIThread));
|
||||||
|
Services.AddTransient<IMainViewModelFactory, MainWindowViewModelFactory>();
|
||||||
_serviceProvider = Services.BuildServiceProvider();
|
|
||||||
|
return Services.BuildServiceProvider();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnFrameworkInitializationCompleted()
|
public override void OnFrameworkInitializationCompleted()
|
||||||
{
|
{
|
||||||
BindingPlugins.DataValidators.RemoveAt(0);
|
BindingPlugins.DataValidators.RemoveAt(0);
|
||||||
|
|
||||||
|
if(_serviceProvider == null)
|
||||||
|
{
|
||||||
|
throw new FieldAccessException($"\"{nameof(_serviceProvider)}\" was null");
|
||||||
|
}
|
||||||
|
|
||||||
|
var viewModelFactory = _serviceProvider.GetRequiredService<IMainViewModelFactory>();
|
||||||
|
var mainWindowViewModel = viewModelFactory.CreateMainWindowViewModel();
|
||||||
|
var mainWindow = new MainWindow()
|
||||||
|
{
|
||||||
|
DataContext = mainWindowViewModel
|
||||||
|
};
|
||||||
|
|
||||||
switch (ApplicationLifetime)
|
switch (ApplicationLifetime)
|
||||||
{
|
{
|
||||||
case IClassicDesktopStyleApplicationLifetime desktop:
|
case IClassicDesktopStyleApplicationLifetime desktop:
|
||||||
desktop.MainWindow = new MainWindow();
|
desktop.MainWindow = mainWindow;
|
||||||
desktop.MainWindow.InjectDataContext<MainWindowViewModel>();
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ISingleViewApplicationLifetime singleViewPlatform:
|
case ISingleViewApplicationLifetime singleViewPlatform:
|
||||||
singleViewPlatform.MainView = new MainWindow();
|
singleViewPlatform.MainView = mainWindow;
|
||||||
singleViewPlatform.MainView.InjectDataContext<MainWindowViewModel>();
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
base.OnFrameworkInitializationCompleted();
|
base.OnFrameworkInitializationCompleted();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,21 +0,0 @@
|
|||||||
using Avalonia;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
|
|
||||||
namespace BetterRaid.Extensions;
|
|
||||||
|
|
||||||
public static class DataContextExtensions
|
|
||||||
{
|
|
||||||
public static T? GetDataContextAs<T>(this T obj) where T : StyledElement
|
|
||||||
{
|
|
||||||
return obj.DataContext as T;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void InjectDataContext<T>(this StyledElement e) where T : class
|
|
||||||
{
|
|
||||||
if (App.ServiceProvider == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var vm = App.ServiceProvider.GetRequiredService<T>();
|
|
||||||
e.DataContext = vm;
|
|
||||||
}
|
|
||||||
}
|
|
@ -15,7 +15,7 @@ public static class Tools
|
|||||||
private static HttpListener? _oauthListener;
|
private static HttpListener? _oauthListener;
|
||||||
|
|
||||||
// Source: https://stackoverflow.com/a/43232486
|
// Source: https://stackoverflow.com/a/43232486
|
||||||
public static void StartOAuthLogin(string url, Action? callback = null, CancellationToken token = default)
|
public static void StartOAuthLogin(ITwitchService twitchService, Action? callback = null, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
if (_oauthListener == null)
|
if (_oauthListener == null)
|
||||||
{
|
{
|
||||||
@ -23,13 +23,13 @@ public static class Tools
|
|||||||
_oauthListener.Prefixes.Add(Constants.TwitchOAuthRedirectUrl + "/");
|
_oauthListener.Prefixes.Add(Constants.TwitchOAuthRedirectUrl + "/");
|
||||||
_oauthListener.Start();
|
_oauthListener.Start();
|
||||||
|
|
||||||
Task.Run(() => WaitForCallback(callback, token), token);
|
Task.Run(() => WaitForCallback(callback, token, twitchService), token);
|
||||||
}
|
}
|
||||||
|
|
||||||
OpenUrl(url);
|
OpenUrl(twitchService.GetOAuthUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task WaitForCallback(Action? callback, CancellationToken token)
|
private static async Task WaitForCallback(Action? callback, CancellationToken token, ITwitchService twitchService)
|
||||||
{
|
{
|
||||||
if (_oauthListener == null)
|
if (_oauthListener == null)
|
||||||
return;
|
return;
|
||||||
@ -107,11 +107,7 @@ public static class Tools
|
|||||||
|
|
||||||
var accessToken = jsonData["access_token"]?.ToString();
|
var accessToken = jsonData["access_token"]?.ToString();
|
||||||
|
|
||||||
var dataService = App.ServiceProvider?.GetService(typeof(ITwitchService));
|
twitchService.ConnectApiAsync(Constants.TwitchClientId, accessToken!);
|
||||||
if (dataService is ITwitchService twitchDataService)
|
|
||||||
{
|
|
||||||
twitchDataService.ConnectApiAsync(Constants.TwitchClientId, accessToken!);
|
|
||||||
}
|
|
||||||
|
|
||||||
res.StatusCode = 200;
|
res.StatusCode = 200;
|
||||||
res.Close();
|
res.Close();
|
||||||
|
8
Services/IMainViewModelFactory.cs
Normal file
8
Services/IMainViewModelFactory.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
using BetterRaid.ViewModels;
|
||||||
|
|
||||||
|
namespace BetterRaid.Services;
|
||||||
|
|
||||||
|
public interface IMainViewModelFactory
|
||||||
|
{
|
||||||
|
MainWindowViewModel CreateMainWindowViewModel();
|
||||||
|
}
|
8
Services/ISynchronizaionService.cs
Normal file
8
Services/ISynchronizaionService.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace BetterRaid.Services;
|
||||||
|
|
||||||
|
public interface ISynchronizaionService
|
||||||
|
{
|
||||||
|
void Invoke(Action action);
|
||||||
|
}
|
18
Services/Implementations/DispatcherService.cs
Normal file
18
Services/Implementations/DispatcherService.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
using Avalonia.Threading;
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace BetterRaid.Services.Implementations;
|
||||||
|
public class DispatcherService : ISynchronizaionService
|
||||||
|
{
|
||||||
|
private readonly Dispatcher dispatcher;
|
||||||
|
|
||||||
|
public DispatcherService(Dispatcher dispatcher)
|
||||||
|
{
|
||||||
|
this.dispatcher = dispatcher;
|
||||||
|
}
|
||||||
|
public void Invoke(Action action)
|
||||||
|
{
|
||||||
|
dispatcher.Invoke(action);
|
||||||
|
}
|
||||||
|
}
|
22
Services/Implementations/MainWindowViewModelFactory.cs
Normal file
22
Services/Implementations/MainWindowViewModelFactory.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
using BetterRaid.ViewModels;
|
||||||
|
|
||||||
|
namespace BetterRaid.Services.Implementations;
|
||||||
|
|
||||||
|
public class MainWindowViewModelFactory : IMainViewModelFactory
|
||||||
|
{
|
||||||
|
private readonly ITwitchPubSubService twitchPubSubService;
|
||||||
|
private readonly ITwitchDataService twitchDataService;
|
||||||
|
private readonly ISynchronizaionService synchronizaionService;
|
||||||
|
|
||||||
|
public MainWindowViewModelFactory(ITwitchPubSubService twitchPubSubService, ITwitchDataService twitchDataService, ISynchronizaionService synchronizaionService)
|
||||||
|
{
|
||||||
|
this.twitchPubSubService = twitchPubSubService;
|
||||||
|
this.twitchDataService = twitchDataService;
|
||||||
|
this.synchronizaionService = synchronizaionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MainWindowViewModel CreateMainWindowViewModel()
|
||||||
|
{
|
||||||
|
return new MainWindowViewModel(twitchPubSubService, twitchDataService, synchronizaionService);
|
||||||
|
}
|
||||||
|
}
|
@ -18,6 +18,7 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
private string? _filter;
|
private string? _filter;
|
||||||
private ObservableCollection<TwitchChannel> _channels = [];
|
private ObservableCollection<TwitchChannel> _channels = [];
|
||||||
private readonly BetterRaidDatabase? _db;
|
private readonly BetterRaidDatabase? _db;
|
||||||
|
private readonly ISynchronizaionService _synchronizaionService;
|
||||||
|
|
||||||
public BetterRaidDatabase? Database
|
public BetterRaidDatabase? Database
|
||||||
{
|
{
|
||||||
@ -36,7 +37,7 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
get => _channels;
|
get => _channels;
|
||||||
set => SetProperty(ref _channels, value);
|
set => SetProperty(ref _channels, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObservableCollection<TwitchChannel> FilteredChannels => GetFilteredChannels();
|
public ObservableCollection<TwitchChannel> FilteredChannels => GetFilteredChannels();
|
||||||
|
|
||||||
public ITwitchService Twitch { get; }
|
public ITwitchService Twitch { get; }
|
||||||
@ -49,8 +50,10 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
|
|
||||||
public bool IsLoggedIn => Twitch.UserChannel != null;
|
public bool IsLoggedIn => Twitch.UserChannel != null;
|
||||||
|
|
||||||
public MainWindowViewModel(ITwitchService twitch)
|
public MainWindowViewModel(ITwitchService twitch, ISynchronizaionService synchronizaionService)
|
||||||
{
|
{
|
||||||
|
_synchronizaionService = synchronizaionService;
|
||||||
|
|
||||||
Twitch = twitch;
|
Twitch = twitch;
|
||||||
Twitch.PropertyChanged += OnTwitchPropertyChanged;
|
Twitch.PropertyChanged += OnTwitchPropertyChanged;
|
||||||
|
|
||||||
@ -73,7 +76,7 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
|
|
||||||
public void LoginWithTwitch()
|
public void LoginWithTwitch()
|
||||||
{
|
{
|
||||||
Tools.StartOAuthLogin(Twitch.GetOAuthUrl(), OnTwitchLoginCallback, CancellationToken.None);
|
Tools.StartOAuthLogin(Twitch, OnTwitchLoginCallback, CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnTwitchLoginCallback()
|
private void OnTwitchLoginCallback()
|
||||||
@ -87,18 +90,18 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var channel in Channels)
|
foreach (var channel in Channels)
|
||||||
{
|
{
|
||||||
Twitch.UnregisterFromEvents(channel);
|
Twitch.UnregisterFromEvents(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
Channels.Clear();
|
Channels.Clear();
|
||||||
|
|
||||||
var channels = _db.Channels
|
var channels = _db.Channels
|
||||||
.Select(channelName => new TwitchChannel(channelName))
|
.Select(channelName => new TwitchChannel(channelName))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
foreach (var channel in channels)
|
foreach (var channel in channels)
|
||||||
{
|
{
|
||||||
Task.Run(() =>
|
Task.Run(() =>
|
||||||
@ -106,7 +109,7 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
channel.UpdateChannelData(Twitch);
|
channel.UpdateChannelData(Twitch);
|
||||||
Twitch.RegisterForEvents(channel);
|
Twitch.RegisterForEvents(channel);
|
||||||
});
|
});
|
||||||
|
|
||||||
Channels.Add(channel);
|
Channels.Add(channel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -117,7 +120,7 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
.Where(channel => Database?.OnlyOnline == false || channel.IsLive)
|
.Where(channel => Database?.OnlyOnline == false || channel.IsLive)
|
||||||
.Where(channel => string.IsNullOrWhiteSpace(Filter) || channel.Name?.Contains(Filter, StringComparison.OrdinalIgnoreCase) == true)
|
.Where(channel => string.IsNullOrWhiteSpace(Filter) || channel.Name?.Contains(Filter, StringComparison.OrdinalIgnoreCase) == true)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
return new ObservableCollection<TwitchChannel>(filteredChannels);
|
return new ObservableCollection<TwitchChannel>(filteredChannels);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,14 +128,14 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
{
|
{
|
||||||
if (e.PropertyName != nameof(Twitch.UserChannel))
|
if (e.PropertyName != nameof(Twitch.UserChannel))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
OnPropertyChanged(nameof(IsLoggedIn));
|
OnPropertyChanged(nameof(IsLoggedIn));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
|
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnPropertyChanged(e);
|
base.OnPropertyChanged(e);
|
||||||
|
|
||||||
if (e.PropertyName == nameof(Filter))
|
if (e.PropertyName == nameof(Filter))
|
||||||
{
|
{
|
||||||
OnPropertyChanged(nameof(FilteredChannels));
|
OnPropertyChanged(nameof(FilteredChannels));
|
||||||
@ -143,7 +146,7 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
{
|
{
|
||||||
if (e.PropertyName != nameof(BetterRaidDatabase.OnlyOnline))
|
if (e.PropertyName != nameof(BetterRaidDatabase.OnlyOnline))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
OnPropertyChanged(nameof(FilteredChannels));
|
OnPropertyChanged(nameof(FilteredChannels));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user