diff --git a/.gitignore b/.gitignore index be8d6dc..27f1c0e 100644 --- a/.gitignore +++ b/.gitignore @@ -20,7 +20,7 @@ mono_crash.* # Build results -[Bb]uild/ +[Bb]iuld/ [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ diff --git a/App.axaml.cs b/App.axaml.cs index ec95bf8..f8b0b4a 100644 --- a/App.axaml.cs +++ b/App.axaml.cs @@ -33,6 +33,8 @@ public partial class App : Application + $"&response_type={TwitchOAuthResponseType}" + $"&scope={string.Join("+", TwitchOAuthScopes)}"; + internal static readonly string ChannelPlaceholderImageUrl = "https://cdn.pixabay.com/photo/2018/11/13/22/01/avatar-3814081_1280.png"; + public override void Initialize() { var userHomeDir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); @@ -137,7 +139,7 @@ public partial class App : Application BindingPlugins.DataValidators.RemoveAt(0); desktop.MainWindow = new MainWindow { - DataContext = new MainWindowViewModel(), + //DataContext = new MainWindowViewModel() }; } diff --git a/Models/TwitchChannel.cs b/Models/TwitchChannel.cs index 91f10ea..ca60254 100644 --- a/Models/TwitchChannel.cs +++ b/Models/TwitchChannel.cs @@ -1,3 +1,4 @@ +using System; using System.ComponentModel; using System.Runtime.CompilerServices; using Avalonia.Threading; @@ -12,6 +13,8 @@ public class TwitchChannel : INotifyPropertyChanged private string? _displayName; private string? _thumbnailUrl; private string? _category; + private string? _title; + private DateTime? _lastRaided; public string? BroadcasterId { @@ -93,6 +96,32 @@ public class TwitchChannel : INotifyPropertyChanged OnPropertyChanged(); } } + + 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) { diff --git a/Services/TwitchDataService.cs b/Services/TwitchDataService.cs new file mode 100644 index 0000000..d56609d --- /dev/null +++ b/Services/TwitchDataService.cs @@ -0,0 +1,6 @@ +namespace BetterRaid.Services; + +public class TwitchDataService +{ + +} \ No newline at end of file diff --git a/ViewModels/MainWindowViewModel.cs b/ViewModels/MainWindowViewModel.cs index 22deb9e..566e402 100644 --- a/ViewModels/MainWindowViewModel.cs +++ b/ViewModels/MainWindowViewModel.cs @@ -1,6 +1,12 @@ using System; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Linq; using System.Threading; +using System.Threading.Tasks; using Avalonia.Controls; +using Avalonia.Threading; using BetterRaid.Extensions; using BetterRaid.Misc; using BetterRaid.Models; @@ -11,13 +17,25 @@ namespace BetterRaid.ViewModels; public partial class MainWindowViewModel : ViewModelBase { private string? _filter; - + private ObservableCollection _channels = []; private BetterRaidDatabase? _db; public BetterRaidDatabase? Database { get => _db; - set => SetProperty(ref _db, value); + set + { + if (SetProperty(ref _db, value) && _db != null) + { + LoadChannelsFromDb(); + } + } + } + + public ObservableCollection Channels + { + get => _channels; + set => SetProperty(ref _channels, value); } public string? Filter @@ -46,10 +64,29 @@ public partial class MainWindowViewModel : ViewModelBase Tools.StartOAuthLogin(App.TwitchOAuthUrl, OnTwitchLoginCallback, CancellationToken.None); } - public void OnTwitchLoginCallback() + private 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); + } + } } diff --git a/Views/MainWindow.axaml b/Views/MainWindow.axaml index cfc2a95..74661c5 100644 --- a/Views/MainWindow.axaml +++ b/Views/MainWindow.axaml @@ -3,6 +3,8 @@ xmlns:vm="using:BetterRaid.ViewModels" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:ai="using:AsyncImageLoader" + xmlns:betterRaid="clr-namespace:BetterRaid" mc:Ignorable="d" d:DesignWidth="600" d:DesignHeight="800" Width="600" Height="800" @@ -108,17 +110,81 @@ IsScrollInertiaEnabled="True" /> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - diff --git a/Views/MainWindow.axaml.cs b/Views/MainWindow.axaml.cs index 9c3e211..a0e4258 100644 --- a/Views/MainWindow.axaml.cs +++ b/Views/MainWindow.axaml.cs @@ -15,279 +15,8 @@ namespace BetterRaid.Views; public partial class MainWindow : Window { - private ObservableCollection _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(); - } } } \ No newline at end of file