Compare commits

..

No commits in common. "ui-rework" and "main" have entirely different histories.

18 changed files with 367 additions and 345 deletions

1
.gitignore vendored
View File

@ -20,7 +20,6 @@
mono_crash.*
# Build results
[Bb]iuld/
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/

View File

@ -5,209 +5,140 @@ using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Data.Core.Plugins;
using Avalonia.Markup.Xaml;
using BetterRaid.Services;
using BetterRaid.ViewModels;
using BetterRaid.Views;
using Microsoft.Extensions.DependencyInjection;
using TwitchLib.Api;
namespace BetterRaid;
public partial class App : Application
{
private readonly ServiceCollection _services = [];
private ServiceProvider? _provider;
private static TwitchAPI? _twitchApi;
private static bool _hasUserZnSubbed;
private static string _betterRaidDataPath = "";
private static string _twitchBroadcasterId = "";
private static string _twitchOAuthAccessToken = "";
private static string _twitchOAuthAccessTokenFilePath = "";
private const string TokenClientId = "kkxu4jorjrrc5jch1ito5i61hbev2o";
private const string TwitchOAuthRedirectUrl = "http://localhost:9900";
private const string TwitchOAuthResponseType = "token";
private static readonly string[] TwitchOAuthScopes = [
internal static TwitchAPI? TwitchApi = null;
internal static int AutoUpdateDelay = 10_000;
internal static bool HasUserZnSubbed = false;
internal static string BetterRaidDataPath = "";
internal static string TwitchBroadcasterId = "";
internal static string TwitchOAuthAccessToken = "";
internal static string TwitchOAuthAccessTokenFilePath = "";
internal static string TokenClientId = "kkxu4jorjrrc5jch1ito5i61hbev2o";
internal static readonly string TwitchOAuthRedirectUrl = "http://localhost:9900";
internal static readonly string TwitchOAuthResponseType = "token";
internal static readonly string[] TwitchOAuthScopes = [
"channel:manage:raids",
"user:read:subscriptions"
];
internal static readonly string TwitchOAuthUrl = $"https://id.twitch.tv/oauth2/authorize"
+ $"?client_id={TokenClientId}"
+ $"&redirect_uri={TwitchOAuthRedirectUrl}"
+ "&redirect_uri=http://localhost:9900"
+ $"&response_type={TwitchOAuthResponseType}"
+ $"&scope={string.Join("+", TwitchOAuthScopes)}";
public const string ChannelPlaceholderImageUrl = "https://cdn.pixabay.com/photo/2018/11/13/22/01/avatar-3814081_1280.png";
public static TwitchAPI? TwitchApi => _twitchApi;
public static bool HasUserZnSubbed => _hasUserZnSubbed;
public IServiceProvider? Provider => _provider;
public static string? TwitchBroadcasterId => _twitchBroadcasterId;
public static string TwitchOAuthAccessToken
{
get => _twitchOAuthAccessToken;
set
{
_twitchOAuthAccessToken = value;
InitTwitchClient(true);
}
}
public override void Initialize()
{
InitializeServices();
LoadTwitchToken();
AvaloniaXamlLoader.Load(_provider, this);
}
private void LoadTwitchToken()
{
var userHomeDir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
_betterRaidDataPath = Environment.OSVersion.Platform switch
switch (Environment.OSVersion.Platform)
{
PlatformID.Win32NT => Path.Combine(userHomeDir, "AppData", "Roaming", "BetterRaid"),
PlatformID.Unix => Path.Combine(userHomeDir, ".config", "BetterRaid"),
PlatformID.MacOSX => Path.Combine(userHomeDir, "Library", "Application Support", "BetterRaid"),
_ => throw new PlatformNotSupportedException($"Your platform '{Environment.OSVersion.Platform}' is not supported. Please report this issue here: https://www.github.com/zion-networks/BetterRaid/issues")
};
if (!Directory.Exists(_betterRaidDataPath))
{
var di = Directory.CreateDirectory(_betterRaidDataPath);
if (di.Exists == false)
{
throw new Exception($"Failed to create directory '{_betterRaidDataPath}'");
}
case PlatformID.Win32NT:
BetterRaidDataPath = Path.Combine(userHomeDir, "AppData", "Roaming", "BetterRaid");
break;
case PlatformID.Unix:
BetterRaidDataPath = Path.Combine(userHomeDir, ".config", "BetterRaid");
break;
case PlatformID.MacOSX:
BetterRaidDataPath = Path.Combine(userHomeDir, "Library", "Application Support", "BetterRaid");
break;
}
_twitchOAuthAccessTokenFilePath = Path.Combine(_betterRaidDataPath, ".access_token");
if (!Directory.Exists(BetterRaidDataPath))
Directory.CreateDirectory(BetterRaidDataPath);
if (!File.Exists(_twitchOAuthAccessTokenFilePath))
return;
TwitchOAuthAccessTokenFilePath = Path.Combine(BetterRaidDataPath, ".access_token");
_twitchOAuthAccessToken = File.ReadAllText(_twitchOAuthAccessTokenFilePath);
InitTwitchClient();
}
if (File.Exists(TwitchOAuthAccessTokenFilePath))
{
TwitchOAuthAccessToken = File.ReadAllText(TwitchOAuthAccessTokenFilePath);
InitTwitchClient();
}
private void InitializeServices()
{
_services.AddSingleton<ITwitchDataService, TwitchDataService>();
_services.AddTransient<MainWindowViewModel>();
_services.AddTransient<AboutWindowViewModel>();
_provider = _services.BuildServiceProvider();
AvaloniaXamlLoader.Load(this);
}
public static void InitTwitchClient(bool overrideToken = false)
{
Console.WriteLine("[INFO] Initializing Twitch Client...");
if (string.IsNullOrEmpty(_twitchOAuthAccessToken))
{
Console.WriteLine("[ERROR] Failed to initialize Twitch Client: Access Token is empty!");
return;
}
_twitchApi = new TwitchAPI
{
Settings =
{
ClientId = TokenClientId,
AccessToken = _twitchOAuthAccessToken
}
};
TwitchApi = new TwitchAPI();
TwitchApi.Settings.ClientId = TokenClientId;
TwitchApi.Settings.AccessToken = TwitchOAuthAccessToken;
Console.WriteLine("[INFO] Testing Twitch API connection...");
var user = _twitchApi.Helix.Users.GetUsersAsync().Result.Users.FirstOrDefault();
var user = TwitchApi.Helix.Users.GetUsersAsync().Result.Users.FirstOrDefault();
if (user == null)
{
_twitchApi = null;
TwitchApi = null;
Console.WriteLine("[ERROR] Failed to connect to Twitch API!");
return;
}
var channel = _twitchApi.Helix.Search
var channel = TwitchApi.Helix.Search
.SearchChannelsAsync(user.Login).Result.Channels
.FirstOrDefault(c => c.BroadcasterLogin == user.Login);
var userSubs = _twitchApi.Helix.Subscriptions.CheckUserSubscriptionAsync(
var userSubs = TwitchApi.Helix.Subscriptions.CheckUserSubscriptionAsync(
userId: user.Id,
broadcasterId: "1120558409"
).Result.Data;
if (userSubs is { Length: > 0 } && userSubs.Any(s => s.BroadcasterId == "1120558409"))
if (userSubs.Length > 0 && userSubs.Any(s => s.BroadcasterId == "1120558409"))
{
_hasUserZnSubbed = true;
HasUserZnSubbed = true;
}
if (channel == null)
{
Console.WriteLine($"[ERROR] Failed to get channel information for '{user.Login}'!");
Console.WriteLine("[ERROR] User channel could not be found!");
return;
}
_twitchBroadcasterId = channel.Id;
Console.WriteLine(_twitchBroadcasterId);
TwitchBroadcasterId = channel.Id;
System.Console.WriteLine(TwitchBroadcasterId);
Console.WriteLine("[INFO] Connected to Twitch API as '{0}'!", user.DisplayName);
if (!overrideToken)
return;
File.WriteAllText(_twitchOAuthAccessTokenFilePath, _twitchOAuthAccessToken);
switch (Environment.OSVersion.Platform)
if (overrideToken)
{
case PlatformID.Win32NT:
File.SetAttributes(_twitchOAuthAccessTokenFilePath, File.GetAttributes(_twitchOAuthAccessTokenFilePath) | FileAttributes.Hidden);
break;
File.WriteAllText(TwitchOAuthAccessTokenFilePath, TwitchOAuthAccessToken);
case PlatformID.Unix:
switch (Environment.OSVersion.Platform)
{
case PlatformID.Win32NT:
File.SetAttributes(TwitchOAuthAccessTokenFilePath, File.GetAttributes(TwitchOAuthAccessTokenFilePath) | FileAttributes.Hidden);
break;
case PlatformID.Unix:
#pragma warning disable CA1416 // Validate platform compatibility
File.SetUnixFileMode(_twitchOAuthAccessTokenFilePath, UnixFileMode.UserRead);
File.SetUnixFileMode(TwitchOAuthAccessTokenFilePath, UnixFileMode.UserRead);
#pragma warning restore CA1416 // Validate platform compatibility
break;
case PlatformID.MacOSX:
File.SetAttributes(_twitchOAuthAccessTokenFilePath, File.GetAttributes(_twitchOAuthAccessTokenFilePath) | FileAttributes.Hidden);
break;
default:
throw new PlatformNotSupportedException($"Your platform '{Environment.OSVersion.Platform}' is not supported. Please report this issue here: https://www.github.com/zion-networks/BetterRaid/issues");
break;
case PlatformID.MacOSX:
File.SetAttributes(TwitchOAuthAccessTokenFilePath, File.GetAttributes(TwitchOAuthAccessTokenFilePath) | FileAttributes.Hidden);
break;
}
}
}
public override void OnFrameworkInitializationCompleted()
{
BindingPlugins.DataValidators.RemoveAt(0);
var vm = _provider?.GetRequiredService<MainWindowViewModel>();
switch (ApplicationLifetime)
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
case IClassicDesktopStyleApplicationLifetime desktop:
// Line below is needed to remove Avalonia data validation.
// Without this line you will get duplicate validations from both Avalonia and CT
desktop.MainWindow = new MainWindow
{
DataContext = vm
};
break;
case ISingleViewApplicationLifetime singleViewPlatform:
singleViewPlatform.MainView = new MainWindow
{
DataContext = vm
};
break;
// Line below is needed to remove Avalonia data validation.
// Without this line you will get duplicate validations from both Avalonia and CT
BindingPlugins.DataValidators.RemoveAt(0);
desktop.MainWindow = new MainWindow
{
DataContext = new MainWindowViewModel(),
};
}
base.OnFrameworkInitializationCompleted();

View File

@ -27,7 +27,6 @@
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.1.0" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="TwitchLib" Version="3.5.3" />
</ItemGroup>
</Project>

View File

@ -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", "{49E459C8-9DCF-4D6D-95FC-75303243F248}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BetterRaid", "BetterRaid.csproj", "{77D4100D-424A-4E36-BFF2-14A40F217605}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -11,10 +11,10 @@ Global
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{49E459C8-9DCF-4D6D-95FC-75303243F248}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{49E459C8-9DCF-4D6D-95FC-75303243F248}.Debug|Any CPU.Build.0 = Debug|Any CPU
{49E459C8-9DCF-4D6D-95FC-75303243F248}.Release|Any CPU.ActiveCfg = Release|Any CPU
{49E459C8-9DCF-4D6D-95FC-75303243F248}.Release|Any CPU.Build.0 = Release|Any CPU
{77D4100D-424A-4E36-BFF2-14A40F217605}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{77D4100D-424A-4E36-BFF2-14A40F217605}.Debug|Any CPU.Build.0 = Debug|Any CPU
{77D4100D-424A-4E36-BFF2-14A40F217605}.Release|Any CPU.ActiveCfg = Release|Any CPU
{77D4100D-424A-4E36-BFF2-14A40F217605}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

BIN
Build/BetterRaid-0.0.1-alpha.exe Executable file

Binary file not shown.

Binary file not shown.

View File

@ -1,22 +1,11 @@
using Avalonia;
using Avalonia.Controls;
using Microsoft.Extensions.DependencyInjection;
namespace BetterRaid.Extensions;
public static class DataContextExtensions
{
public static T? GetDataContextAs<T>(this T obj) where T : StyledElement
public static T? GetDataContextAs<T>(this T obj) where T : Window
{
return obj.DataContext as T;
}
public static void InjectDataContext<T>(this StyledElement e) where T : class
{
if (Application.Current is not App { Provider: not null } app)
return;
var vm = app.Provider.GetRequiredService<T>();
e.DataContext = vm;
}
}

View File

@ -1,6 +1,6 @@
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Avalonia.Threading;
namespace BetterRaid.Models;
@ -12,8 +12,6 @@ public class TwitchChannel : INotifyPropertyChanged
private string? _displayName;
private string? _thumbnailUrl;
private string? _category;
private string? _title;
private DateTime? _lastRaided;
public string? BroadcasterId
{
@ -96,32 +94,6 @@ public class TwitchChannel : INotifyPropertyChanged
}
}
public string? Title
{
get => _title;
set
{
if (value == _title)
return;
_title = value;
OnPropertyChanged();
}
}
public DateTime? LastRaided
{
get => _lastRaided;
set
{
if (value == _lastRaided)
return;
_lastRaided = value;
OnPropertyChanged();
}
}
public TwitchChannel(string channelName)
{
Name = channelName;

View File

@ -9,9 +9,8 @@ sealed class Program
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break.
[STAThread]
public static void Main(string[] args) =>
BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);
public static void Main(string[] args) => BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);
// Avalonia configuration, don't remove; also used by visual designer.
public static AppBuilder BuildAvaloniaApp()

View File

@ -1,6 +0,0 @@
namespace BetterRaid.Services;
public interface ITwitchDataService
{
}

View File

@ -1,6 +0,0 @@
namespace BetterRaid.Services;
public class TwitchDataService : ITwitchDataService
{
}

View File

@ -1,13 +0,0 @@
using System;
using BetterRaid.Services;
namespace BetterRaid.ViewModels;
public class AboutWindowViewModel : ViewModelBase
{
public AboutWindowViewModel(ITwitchDataService s)
{
Console.WriteLine(s);
Console.WriteLine("[DEBUG] AboutWindowViewModel created");
}
}

View File

@ -1,11 +1,6 @@
using System;
namespace BetterRaid.ViewModels;
public class AddChannelWindowViewModel : ViewModelBase
{
public AddChannelWindowViewModel()
{
Console.WriteLine("[DEBUG] AddChannelWindowViewModel created");
}
}

View File

@ -1,12 +1,9 @@
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using Avalonia.Controls;
using BetterRaid.Extensions;
using BetterRaid.Misc;
using BetterRaid.Models;
using BetterRaid.Services;
using BetterRaid.Views;
namespace BetterRaid.ViewModels;
@ -14,25 +11,13 @@ namespace BetterRaid.ViewModels;
public partial class MainWindowViewModel : ViewModelBase
{
private string? _filter;
private ObservableCollection<TwitchChannel> _channels = [];
private BetterRaidDatabase? _db;
public BetterRaidDatabase? Database
{
get => _db;
set
{
if (SetProperty(ref _db, value) && _db != null)
{
LoadChannelsFromDb();
}
}
}
public ObservableCollection<TwitchChannel> Channels
{
get => _channels;
set => SetProperty(ref _channels, value);
set => SetProperty(ref _db, value);
}
public string? Filter
@ -43,12 +28,6 @@ public partial class MainWindowViewModel : ViewModelBase
public bool IsLoggedIn => App.TwitchApi != null;
public MainWindowViewModel(ITwitchDataService t)
{
Console.WriteLine(t);
Console.WriteLine("[DEBUG] MainWindowViewModel created");
}
public void ExitApplication()
{
//TODO polish later
@ -58,7 +37,6 @@ public partial class MainWindowViewModel : ViewModelBase
public void ShowAboutWindow(Window owner)
{
var about = new AboutWindow();
about.InjectDataContext<AboutWindowViewModel>();
about.ShowDialog(owner);
about.CenterToOwner();
}
@ -68,27 +46,10 @@ public partial class MainWindowViewModel : ViewModelBase
Tools.StartOAuthLogin(App.TwitchOAuthUrl, OnTwitchLoginCallback, CancellationToken.None);
}
private void OnTwitchLoginCallback()
public void OnTwitchLoginCallback()
{
App.InitTwitchClient(overrideToken: true);
OnPropertyChanged(nameof(IsLoggedIn));
}
private void LoadChannelsFromDb()
{
if (_db == null)
{
return;
}
Channels.Clear();
var channels = _db.Channels
.Select(channelName => new TwitchChannel(channelName))
.ToList();
foreach (var c in channels)
{
Channels.Add(c);
}
}
}

View File

@ -4,5 +4,4 @@ namespace BetterRaid.ViewModels;
public class ViewModelBase : ObservableObject
{
}

View File

@ -2,10 +2,8 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:BetterRaid.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="BetterRaid.Views.AboutWindow"
x:DataType="vm:AboutWindowViewModel"
Title="About"
MaxWidth="300"
MinWidth="300"

View File

@ -1,11 +1,9 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:BetterRaid.ViewModels"
xmlns:br="using:BetterRaid"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ai="using:AsyncImageLoader"
mc:Ignorable="d" d:DesignWidth="600" d:DesignHeight="800"
mc:Ignorable="d" d:DesignWidth="300" d:DesignHeight="450"
Width="600"
Height="800"
x:Class="BetterRaid.Views.MainWindow"
@ -110,81 +108,17 @@
IsScrollInertiaEnabled="True" />
</ScrollViewer.GestureRecognizers>
<ListBox ItemsSource="{Binding Channels}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid ColumnDefinitions="200,*"
RowDefinitions="200">
<Grid HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
x:Name="raidGrid">
<ai:AdvancedImage Grid.Column="0"
Grid.Row="0"
Source="{Binding ThumbnailUrl, TargetNullValue={x:Static br:App.ChannelPlaceholderImageUrl}}" />
<Border Grid.Column="0"
Grid.Row="0"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Height="20"
MinWidth="20"
CornerRadius="10"
Background="#FFFFFF"
Padding="3"
Margin="0, 0, 10, 10">
<TextBlock Text="{Binding ViewerCount, TargetNullValue='-'}"/>
</Border>
<Grid Grid.Column="1"
Grid.Row="0"
ColumnDefinitions="30*, 70*"
RowDefinitions="40, 40, 40, 40, 40">
<TextBlock Grid.Column="0"
Grid.Row="0"
Grid.ColumnSpan="2"
FontWeight="Bold"
TextDecorations="Underline"
Text="{Binding DisplayName, TargetNullValue='???'}" />
<TextBlock Grid.Column="0"
Grid.Row="1"
Text="Category:"
FontWeight="SemiBold" />
<TextBlock Grid.Column="0"
Grid.Row="2"
Text="Title:"
FontWeight="SemiBold" />
<TextBlock Grid.Column="0"
Grid.Row="3"
Text="Last Raided:"
FontWeight="SemiBold" />
<TextBlock Grid.Column="0"
Grid.Row="4"
Text=""
FontWeight="SemiBold" />
<TextBlock Grid.Column="1"
Grid.Row="1"
Text="{Binding Category, TargetNullValue='-'}" />
<TextBlock Grid.Column="1"
Grid.Row="2"
Text="{Binding Title, TargetNullValue='-'}" />
<TextBlock Grid.Column="1"
Grid.Row="3"
Text="{Binding LastRaided, TargetNullValue='Never Raided'}" />
<TextBlock Grid.Column="1"
Grid.Row="4"
Text="" />
</Grid>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
</Grid>
</ScrollViewer>
</Grid>

View File

@ -15,8 +15,279 @@ namespace BetterRaid.Views;
public partial class MainWindow : Window
{
private ObservableCollection<RaidButtonViewModel> _raidButtonVMs;
private RaidButtonViewModel? _znButtonVm;
private BackgroundWorker _autoUpdater;
public MainWindow()
{
_raidButtonVMs = [];
_znButtonVm = null;
_autoUpdater = new();
DataContextChanged += OnDataContextChanged;
InitializeComponent();
_autoUpdater.WorkerSupportsCancellation = true;
_autoUpdater.DoWork += UpdateAllTiles;
}
private void OnDatabaseChanged(object? sender, PropertyChangedEventArgs e)
{
// TODO: Only if new channel was added or existing were removed
// InitializeRaidChannels();
GenerateRaidGrid();
}
private void OnDataContextChanged(object? sender, EventArgs e)
{
if (DataContext is MainWindowViewModel vm)
{
var dbPath = Path.Combine(App.BetterRaidDataPath, "db.json");
try
{
vm.Database = BetterRaidDatabase.LoadFromFile(dbPath);
}
catch (FileNotFoundException)
{
var db = new BetterRaidDatabase();
db.Save(dbPath);
vm.Database = db;
}
vm.Database.AutoSave = true;
vm.Database.PropertyChanged += OnDatabaseChanged;
vm.PropertyChanged += OnViewModelChanged;
InitializeRaidChannels();
GenerateRaidGrid();
}
}
private void OnViewModelChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(MainWindowViewModel.Filter))
{
GenerateRaidGrid();
}
if (e.PropertyName == nameof(MainWindowViewModel.IsLoggedIn) && DataContext is MainWindowViewModel { IsLoggedIn: true })
{
InitializeRaidChannels();
GenerateRaidGrid();
}
}
private void InitializeRaidChannels()
{
if (DataContext is MainWindowViewModel { IsLoggedIn: false })
return;
if (_autoUpdater?.IsBusy == false)
{
_autoUpdater?.CancelAsync();
}
_raidButtonVMs.Clear();
var vm = DataContext as MainWindowViewModel;
if (vm?.Database == null)
return;
foreach (var channel in vm.Database.Channels)
{
if (string.IsNullOrEmpty(channel))
continue;
var rbvm = new RaidButtonViewModel(channel)
{
MainVm = vm
};
_raidButtonVMs.Add(rbvm);
}
if (App.HasUserZnSubbed)
{
_znButtonVm = null;
}
else
{
_znButtonVm = new RaidButtonViewModel("zionnetworks")
{
MainVm = vm,
HideDeleteButton = true,
IsAd = true
};
}
if (_autoUpdater?.IsBusy == false)
{
_autoUpdater?.RunWorkerAsync();
}
}
private void GenerateRaidGrid()
{
if (DataContext is MainWindowViewModel { IsLoggedIn: false })
return;
foreach (var child in raidGrid.Children)
{
if (child is Button btn)
{
btn.Click -= OnAddChannelButtonClicked;
}
}
raidGrid.Children.Clear();
var vm = DataContext as MainWindowViewModel;
if (vm?.Database == null)
{
return;
}
var visibleChannels = _raidButtonVMs.Where(channel =>
{
var visible = true;
if (string.IsNullOrWhiteSpace(vm.Filter) == false)
{
if (channel.ChannelName.Contains(vm.Filter, StringComparison.OrdinalIgnoreCase) == false)
{
visible = false;
}
}
if (vm.Database.OnlyOnline && channel.Channel?.IsLive == false)
{
visible = false;
}
return visible;
}).OrderByDescending(c => c.Channel?.IsLive).ToList();
var rows = (int)Math.Ceiling((visibleChannels.Count + (App.HasUserZnSubbed ? 1 : 2)) / 3.0);
for (var i = 0; i < rows; i++)
{
raidGrid.RowDefinitions.Add(new RowDefinition(GridLength.Parse("Auto")));
}
var colIndex = App.HasUserZnSubbed ? 0 : 1;
var rowIndex = 0;
foreach (var channel in visibleChannels)
{
var btn = new RaidButton
{
DataContext = channel
};
Grid.SetColumn(btn, colIndex);
Grid.SetRow(btn, rowIndex);
raidGrid.Children.Add(btn);
colIndex++;
if (colIndex % 3 == 0)
{
colIndex = 0;
rowIndex++;
}
}
var addButton = new Button
{
Content = "+",
FontSize = 72,
Margin = new Avalonia.Thickness(5),
MinHeight = 250,
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Stretch,
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Stretch,
HorizontalContentAlignment = Avalonia.Layout.HorizontalAlignment.Center,
VerticalContentAlignment = Avalonia.Layout.VerticalAlignment.Center
};
addButton.Click += OnAddChannelButtonClicked;
Grid.SetColumn(addButton, colIndex);
Grid.SetRow(addButton, rowIndex);
raidGrid.Children.Add(addButton);
if (App.HasUserZnSubbed == false)
{
var znButton = new RaidButton
{
DataContext = _znButtonVm
};
Grid.SetColumn(znButton, 0);
Grid.SetRow(znButton, 0);
raidGrid.Children.Add(znButton);
}
}
private void OnAddChannelButtonClicked(object? sender, RoutedEventArgs e)
{
var dialog = new AddChannelWindow();
dialog.CenterToOwner();
var vm = DataContext as MainWindowViewModel;
if (vm?.Database == null)
return;
// TODO Button Command not working, Button remains disabled
// This is a dirty workaround
dialog.okBtn.Click += (sender, args) => {
if (string.IsNullOrWhiteSpace(dialog?.channelNameTxt.Text) == false)
{
vm.Database.AddChannel(dialog.channelNameTxt.Text);
vm.Database.Save();
}
dialog?.Close();
InitializeRaidChannels();
GenerateRaidGrid();
};
dialog.ShowDialog(this);
}
public void UpdateChannelData()
{
var loggedIn = Dispatcher.UIThread.Invoke(() => {
return (DataContext as MainWindowViewModel)?.IsLoggedIn ?? false;
});
if (loggedIn == false)
return;
foreach (var vm in _raidButtonVMs)
{
Task.Run(vm.GetOrUpdateChannelAsync);
}
if (_znButtonVm != null)
{
Task.Run(_znButtonVm.GetOrUpdateChannelAsync);
}
}
private void UpdateAllTiles(object? sender, DoWorkEventArgs e)
{
while (e.Cancel == false)
{
UpdateChannelData();
Task.Delay(App.AutoUpdateDelay).Wait();
}
}
}