Build 729 (creator): - "press <esc> to return" loses the word "press"; the label is "Record / Go-Live". - The bar takes focus on "Build scenes" and Tab cycles between the two halves only (KeyboardNavigation.TabNavigation="Cycle"), never off into the window. - Entering Record / Go-Live starts with NOTHING chosen — the output switches are a fresh decision, not a remembered one. - Three states: nothing chosen [YouTube][Local MP4]; YouTube chosen [YouTube(red)][Live Stream][Private Stream]; Local MP4 chosen [Local MP4(red)][Start Recording] → [End Recording] while recording. Only the chosen output is red; the action chips never highlight. Choosing Local MP4 takes the left slot and the YouTube chip disappears with it, which is what "slides to the left, replacing it" describes. "Private Stream" is the private-visibility stream — the same thing this app has always called the test stream (creator's clarification, so the wording is deliberate, not a new broadcast type). Output chips are one style with a Tag-driven trigger (OutputChip + OutputChipIcon), so a chip's red face and its glyph colour can't drift apart; the one-alpha hit-area colour is a token, because DesignTokensTests rightly refuses literals in theme files. 680 pass, 0 warnings. Scope justification: Assets/starting-background.jpg is outside this unit — the creator's replacement art, still awaiting their visual check.
91 lines
3.1 KiB
C#
91 lines
3.1 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Net.Http;
|
|
using System.Windows;
|
|
using System.Windows.Controls;
|
|
using System.Windows.Media.Imaging;
|
|
using ytLive.Helpers;
|
|
using ytLive.ViewModels;
|
|
|
|
namespace ytLive.Controls;
|
|
|
|
public partial class TopBar : UserControl
|
|
{
|
|
private static readonly HttpClient AvatarHttp = new() { Timeout = TimeSpan.FromSeconds(15) };
|
|
private string? _loadedAvatarUrl;
|
|
|
|
public TopBar()
|
|
{
|
|
InitializeComponent();
|
|
AvatarImage.Source = null;
|
|
AvatarInitial.Visibility = System.Windows.Visibility.Collapsed;
|
|
|
|
// Keyboard enters the bar on Build scenes, and Tab cycles between the two halves only
|
|
// (creator, build 729) — Tab must not walk off into the rest of the window from here.
|
|
Loaded += (_, _) => BuildSegmentButton.Focus();
|
|
}
|
|
|
|
/// <summary>Right-click on the LlamaCasty wordmark opens the app menu (Report Bug / Feature
|
|
/// Request / About) — the menu that used to hang off the gear, which now opens the Settings
|
|
/// drawer directly (creator ruling 2026-11-01).</summary>
|
|
private void BrandWordmark_PreviewMouseRightButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
|
|
{
|
|
if (sender is Button { ContextMenu: { } menu } button)
|
|
{
|
|
menu.PlacementTarget = button;
|
|
menu.Placement = System.Windows.Controls.Primitives.PlacementMode.Bottom;
|
|
menu.IsOpen = true;
|
|
e.Handled = true;
|
|
}
|
|
}
|
|
|
|
public void RefreshAvatar()
|
|
{
|
|
_ = LoadAvatarImageAsync();
|
|
}
|
|
|
|
private async System.Threading.Tasks.Task LoadAvatarImageAsync()
|
|
{
|
|
var vm = DataContext as MainViewModel;
|
|
var url = vm?.AccountAvatarUrl;
|
|
if (vm is not { IsConnected: true })
|
|
{
|
|
ShowAvatarFallback(null);
|
|
return;
|
|
}
|
|
if (string.IsNullOrWhiteSpace(url))
|
|
{
|
|
AppLog.Write("Avatar: connected but no profile image URL on the session");
|
|
ShowAvatarFallback(null);
|
|
return;
|
|
}
|
|
if (url == _loadedAvatarUrl && AvatarImage.Source != null) return;
|
|
try
|
|
{
|
|
var bytes = await AvatarHttp.GetByteArrayAsync(url);
|
|
using var ms = new MemoryStream(bytes);
|
|
var bitmap = new BitmapImage();
|
|
bitmap.BeginInit();
|
|
bitmap.CacheOption = BitmapCacheOption.OnLoad;
|
|
bitmap.StreamSource = ms;
|
|
bitmap.EndInit();
|
|
bitmap.Freeze();
|
|
_loadedAvatarUrl = url;
|
|
AvatarImage.Source = bitmap;
|
|
AvatarInitial.Visibility = System.Windows.Visibility.Collapsed;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
AppLog.Write($"Avatar load failed ({url}): {ex.Message}");
|
|
ShowAvatarFallback(vm?.AccountDisplayName);
|
|
}
|
|
}
|
|
|
|
private void ShowAvatarFallback(string? name)
|
|
{
|
|
_loadedAvatarUrl = null;
|
|
AvatarImage.Source = null;
|
|
AvatarInitial.Text = string.IsNullOrWhiteSpace(name) ? "?" : name.Substring(0, 1).ToUpperInvariant();
|
|
AvatarInitial.Visibility = System.Windows.Visibility.Visible;
|
|
}
|
|
} |