Group App Settings by purpose and let right-click on the gear open settings directly while preserving the left-click menu. WPF event reference: https://learn.microsoft.com/en-us/dotnet/api/system.windows.uielement.previewmouserightbuttondown?view=windowsdesktop-10.0
93 lines
3.0 KiB
C#
93 lines
3.0 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;
|
|
}
|
|
|
|
public void RefreshAvatar()
|
|
{
|
|
_ = LoadAvatarImageAsync();
|
|
}
|
|
|
|
private void GearButton_PreviewMouseRightButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
|
|
{
|
|
// Right-click goes straight to App Settings; the menu stays on left-click.
|
|
e.Handled = true;
|
|
if (DataContext is MainViewModel { OpenSettingsCommand: { } open } && open.CanExecute(null))
|
|
open.Execute(null);
|
|
}
|
|
|
|
private void GearButton_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
// One menu (Settings / Bug / Feature / About), opened on click — moved up
|
|
// from the bottom bar so app-level actions live next to the brand.
|
|
if (sender is Button { ContextMenu: { } menu } button)
|
|
{
|
|
menu.PlacementTarget = button;
|
|
menu.Placement = System.Windows.Controls.Primitives.PlacementMode.Bottom;
|
|
menu.IsOpen = true;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
} |