Shorten the bandmap hover text to frequency, spotter, age and comment
The first line reads "14025.0 by K1TTT, 41m ago". The comment follows on a second line and is left out when it is only LSB, USB or SSB. Control characters are removed from the comment: cluster lines end in bell and carriage-return characters, which the tooltip drew as boxes. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,23 +2,38 @@ using System.Globalization;
|
|||||||
|
|
||||||
namespace Nonemm.Spotting;
|
namespace Nonemm.Spotting;
|
||||||
|
|
||||||
/// What the bandmap shows when the pointer rests on a spot: who spotted it,
|
/// What the bandmap shows when the pointer rests on a spot: the frequency,
|
||||||
/// when, and the comment they sent.
|
/// who spotted it and how long ago, and the comment if it says more than the
|
||||||
|
/// sideband.
|
||||||
public static class SpotDescription
|
public static class SpotDescription
|
||||||
{
|
{
|
||||||
public static string Of(Spot spot)
|
public static string Of(Spot spot, DateTime? nowUtc = null)
|
||||||
{
|
{
|
||||||
string time = spot.AtUtc.ToString("HH:mm", CultureInfo.InvariantCulture);
|
string age = Age((nowUtc ?? DateTime.UtcNow) - spot.AtUtc);
|
||||||
string kilohertz = spot.Frequency.Kilohertz.ToString("0.0", CultureInfo.InvariantCulture);
|
string kilohertz = spot.Frequency.Kilohertz.ToString("0.0", CultureInfo.InvariantCulture);
|
||||||
string who = spot.Spotter.Length > 0
|
string first = spot.Spotter.Length > 0
|
||||||
? $"Spotted by {spot.Spotter} at {time}Z"
|
? $"{kilohertz} by {spot.Spotter}, {age}"
|
||||||
: spot.Source switch
|
: $"{kilohertz}, {age}";
|
||||||
{
|
string comment = Comment(spot.Comment);
|
||||||
SpotSource.Log => $"Worked at {time}Z",
|
return comment.Length > 0 ? $"{first}\n{comment}" : first;
|
||||||
SpotSource.Operator => $"Spotted here at {time}Z",
|
}
|
||||||
_ => $"Spotted at {time}Z",
|
|
||||||
};
|
private static string Age(TimeSpan span)
|
||||||
string text = $"{spot.Call.Text} {kilohertz}\n{who}";
|
{
|
||||||
return spot.Comment.Trim().Length > 0 ? $"{text}\n{spot.Comment.Trim()}" : text;
|
int minutes = Math.Max(0, (int)span.TotalMinutes);
|
||||||
|
return minutes switch
|
||||||
|
{
|
||||||
|
< 60 => $"{minutes}m ago",
|
||||||
|
_ => $"{minutes / 60}h {minutes % 60}m ago",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cluster lines end in bell and carriage-return characters, which a
|
||||||
|
/// tooltip draws as boxes, so control characters are dropped.
|
||||||
|
private static string Comment(string text)
|
||||||
|
{
|
||||||
|
string clean = new([.. text.Where(c => !char.IsControl(c))]);
|
||||||
|
clean = clean.Trim();
|
||||||
|
return clean.ToUpperInvariant() is "LSB" or "USB" or "SSB" ? "" : clean;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ namespace Nonemm.Spotting.Tests;
|
|||||||
public class SpotDescriptionTests
|
public class SpotDescriptionTests
|
||||||
{
|
{
|
||||||
private static readonly DateTime At = new(2026, 5, 30, 12, 1, 0, DateTimeKind.Utc);
|
private static readonly DateTime At = new(2026, 5, 30, 12, 1, 0, DateTimeKind.Utc);
|
||||||
|
private static readonly DateTime Now = At.AddMinutes(41);
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ASpotNamesTheSpotterTheTimeAndTheComment()
|
public void ASpotNamesTheSpotterTheTimeAndTheComment()
|
||||||
@@ -13,15 +14,49 @@ public class SpotDescriptionTests
|
|||||||
Callsign.Parse("JA1XYZ"), Frequency.FromKilohertz(14_025), At, SpotSource.Cluster,
|
Callsign.Parse("JA1XYZ"), Frequency.FromKilohertz(14_025), At, SpotSource.Cluster,
|
||||||
"K1TTT", "CW 26 dB");
|
"K1TTT", "CW 26 dB");
|
||||||
|
|
||||||
Assert.Equal("JA1XYZ 14025.0\nSpotted by K1TTT at 12:01Z\nCW 26 dB", SpotDescription.Of(spot));
|
Assert.Equal("14025.0 by K1TTT, 41m ago\nCW 26 dB", SpotDescription.Of(spot, Now));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ASpotWithoutACommentHasTwoLines()
|
public void ASpotWithoutACommentOrSpotterHasOneLine()
|
||||||
{
|
{
|
||||||
Spot spot = new(
|
Spot spot = new(
|
||||||
Callsign.Parse("JA1XYZ"), Frequency.FromKilohertz(14_025), At, SpotSource.Log);
|
Callsign.Parse("JA1XYZ"), Frequency.FromKilohertz(14_025), At, SpotSource.Log);
|
||||||
|
|
||||||
Assert.Equal("JA1XYZ 14025.0\nWorked at 12:01Z", SpotDescription.Of(spot));
|
Assert.Equal("14025.0, 41m ago", SpotDescription.Of(spot, Now));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("USB")]
|
||||||
|
[InlineData("lsb")]
|
||||||
|
[InlineData("SSB\a\a")]
|
||||||
|
public void ASidebandAloneIsLeftOut(string comment)
|
||||||
|
{
|
||||||
|
Spot spot = new(
|
||||||
|
Callsign.Parse("JA1XYZ"), Frequency.FromKilohertz(14_025), At, SpotSource.Cluster,
|
||||||
|
"K1TTT", comment);
|
||||||
|
|
||||||
|
Assert.Equal("14025.0 by K1TTT, 41m ago", SpotDescription.Of(spot, Now));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ControlCharactersAreDroppedFromTheComment()
|
||||||
|
{
|
||||||
|
Spot spot = new(
|
||||||
|
Callsign.Parse("JA1XYZ"), Frequency.FromKilohertz(14_025), At, SpotSource.Cluster,
|
||||||
|
"K1TTT", "CW 26 dB\r\a");
|
||||||
|
|
||||||
|
Assert.Equal("14025.0 by K1TTT, 41m ago\nCW 26 dB", SpotDescription.Of(spot, Now));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(20, "14025.0, 0m ago")]
|
||||||
|
[InlineData(3600 + 300, "14025.0, 1h 5m ago")]
|
||||||
|
public void TheAgeIsInMinutesThenHours(int seconds, string expected)
|
||||||
|
{
|
||||||
|
Spot spot = new(
|
||||||
|
Callsign.Parse("JA1XYZ"), Frequency.FromKilohertz(14_025), At, SpotSource.Log);
|
||||||
|
|
||||||
|
Assert.Equal(expected, SpotDescription.Of(spot, At.AddSeconds(seconds)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user