diff --git a/App.axaml.cs b/App.axaml.cs
index e136622..b454913 100644
--- a/App.axaml.cs
+++ b/App.axaml.cs
@@ -1,5 +1,6 @@
using System;
using System.IO;
+using System.Linq;
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Data.Core.Plugins;
@@ -17,6 +18,8 @@ namespace BetterRaid;
public partial class App : Application
{
+ public static int AutoUpdateDelay = 10_000;
+ public static string TwitchBroadcasterId = "";
public static string TwitchChannelName = "";
public static string TokenClientId = "";
public static string TokenClientSecret = "";
@@ -54,7 +57,6 @@ public partial class App : Application
TwitchClient = new TwitchClient(customClient);
TwitchClient.Initialize(creds, TwitchChannelName);
- TwitchClient.OnMessageReceived += OnMessageReceived;
TwitchClient.OnConnected += OnConnected;
TwitchClient.OnConnectionError += OnConnectionError;
@@ -64,6 +66,19 @@ public partial class App : Application
TwitchAPI.Settings.ClientId = TokenClientId;
TwitchAPI.Settings.AccessToken = TokenClientAccess;
+ var channels = TwitchAPI.Helix.Search.SearchChannelsAsync(TwitchChannelName).Result;
+ var exactChannel = channels.Channels.FirstOrDefault(c => c.BroadcasterLogin == TwitchChannelName);
+
+ if (exactChannel != null)
+ {
+ TwitchBroadcasterId = exactChannel.Id;
+ Console.WriteLine($"TWITCH BROADCASTER ID SET TO {TwitchBroadcasterId}");
+ }
+ else
+ {
+ Console.WriteLine("FAILED TO SET BROADCASTER ID!");
+ }
+
AvaloniaXamlLoader.Load(this);
}
@@ -77,11 +92,6 @@ public partial class App : Application
Console.WriteLine("[INFO] Twitch Client connected!");
}
- private void OnMessageReceived(object? sender, OnMessageReceivedArgs e)
- {
- Console.WriteLine($"{e.ChatMessage.DisplayName}: {e.ChatMessage.Message}");
- }
-
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
diff --git a/BetterRaid.generated.sln b/BetterRaid.generated.sln
index e1c46b5..4942695 100644
--- a/BetterRaid.generated.sln
+++ b/BetterRaid.generated.sln
@@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.002.0
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BetterRaid", "BetterRaid.csproj", "{050F930D-FE73-4FA8-A05B-E659A4A0CEEA}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BetterRaid", "BetterRaid.csproj", "{7AB75970-30DB-496E-960C-535708A65EC6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -11,10 +11,10 @@ Global
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {050F930D-FE73-4FA8-A05B-E659A4A0CEEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {050F930D-FE73-4FA8-A05B-E659A4A0CEEA}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {050F930D-FE73-4FA8-A05B-E659A4A0CEEA}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {050F930D-FE73-4FA8-A05B-E659A4A0CEEA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7AB75970-30DB-496E-960C-535708A65EC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7AB75970-30DB-496E-960C-535708A65EC6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7AB75970-30DB-496E-960C-535708A65EC6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7AB75970-30DB-496E-960C-535708A65EC6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/Controls/RaidButton.axaml b/Controls/RaidButton.axaml
new file mode 100644
index 0000000..2249a06
--- /dev/null
+++ b/Controls/RaidButton.axaml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
diff --git a/Controls/RaidButton.axaml.cs b/Controls/RaidButton.axaml.cs
new file mode 100644
index 0000000..023c2c6
--- /dev/null
+++ b/Controls/RaidButton.axaml.cs
@@ -0,0 +1,13 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace BetterRaid;
+
+public partial class RaidButton : UserControl
+{
+ public RaidButton()
+ {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/Models/TwitchChannel.cs b/Models/TwitchChannel.cs
index 20bdff9..a534f26 100644
--- a/Models/TwitchChannel.cs
+++ b/Models/TwitchChannel.cs
@@ -1,14 +1,94 @@
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+using Avalonia.Threading;
+
namespace BetterRaid.Models;
-public class TwitchChannel
+public class TwitchChannel : INotifyPropertyChanged
{
- public string? BroadcasterId { get; set; }
- public string Name { get; set; }
- public bool IsLive { get; set; }
- public int ViewerCount { get; set; }
+ private string? viewerCount;
+ private bool isLive;
+ private string? name;
+ private string? displayName;
+ private string? thumbnailUrl;
+
+ public string? BroadcasterId
+ {
+ get;
+ set;
+ }
+ public string? Name
+ {
+ get => name;
+ set
+ {
+ if (value == name)
+ return;
+
+ name = value;
+ OnPropertyChanged();
+ }
+ }
+ public bool IsLive
+ {
+ get => isLive;
+ set
+ {
+ if (value == isLive)
+ return;
+
+ isLive = value;
+ OnPropertyChanged();
+ }
+ }
+ public string? ViewerCount
+ {
+ get => viewerCount;
+ set
+ {
+ if (value == viewerCount)
+ return;
+
+ viewerCount = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public string? ThumbnailUrl
+ {
+ get => thumbnailUrl;
+ set
+ {
+ if (value == thumbnailUrl)
+ return;
+
+ thumbnailUrl = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public string? DisplayName
+ {
+ get => displayName;
+ set
+ {
+ if (value == displayName)
+ return;
+
+ displayName = value;
+ OnPropertyChanged();
+ }
+ }
public TwitchChannel(string channelName)
{
Name = channelName;
}
+
+ public event PropertyChangedEventHandler? PropertyChanged;
+
+ private void OnPropertyChanged([CallerMemberName] string? propertyName = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
}
\ No newline at end of file
diff --git a/ViewModels/MainWindowViewModel.cs b/ViewModels/MainWindowViewModel.cs
index 58589dc..aea25a2 100644
--- a/ViewModels/MainWindowViewModel.cs
+++ b/ViewModels/MainWindowViewModel.cs
@@ -1,6 +1,28 @@
-namespace BetterRaid.ViewModels;
+using System;
+using Avalonia;
+
+namespace BetterRaid.ViewModels;
public partial class MainWindowViewModel : ViewModelBase
{
-
+ private string? _filter;
+
+ public string? Filter
+ {
+ get => _filter;
+ set
+ {
+ if (value == _filter)
+ return;
+
+ _filter = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public void ExitApplication()
+ {
+ //TODO polish later
+ Environment.Exit(0);
+ }
}
diff --git a/ViewModels/RaidButtonViewModel.cs b/ViewModels/RaidButtonViewModel.cs
new file mode 100644
index 0000000..d1da0bf
--- /dev/null
+++ b/ViewModels/RaidButtonViewModel.cs
@@ -0,0 +1,131 @@
+using System;
+using System.ComponentModel;
+using System.Linq;
+using System.Threading.Tasks;
+using Avalonia.Media;
+using BetterRaid.Models;
+using TwitchLib.Api.Helix.Models.Raids.StartRaid;
+using TwitchLib.Api.Helix.Models.Search;
+using TwitchLib.Api.Helix.Models.Streams.GetStreams;
+
+namespace BetterRaid.ViewModels;
+
+public class RaidButtonViewModel : ViewModelBase
+{
+ private TwitchChannel? _channel;
+ private SolidColorBrush _viewerCountColor = new SolidColorBrush(Color.FromRgb(byte.MaxValue, byte.MaxValue, byte.MaxValue));
+
+ public required string ChannelName
+ {
+ get;
+ set;
+ }
+
+ public TwitchChannel Channel => _channel ?? new TwitchChannel(ChannelName);
+
+ public SolidColorBrush ViewerCountColor
+ {
+ get => _viewerCountColor;
+ set
+ {
+ if (value == _viewerCountColor)
+ return;
+
+ _viewerCountColor = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public async Task GetOrUpdateChannelAsync()
+ {
+ if (_channel == null)
+ {
+ _channel = new TwitchChannel(ChannelName);
+ _channel.PropertyChanged += OnChannelDataChanged;
+ }
+
+ var currentChannelData = await GetChannelAsync(ChannelName);
+
+ if (currentChannelData == null)
+ return false;
+
+ var currentStreamData = await GetStreamAsync(currentChannelData);
+
+ _channel.BroadcasterId = currentChannelData.Id;
+ _channel.Name = ChannelName;
+ _channel.DisplayName = currentChannelData.DisplayName;
+ _channel.IsLive = currentChannelData.IsLive;
+ _channel.ThumbnailUrl = currentChannelData.ThumbnailUrl;
+ _channel.ViewerCount = currentStreamData?.ViewerCount.ToString() ?? "(Offline)";
+
+ if (_channel.IsLive)
+ {
+ ViewerCountColor = new SolidColorBrush(Color.FromRgb(0, byte.MaxValue, 0));
+ }
+ else
+ {
+ ViewerCountColor = new SolidColorBrush(Color.FromRgb(byte.MaxValue, 0, 0));
+ }
+
+ return true;
+ }
+
+ private async Task GetChannelAsync(string channelName)
+ {
+ if (App.TwitchAPI == null)
+ return null;
+
+ if (string.IsNullOrEmpty(channelName))
+ return null;
+
+ var channels = await App.TwitchAPI.Helix.Search.SearchChannelsAsync(channelName);
+ var exactChannel = channels.Channels.FirstOrDefault(c => c.BroadcasterLogin.Equals(channelName, StringComparison.CurrentCultureIgnoreCase));
+
+ return exactChannel;
+ }
+
+ private async Task GetStreamAsync(Channel currentChannelData)
+ {
+ if (App.TwitchAPI == null)
+ return null;
+
+ if (currentChannelData == null)
+ return null;
+
+ var streams = await App.TwitchAPI.Helix.Streams.GetStreamsAsync(userLogins: [currentChannelData.BroadcasterLogin]);
+ var exactStream = streams.Streams.FirstOrDefault(s => s.UserLogin == currentChannelData.BroadcasterLogin);
+
+ return exactStream;
+ }
+
+ public async Task RaidChannel()
+ {
+ if (App.TwitchAPI == null)
+ return;
+
+ StartRaidResponse? raid = null;
+
+ try
+ {
+ raid = await App.TwitchAPI.Helix.Raids.StartRaidAsync(App.TwitchBroadcasterId, Channel.BroadcasterId);
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(e.Message);
+ Console.WriteLine(e.StackTrace);
+
+ return;
+ }
+
+ if (raid.Data.Length > 0)
+ {
+ var createdAt = raid.Data[0].CreatedAt;
+ var isMature = raid.Data[0].IsMature;
+ }
+ }
+
+ private void OnChannelDataChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ OnPropertyChanged(nameof(Channel));
+ }
+}
\ No newline at end of file
diff --git a/Views/MainWindow.axaml b/Views/MainWindow.axaml
index d7ae223..ba339df 100644
--- a/Views/MainWindow.axaml
+++ b/Views/MainWindow.axaml
@@ -15,17 +15,64 @@
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Views/MainWindow.axaml.cs b/Views/MainWindow.axaml.cs
index 6fc9b5b..3abc772 100644
--- a/Views/MainWindow.axaml.cs
+++ b/Views/MainWindow.axaml.cs
@@ -1,21 +1,16 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using AsyncImageLoader;
-using Avalonia;
+using System.ComponentModel;
+using System.Threading.Tasks;
using Avalonia.Controls;
-using Avalonia.Data;
-using Avalonia.Media;
-using Avalonia.Media.Imaging;
using Avalonia.Threading;
-using BetterRaid.Models;
using BetterRaid.ViewModels;
-using TwitchLib.Client.Events;
namespace BetterRaid.Views;
public partial class MainWindow : Window
{
+ private BackgroundWorker _autoUpdater;
+
private string[] _channelNames = [
"Cedricun", // Ehrenbruder
"ZanTal", // Ehrenschwester
@@ -31,33 +26,80 @@ public partial class MainWindow : Window
public MainWindow()
{
+ _autoUpdater = new BackgroundWorker();
+
InitializeComponent();
- PrepareRaidGrid();
- ConnectToTwitch();
+ GenerateRaidGrid();
+
+ DataContextChanged += OnDataContextChanged;
}
- private void PrepareRaidGrid()
+ private void OnDataContextChanged(object? sender, EventArgs e)
+ {
+ if (DataContext is MainWindowViewModel vm)
+ {
+ vm.PropertyChanged += OnViewModelChanged;
+ }
+ }
+
+ private void OnViewModelChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == nameof(MainWindowViewModel.Filter))
+ {
+ if (DataContext is MainWindowViewModel mainWindowVm)
+ {
+ if (string.IsNullOrEmpty(mainWindowVm.Filter))
+ {
+ foreach (var child in raidGrid.Children)
+ {
+ child.IsVisible = true;
+ }
+
+ return;
+ }
+
+ foreach (var child in raidGrid.Children)
+ {
+ if (child.DataContext is RaidButtonViewModel vm)
+ {
+ if (string.IsNullOrEmpty(vm.Channel?.DisplayName))
+ continue;
+
+ if (string.IsNullOrEmpty(mainWindowVm.Filter))
+ continue;
+
+ if (vm.Channel.DisplayName.Contains(mainWindowVm.Filter, StringComparison.OrdinalIgnoreCase) == false)
+ {
+ child.IsVisible = false;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void GenerateRaidGrid()
{
var rows = (int)Math.Ceiling(_channelNames.Length / 3.0);
for (var i = 0; i < rows; i++)
{
- raidGrid.RowDefinitions.Add(new RowDefinition(GridLength.Parse("200")));
+ raidGrid.RowDefinitions.Add(new RowDefinition(GridLength.Parse("*")));
}
var colIndex = 0;
var rowIndex = 0;
foreach (var channel in _channelNames)
{
- var btn = new Button
+ if (string.IsNullOrEmpty(channel))
+ continue;
+
+ var btn = new RaidButton
{
- Content = channel,
- DataContext = new TwitchChannel(channel),
- Margin = Thickness.Parse("5"),
- HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Stretch,
- VerticalAlignment = Avalonia.Layout.VerticalAlignment.Stretch,
- HorizontalContentAlignment = Avalonia.Layout.HorizontalAlignment.Center,
- VerticalContentAlignment = Avalonia.Layout.VerticalAlignment.Center
+ DataContext = new RaidButtonViewModel
+ {
+ ChannelName = channel
+ }
};
Grid.SetColumn(btn, colIndex);
@@ -71,64 +113,41 @@ public partial class MainWindow : Window
colIndex = 0;
rowIndex++;
}
+
+ if (btn.DataContext is RaidButtonViewModel vm)
+ {
+ Dispatcher.UIThread.InvokeAsync(vm.GetOrUpdateChannelAsync);
+ }
}
+
+ _autoUpdater.DoWork += UpdateAllTiles;
+ _autoUpdater.RunWorkerAsync();
}
- private void ConnectToTwitch()
+ public void UpdateAllTiles(object? sender, DoWorkEventArgs e)
{
- if (App.TwitchClient != null && App.TwitchAPI != null)
+ while (e.Cancel == false)
{
- foreach (var c in raidGrid.Children)
+ Task.Delay(App.AutoUpdateDelay).Wait();
+
+ if (raidGrid == null || raidGrid.Children.Count == 0)
{
- if (c is Button btn)
+ return;
+ }
+
+ foreach (var children in raidGrid.Children)
+ {
+ Dispatcher.UIThread.InvokeAsync(async () =>
{
- var channel = (btn.DataContext as TwitchChannel)?.Name;
-
- if (string.IsNullOrEmpty(channel) == false)
+ if (children.DataContext is RaidButtonViewModel vm)
{
- var channels = App.TwitchAPI.Helix.Search.SearchChannelsAsync(channel).Result;
- var exactChannel = channels.Channels.FirstOrDefault(c => c.BroadcasterLogin.ToLower() == channel.ToLower());
-
- Dispatcher.UIThread.Invoke(() =>
- {
- if (exactChannel != null)
- {
- if (btn.DataContext is TwitchChannel ctx)
- {
- ctx.BroadcasterId = exactChannel.Id;
- var ib = new ImageBrush();
- ImageBrushLoader.SetSource(ib, exactChannel.ThumbnailUrl);
- btn.Background = ib;
-
- var streamInfo = App.TwitchAPI.Helix.Streams.GetStreamsAsync(userLogins: new List([channel])).Result;
- var exactStreamInfo = streamInfo.Streams.FirstOrDefault(s => s.UserLogin.ToLower() == channel.ToLower());
-
- if (exactStreamInfo != null)
- {
- if (exactChannel.IsLive)
- {
- btn.Foreground = new SolidColorBrush(new Color(byte.MaxValue, 0, byte.MaxValue, 0));
- btn.Content = $"{exactChannel.DisplayName} ({exactStreamInfo.ViewerCount})";
- }
- else
- {
- btn.Foreground = new SolidColorBrush(new Color(byte.MaxValue, byte.MaxValue, 0, 0));
- btn.Content = $"{exactChannel.DisplayName} (Offline)";
- }
-
- ctx.ViewerCount = exactStreamInfo.ViewerCount;
- }
- else
- {
- btn.Foreground = new SolidColorBrush(new Color(byte.MaxValue, byte.MaxValue, 0, 0));
- btn.Content = $"{exactChannel.DisplayName} (Offline)";
- }
- }
- }
- });
+ await vm.GetOrUpdateChannelAsync();
}
}
+ );
}
+
+ Console.WriteLine("Data Update");
}
}
}
\ No newline at end of file