Added logo; Added ad; Optimized stuff; ready for first alpha release

This commit is contained in:
Enrico Ludwig 2024-08-29 16:38:32 +02:00
parent baa2a15d02
commit 6c860efd29
15 changed files with 156 additions and 34 deletions

View File

@ -15,12 +15,18 @@ public partial class App : Application
{ {
internal static TwitchAPI? TwitchApi = null; internal static TwitchAPI? TwitchApi = null;
internal static int AutoUpdateDelay = 10_000; 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 TwitchOAuthAccessToken = "";
internal static string TwitchOAuthAccessTokenFilePath = ""; internal static string TwitchOAuthAccessTokenFilePath = "";
internal static string TokenClientId = "kkxu4jorjrrc5jch1ito5i61hbev2o"; internal static string TokenClientId = "kkxu4jorjrrc5jch1ito5i61hbev2o";
internal static readonly string TwitchOAuthRedirectUrl = "http://localhost:9900"; internal static readonly string TwitchOAuthRedirectUrl = "http://localhost:9900";
internal static readonly string TwitchOAuthResponseType = "token"; internal static readonly string TwitchOAuthResponseType = "token";
internal static readonly string[] TwitchOAuthScopes = [ "channel:manage:raids", "user:read:chat" ]; internal static readonly string[] TwitchOAuthScopes = [
"channel:manage:raids",
"user:read:subscriptions"
];
internal static readonly string TwitchOAuthUrl = $"https://id.twitch.tv/oauth2/authorize" internal static readonly string TwitchOAuthUrl = $"https://id.twitch.tv/oauth2/authorize"
+ $"?client_id={TokenClientId}" + $"?client_id={TokenClientId}"
+ "&redirect_uri=http://localhost:9900" + "&redirect_uri=http://localhost:9900"
@ -30,25 +36,24 @@ public partial class App : Application
public override void Initialize() public override void Initialize()
{ {
var userHomeDir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); var userHomeDir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
var betterRaidDir = "";
switch (Environment.OSVersion.Platform) switch (Environment.OSVersion.Platform)
{ {
case PlatformID.Win32NT: case PlatformID.Win32NT:
betterRaidDir = Path.Combine(userHomeDir, "AppData", "Roaming", "BetterRaid"); BetterRaidDataPath = Path.Combine(userHomeDir, "AppData", "Roaming", "BetterRaid");
break; break;
case PlatformID.Unix: case PlatformID.Unix:
betterRaidDir = Path.Combine(userHomeDir, ".config", "BetterRaid"); BetterRaidDataPath = Path.Combine(userHomeDir, ".config", "BetterRaid");
break; break;
case PlatformID.MacOSX: case PlatformID.MacOSX:
betterRaidDir = Path.Combine(userHomeDir, "Library", "Application Support", "BetterRaid"); BetterRaidDataPath = Path.Combine(userHomeDir, "Library", "Application Support", "BetterRaid");
break; break;
} }
if (!Directory.Exists(betterRaidDir)) if (!Directory.Exists(BetterRaidDataPath))
Directory.CreateDirectory(betterRaidDir); Directory.CreateDirectory(BetterRaidDataPath);
TwitchOAuthAccessTokenFilePath = Path.Combine(betterRaidDir, ".access_token"); TwitchOAuthAccessTokenFilePath = Path.Combine(BetterRaidDataPath, ".access_token");
if (File.Exists(TwitchOAuthAccessTokenFilePath)) if (File.Exists(TwitchOAuthAccessTokenFilePath))
{ {
@ -56,7 +61,6 @@ public partial class App : Application
InitTwitchClient(); InitTwitchClient();
} }
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
} }
@ -78,6 +82,29 @@ public partial class App : Application
return; return;
} }
var channel = TwitchApi.Helix.Search
.SearchChannelsAsync(user.Login).Result.Channels
.FirstOrDefault(c => c.BroadcasterLogin == user.Login);
var userSubs = TwitchApi.Helix.Subscriptions.CheckUserSubscriptionAsync(
userId: user.Id,
broadcasterId: "1120558409"
).Result.Data;
if (userSubs.Length > 0 && userSubs.Any(s => s.BroadcasterId == "1120558409"))
{
HasUserZnSubbed = true;
}
if (channel == null)
{
Console.WriteLine("[ERROR] User channel could not be found!");
return;
}
TwitchBroadcasterId = channel.Id;
System.Console.WriteLine(TwitchBroadcasterId);
Console.WriteLine("[INFO] Connected to Twitch API as '{0}'!", user.DisplayName); Console.WriteLine("[INFO] Connected to Twitch API as '{0}'!", user.DisplayName);
if (overrideToken) if (overrideToken)

BIN
Assets/logo.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
Assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -5,7 +5,12 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport> <BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<ApplicationManifest>app.manifest</ApplicationManifest> <ApplicationManifest>app.manifest</ApplicationManifest>
<DebugType>embedded</DebugType>
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
<PublishSingleFile>true</PublishSingleFile>
<SelfContained>true</SelfContained>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault> <AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
<ApplicationIcon>Assets/logo.ico</ApplicationIcon>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17 # Visual Studio Version 17
VisualStudioVersion = 17.5.002.0 VisualStudioVersion = 17.5.002.0
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BetterRaid", "BetterRaid.csproj", "{EDFD12AB-9E05-4D87-9139-C220A703CFDB}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BetterRaid", "BetterRaid.csproj", "{260E29DE-709B-4860-8A06-F6C4F33B6659}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -11,10 +11,10 @@ Global
Release|Any CPU = Release|Any CPU Release|Any CPU = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{EDFD12AB-9E05-4D87-9139-C220A703CFDB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {260E29DE-709B-4860-8A06-F6C4F33B6659}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EDFD12AB-9E05-4D87-9139-C220A703CFDB}.Debug|Any CPU.Build.0 = Debug|Any CPU {260E29DE-709B-4860-8A06-F6C4F33B6659}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EDFD12AB-9E05-4D87-9139-C220A703CFDB}.Release|Any CPU.ActiveCfg = Release|Any CPU {260E29DE-709B-4860-8A06-F6C4F33B6659}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EDFD12AB-9E05-4D87-9139-C220A703CFDB}.Release|Any CPU.Build.0 = Release|Any CPU {260E29DE-709B-4860-8A06-F6C4F33B6659}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

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

Binary file not shown.

Binary file not shown.

View File

@ -35,6 +35,25 @@
Grid.Row="0" Grid.Row="0"
Source="{Binding Channel.ThumbnailUrl}" /> Source="{Binding Channel.ThumbnailUrl}" />
<Border IsVisible="{Binding IsAd}"
BorderThickness="1"
BorderBrush="DarkGoldenrod"
CornerRadius="4"
Width="24"
Height="16"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Margin="5">
<TextBlock Text="Ad"
Margin="2"
FontSize="12"
HorizontalAlignment="Center"
VerticalAlignment="Center"
TextAlignment="Center"
Foreground="DarkGoldenrod" />
</Border>
<Button Width="32" <Button Width="32"
Height="32" Height="32"
Background="DarkRed" Background="DarkRed"
@ -46,6 +65,7 @@
HorizontalAlignment="Right" HorizontalAlignment="Right"
VerticalAlignment="Top" VerticalAlignment="Top"
Margin="5" Margin="5"
IsVisible="{Binding !HideDeleteButton}"
Command="{Binding RemoveChannel}"> Command="{Binding RemoveChannel}">
<Image Source="avares://BetterRaid/Assets/icons8-close-32.png" <Image Source="avares://BetterRaid/Assets/icons8-close-32.png"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"

View File

@ -0,0 +1,11 @@
using Avalonia.Controls;
namespace BetterRaid.Extensions;
public static class DataContextExtensions
{
public static T? GetDataContextAs<T>(this T obj) where T : Window
{
return obj.DataContext as T;
}
}

View File

@ -72,6 +72,11 @@ public class BetterRaidDatabase : INotifyPropertyChanged
throw new ArgumentException("No target path given to save database at"); throw new ArgumentException("No target path given to save database at");
} }
if (string.IsNullOrEmpty(path) == false && string.IsNullOrEmpty(_databaseFilePath))
{
_databaseFilePath = path;
}
var dbStr = JsonConvert.SerializeObject(this); var dbStr = JsonConvert.SerializeObject(this);
var targetPath = (path ?? _databaseFilePath)!; var targetPath = (path ?? _databaseFilePath)!;

View File

@ -16,6 +16,8 @@ public class RaidButtonViewModel : ViewModelBase
{ {
private TwitchChannel? _channel; private TwitchChannel? _channel;
private SolidColorBrush _viewerCountColor = new SolidColorBrush(Color.FromRgb(byte.MaxValue, byte.MaxValue, byte.MaxValue)); private SolidColorBrush _viewerCountColor = new SolidColorBrush(Color.FromRgb(byte.MaxValue, byte.MaxValue, byte.MaxValue));
private bool _hideDeleteButton;
private bool _isAd;
public string ChannelName public string ChannelName
{ {
@ -23,6 +25,18 @@ public class RaidButtonViewModel : ViewModelBase
set; set;
} }
public bool HideDeleteButton
{
get => _hideDeleteButton;
set => SetProperty(ref _hideDeleteButton, value);
}
public bool IsAd
{
get => _isAd;
set => SetProperty(ref _isAd, value);
}
public TwitchChannel? Channel => _channel ?? new TwitchChannel(ChannelName); public TwitchChannel? Channel => _channel ?? new TwitchChannel(ChannelName);
public SolidColorBrush ViewerCountColor public SolidColorBrush ViewerCountColor
@ -128,12 +142,15 @@ public class RaidButtonViewModel : ViewModelBase
if (Channel == null) if (Channel == null)
return; return;
StartRaidResponse? raid = null; if (string.IsNullOrWhiteSpace(App.TwitchBroadcasterId))
return;
if (App.TwitchBroadcasterId == Channel.BroadcasterId)
return;
try try
{ {
// TODO: Get own broadcaster id await App.TwitchApi.Helix.Raids.StartRaidAsync(App.TwitchBroadcasterId, Channel.BroadcasterId);
raid = await App.TwitchApi.Helix.Raids.StartRaidAsync("", Channel.BroadcasterId);
} }
catch (Exception e) catch (Exception e)
{ {
@ -142,12 +159,6 @@ public class RaidButtonViewModel : ViewModelBase
return; return;
} }
if (raid.Data.Length > 0)
{
var createdAt = raid.Data[0].CreatedAt;
var isMature = raid.Data[0].IsMature;
}
if (MainVm?.Database != null) if (MainVm?.Database != null)
{ {

View File

@ -8,6 +8,7 @@
MaxWidth="300" MaxWidth="300"
MinWidth="300" MinWidth="300"
Height="200" Height="200"
Icon="/Assets/logo.png"
Background="DarkSlateGray"> Background="DarkSlateGray">
<ScrollViewer> <ScrollViewer>

View File

@ -6,6 +6,7 @@
mc:Ignorable="d" d:DesignWidth="100" d:DesignHeight="50" mc:Ignorable="d" d:DesignWidth="100" d:DesignHeight="50"
x:Class="BetterRaid.Views.AddChannelWindow" x:Class="BetterRaid.Views.AddChannelWindow"
x:DataType="vm:AddChannelWindowViewModel" x:DataType="vm:AddChannelWindowViewModel"
Icon="/Assets/logo.png"
Width="200" Width="200"
Height="80" Height="80"
MaxWidth="200" MaxWidth="200"

View File

@ -8,7 +8,7 @@
Height="800" Height="800"
x:Class="BetterRaid.Views.MainWindow" x:Class="BetterRaid.Views.MainWindow"
x:DataType="vm:MainWindowViewModel" x:DataType="vm:MainWindowViewModel"
Icon="/Assets/avalonia-logo.ico" Icon="/Assets/logo.png"
Title="BetterRaid" Title="BetterRaid"
Background="DarkSlateGray"> Background="DarkSlateGray">

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.ComponentModel; using System.ComponentModel;
using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Controls; using Avalonia.Controls;
@ -15,15 +16,17 @@ namespace BetterRaid.Views;
public partial class MainWindow : Window public partial class MainWindow : Window
{ {
private ObservableCollection<RaidButtonViewModel> _raidButtonVMs; private ObservableCollection<RaidButtonViewModel> _raidButtonVMs;
private RaidButtonViewModel? _znButtonVm;
private BackgroundWorker _autoUpdater; private BackgroundWorker _autoUpdater;
public MainWindow() public MainWindow()
{ {
_raidButtonVMs = []; _raidButtonVMs = [];
_znButtonVm = null;
_autoUpdater = new(); _autoUpdater = new();
DataContextChanged += OnDataContextChanged; DataContextChanged += OnDataContextChanged;
InitializeComponent(); InitializeComponent();
_autoUpdater.WorkerSupportsCancellation = true; _autoUpdater.WorkerSupportsCancellation = true;
@ -41,7 +44,20 @@ public partial class MainWindow : Window
{ {
if (DataContext is MainWindowViewModel vm) if (DataContext is MainWindowViewModel vm)
{ {
vm.Database = BetterRaidDatabase.LoadFromFile("db.json"); 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.AutoSave = true;
vm.Database.PropertyChanged += OnDatabaseChanged; vm.Database.PropertyChanged += OnDatabaseChanged;
@ -76,11 +92,6 @@ public partial class MainWindow : Window
_autoUpdater?.CancelAsync(); _autoUpdater?.CancelAsync();
} }
foreach (var rbvm in _raidButtonVMs)
{
}
_raidButtonVMs.Clear(); _raidButtonVMs.Clear();
var vm = DataContext as MainWindowViewModel; var vm = DataContext as MainWindowViewModel;
@ -101,6 +112,20 @@ public partial class MainWindow : Window
_raidButtonVMs.Add(rbvm); _raidButtonVMs.Add(rbvm);
} }
if (App.HasUserZnSubbed)
{
_znButtonVm = null;
}
else
{
_znButtonVm = new RaidButtonViewModel("zionnetworks")
{
MainVm = vm,
HideDeleteButton = true,
IsAd = true
};
}
if (_autoUpdater?.IsBusy == false) if (_autoUpdater?.IsBusy == false)
{ {
_autoUpdater?.RunWorkerAsync(); _autoUpdater?.RunWorkerAsync();
@ -148,15 +173,14 @@ public partial class MainWindow : Window
return visible; return visible;
}).OrderByDescending(c => c.Channel?.IsLive).ToList(); }).OrderByDescending(c => c.Channel?.IsLive).ToList();
var rows = (int)Math.Ceiling((visibleChannels.Count + (App.HasUserZnSubbed ? 1 : 2)) / 3.0);
var rows = (int)Math.Ceiling((visibleChannels.Count + 1) / 3.0);
for (var i = 0; i < rows; i++) for (var i = 0; i < rows; i++)
{ {
raidGrid.RowDefinitions.Add(new RowDefinition(GridLength.Parse("Auto"))); raidGrid.RowDefinitions.Add(new RowDefinition(GridLength.Parse("Auto")));
} }
var colIndex = 0; var colIndex = App.HasUserZnSubbed ? 0 : 1;
var rowIndex = 0; var rowIndex = 0;
foreach (var channel in visibleChannels) foreach (var channel in visibleChannels)
{ {
@ -196,6 +220,18 @@ public partial class MainWindow : Window
Grid.SetRow(addButton, rowIndex); Grid.SetRow(addButton, rowIndex);
raidGrid.Children.Add(addButton); 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) private void OnAddChannelButtonClicked(object? sender, RoutedEventArgs e)
@ -239,6 +275,11 @@ public partial class MainWindow : Window
{ {
Task.Run(vm.GetOrUpdateChannelAsync); Task.Run(vm.GetOrUpdateChannelAsync);
} }
if (_znButtonVm != null)
{
Task.Run(_znButtonVm.GetOrUpdateChannelAsync);
}
} }
private void UpdateAllTiles(object? sender, DoWorkEventArgs e) private void UpdateAllTiles(object? sender, DoWorkEventArgs e)