Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions Trdo/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,14 @@
private readonly UISettings _uiSettings = new();
private Mutex? _singleInstanceMutex;
private EventWaitHandle? _trayIconRestoreEvent;
private DispatcherQueueTimer? _trayIconWatchdogTimer;

Check warning on line 26 in Trdo/App.xaml.cs

View workflow job for this annotation

GitHub Actions / build

The field 'App._trayIconWatchdogTimer' is never used
private DispatcherQueueTimer? _restoreEventMonitorTimer;

/// <summary>
/// Maximum length for the now playing text in the tooltip before truncation.
/// </summary>
private const int MaxTooltipNowPlayingLength = 60;

public App()
{
InitializeComponent();
Expand Down Expand Up @@ -105,6 +110,12 @@
{
UpdatePlayPauseCommandText();
}
else if (e.PropertyName == nameof(PlayerViewModel.NowPlaying) ||
e.PropertyName == nameof(PlayerViewModel.HasNowPlaying))
{
// Update tooltip when now playing info changes
UpdatePlayPauseCommandText();
}
}

private void OnColorValuesChanged(UISettings sender, object args)
Expand Down Expand Up @@ -233,11 +244,25 @@
}
else if (_playerVm.IsPlaying)
{
_trayIcon.Tooltip = "Trdo (Playing) - Click to Pause";
// Include now playing info if available
if (_playerVm.HasNowPlaying)
{
// Truncate long now playing text to keep tooltip readable
string nowPlaying = _playerVm.NowPlaying;
if (nowPlaying.Length > MaxTooltipNowPlayingLength)
{
nowPlaying = string.Concat(nowPlaying.AsSpan(0, MaxTooltipNowPlayingLength - 3), "...");
}
_trayIcon.Tooltip = $"Trdo (Playing)\n{nowPlaying}";
}
else
{
_trayIcon.Tooltip = "Trdo (Playing)";
}
}
else
{
_trayIcon.Tooltip = "Trdo - Play";
_trayIcon.Tooltip = "Trdo (Paused)";
}
}

Expand Down
3 changes: 3 additions & 0 deletions Trdo/Assets/spotify.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
35 changes: 35 additions & 0 deletions Trdo/Converters/BooleanToVisibilityConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Data;
using System;

namespace Trdo.Converters;

/// <summary>
/// Converts a boolean value to Visibility. Returns Visible when true, Collapsed when false.
/// Use ConverterParameter="Invert" to invert the logic.
/// </summary>
public class BooleanToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is bool boolValue)
{
// Check if we should invert the result
bool invert = parameter is string paramString &&
paramString.Equals("Invert", StringComparison.OrdinalIgnoreCase);

if (invert)
{
boolValue = !boolValue;
}

return boolValue ? Visibility.Visible : Visibility.Collapsed;
}
return Visibility.Collapsed;
}

public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
52 changes: 52 additions & 0 deletions Trdo/Models/StreamMetadata.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
namespace Trdo.Models;

/// <summary>
/// Represents metadata extracted from an internet radio stream, typically from ICY (Icecast/Shoutcast) protocol.
/// </summary>
public class StreamMetadata
{
/// <summary>
/// The full stream title string, typically containing song and artist info.
/// Format is usually "Artist - Title" or similar.
/// </summary>
public string StreamTitle { get; set; } = string.Empty;

/// <summary>
/// The artist name, if available.
/// </summary>
public string Artist { get; set; } = string.Empty;

/// <summary>
/// The song/track title, if available.
/// </summary>
public string Title { get; set; } = string.Empty;

/// <summary>
/// Indicates whether any meaningful metadata was found.
/// </summary>
public bool HasMetadata => !string.IsNullOrWhiteSpace(StreamTitle) ||
!string.IsNullOrWhiteSpace(Artist) ||
!string.IsNullOrWhiteSpace(Title);

/// <summary>
/// Gets a display-friendly string for the now playing information.
/// </summary>
public string DisplayText
{
get
{
if (!string.IsNullOrWhiteSpace(Artist) && !string.IsNullOrWhiteSpace(Title))
return $"{Artist} - {Title}";

if (!string.IsNullOrWhiteSpace(StreamTitle))
return StreamTitle;

return string.Empty;
}
}

/// <summary>
/// Creates a new StreamMetadata with no data.
/// </summary>
public static StreamMetadata Empty => new();
}
2 changes: 1 addition & 1 deletion Trdo/Package.appxmanifest
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<Identity
Name="40087JoeFinApps.Trdo"
Publisher="CN=153F3B0F-BA3D-4964-8098-71AC78A1DF6A"
Version="1.3.2.0" />
Version="1.4.0.0" />

<mp:PhoneIdentity PhoneProductId="fa86867b-3ba1-44b4-b776-ea2ad8aea4c7" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>

Expand Down
4 changes: 4 additions & 0 deletions Trdo/Pages/AddStation.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using Trdo.Models;
using Trdo.Services;
using Trdo.ViewModels;

namespace Trdo.Pages;
Expand Down Expand Up @@ -71,6 +72,9 @@ private void SaveButton_Click(object sender, RoutedEventArgs e)
{
// Navigate to main page after successful save
_shellViewModel?.NavigateToPlayingPage();

// Reset navigation stack history when going home after adding a station
NavigationService.Instance.ClearBackStack();
}
}

Expand Down
135 changes: 135 additions & 0 deletions Trdo/Pages/NowPlayingPage.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
<?xml version="1.0" encoding="utf-8" ?>
<Page
x:Class="Trdo.Pages.NowPlayingPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="using:Trdo.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">

<Page.Resources>
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</Page.Resources>

<Grid RowDefinitions="Auto,*" RowSpacing="16">
<!-- Page Title -->
<TextBlock
Grid.Row="0"
FontSize="24"
FontWeight="SemiBold"
Text="Now Playing" />

<!-- Content -->
<Grid Grid.Row="1" Padding="0,0,12,0">

<!-- No Metadata Message -->
<StackPanel
HorizontalAlignment="Center"
VerticalAlignment="Center"
Spacing="12"
Visibility="{x:Bind ViewModel.HasMetadata, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}, ConverterParameter=Invert}">
<FontIcon
HorizontalAlignment="Center"
FontSize="48"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Glyph="&#xE8D6;" />
<TextBlock
HorizontalAlignment="Center"
FontSize="16"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="No track information available"
TextAlignment="Center" />
<TextBlock
HorizontalAlignment="Center"
FontSize="12"
Foreground="{ThemeResource TextFillColorTertiaryBrush}"
Text="Track info will appear here when the station broadcasts it"
TextAlignment="Center"
TextWrapping="Wrap" />
</StackPanel>

<!-- Track Info Display -->
<Grid VerticalAlignment="Center" Visibility="{x:Bind ViewModel.HasMetadata, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}">
<Grid
Padding="20"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
CornerRadius="12">
<StackPanel Spacing="12">

<!-- Music Icon -->
<Viewbox
Width="56"
Height="56"
HorizontalAlignment="Center">
<FontIcon
FontSize="56"
Foreground="{ThemeResource AccentFillColorDefaultBrush}"
Glyph="&#xE8D6;" />
</Viewbox>

<!-- Title (if available) -->
<TextBlock
HorizontalAlignment="Center"
FontSize="18"
FontWeight="SemiBold"
Text="{x:Bind ViewModel.Title, Mode=OneWay}"
TextAlignment="Center"
TextWrapping="Wrap"
Visibility="{x:Bind ViewModel.HasTitle, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}" />

<!-- Artist (if available) -->
<TextBlock
HorizontalAlignment="Center"
FontSize="14"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{x:Bind ViewModel.Artist, Mode=OneWay}"
TextAlignment="Center"
TextWrapping="Wrap"
Visibility="{x:Bind ViewModel.HasArtist, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}" />

<!-- Stream Title Only (shown when we don't have parsed artist/title) -->
<TextBlock
HorizontalAlignment="Center"
FontSize="16"
FontWeight="SemiBold"
Text="{x:Bind ViewModel.StreamTitle, Mode=OneWay}"
TextAlignment="Center"
TextWrapping="Wrap"
Visibility="{x:Bind ViewModel.ShowStreamTitleOnly, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}" />

<!-- Search Links -->
<StackPanel
Margin="0,4,0,0"
HorizontalAlignment="Center"
Orientation="Horizontal"
Spacing="16">

<!-- Search on Spotify -->
<HyperlinkButton Click="SpotifyLink_Click">
<StackPanel Orientation="Horizontal" Spacing="8">
<Image Width="20" Height="20">
<Image.Source>
<SvgImageSource UriSource="ms-appx:///Assets/spotify.svg" />
</Image.Source>
</Image>
<TextBlock Text="Spotify" />
</StackPanel>
</HyperlinkButton>

<!-- Search on Discogs -->
<HyperlinkButton Click="DiscogsLink_Click">
<StackPanel Orientation="Horizontal" Spacing="8">
<FontIcon FontSize="14" Glyph="&#xE721;" />
<TextBlock Text="Discogs" />
</StackPanel>
</HyperlinkButton>

</StackPanel>

</StackPanel>
</Grid>
</Grid>
</Grid>
</Grid>
</Page>
39 changes: 39 additions & 0 deletions Trdo/Pages/NowPlayingPage.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System.Diagnostics;
using Trdo.ViewModels;

namespace Trdo.Pages;

/// <summary>
/// A page that displays detailed stream metadata (now playing) information.
/// </summary>
public sealed partial class NowPlayingPage : Page
{
public NowPlayingViewModel ViewModel { get; }

public NowPlayingPage()
{
Debug.WriteLine("=== NowPlayingPage Constructor START ===");

InitializeComponent();
ViewModel = new NowPlayingViewModel();
DataContext = ViewModel;

Debug.WriteLine("[NowPlayingPage] ViewModel created and DataContext set");
Debug.WriteLine($"[NowPlayingPage] Current metadata: {ViewModel.DisplayText}");
Debug.WriteLine("=== NowPlayingPage Constructor END ===");
}

private async void DiscogsLink_Click(object sender, RoutedEventArgs e)
{
Debug.WriteLine("[NowPlayingPage] Discogs link clicked");
await ViewModel.SearchOnDiscogs();
}

private async void SpotifyLink_Click(object sender, RoutedEventArgs e)
{
Debug.WriteLine("[NowPlayingPage] Spotify link clicked");
await ViewModel.SearchOnSpotify();
}
}
Loading
Loading