OAuth session persists via DPAPI; sign-in lives in the Start Stream dialog; add xUnit test project (6 passing)

This commit is contained in:
2026-08-06 09:38:35 -07:00
parent 8f0ecd3796
commit 4afe11e718
15 changed files with 432 additions and 67 deletions
+74 -6
View File
@@ -1,13 +1,31 @@
using System.Windows.Input;
using ytLive.Helpers;
using ytLive.Models;
namespace ytLive.ViewModels;
public class GoLiveViewModel : ViewModelBase
{
private readonly Func<Task<YouTubeChannel?>> _signInProvider;
private string _streamTitle = string.Empty;
private string _streamDescription = string.Empty;
private string _visibility = "Public";
private bool _isSignedIn;
private string _accountDisplayName = string.Empty;
private string _accountAvatarUrl = string.Empty;
private bool _isBusy;
public GoLiveViewModel(Func<Task<YouTubeChannel?>> signInProvider, YouTubeChannel? account)
{
_signInProvider = signInProvider;
SignInCommand = new RelayCommand(_ => _ = SignInAsync());
StartCommand = new RelayCommand(_ => StartRequested?.Invoke(), _ => IsSignedIn && !IsBusy);
CancelCommand = new RelayCommand(_ => CancelRequested?.Invoke(), _ => !IsBusy);
if (account != null)
ApplyAccount(account);
}
public string StreamTitle
{
@@ -29,15 +47,65 @@ public class GoLiveViewModel : ViewModelBase
public string[] Visibilities { get; } = { "Public", "Unlisted", "Private" };
public bool IsSignedIn
{
get => _isSignedIn;
private set
{
if (SetProperty(ref _isSignedIn, value))
CommandManager.InvalidateRequerySuggested();
}
}
public string AccountDisplayName
{
get => _accountDisplayName;
private set => SetProperty(ref _accountDisplayName, value);
}
public string AccountAvatarUrl
{
get => _accountAvatarUrl;
private set => SetProperty(ref _accountAvatarUrl, value);
}
public bool IsBusy
{
get => _isBusy;
private set
{
if (SetProperty(ref _isBusy, value))
CommandManager.InvalidateRequerySuggested();
}
}
public ICommand SignInCommand { get; }
public ICommand StartCommand { get; }
public ICommand CancelCommand { get; }
public GoLiveViewModel()
{
StartCommand = new RelayCommand(_ => StartRequested?.Invoke());
CancelCommand = new RelayCommand(_ => CancelRequested?.Invoke());
}
public event Action? StartRequested;
public event Action? CancelRequested;
private async Task SignInAsync()
{
if (IsBusy) return;
IsBusy = true;
try
{
var channel = await _signInProvider();
if (channel != null)
ApplyAccount(channel);
}
finally
{
IsBusy = false;
}
}
private void ApplyAccount(YouTubeChannel channel)
{
AccountDisplayName = channel.DisplayName;
AccountAvatarUrl = channel.ProfileImageUrl;
IsSignedIn = true;
}
}