Started working on UI rework
This commit is contained in:
parent
019649b179
commit
b42d313bff
2
.gitignore
vendored
2
.gitignore
vendored
@ -20,7 +20,7 @@
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
[Bb]uild/
|
||||
[Bb]iuld/
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
|
@ -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()
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
@ -94,6 +97,32 @@ 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;
|
||||
|
6
Services/TwitchDataService.cs
Normal file
6
Services/TwitchDataService.cs
Normal file
@ -0,0 +1,6 @@
|
||||
namespace BetterRaid.Services;
|
||||
|
||||
public class TwitchDataService
|
||||
{
|
||||
|
||||
}
|
@ -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<TwitchChannel> _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<TwitchChannel> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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" />
|
||||
</ScrollViewer.GestureRecognizers>
|
||||
|
||||
<Grid HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
x:Name="raidGrid">
|
||||
<ListBox ItemsSource="{Binding Channels}">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid ColumnDefinitions="200,*"
|
||||
RowDefinitions="200">
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ai:AdvancedImage Grid.Column="0"
|
||||
Grid.Row="0"
|
||||
Source="{Binding ThumbnailUrl, TargetNullValue={x:Static betterRaid: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>
|
||||
</ScrollViewer>
|
||||
|
||||
</Grid>
|
||||
|
@ -15,279 +15,8 @@ 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();
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user