refactor(ui): alert rows are two lines of text — the heading line was redundant
Creator: system messages are limited to two lines; the label above the message ('STEAM ID NOT
PRESENT', 'STREAM') was repeating what the row already said and pushing the stack past its budget.
AttentionAlert loses Label; rows render Text only. The licence row is now exactly:
Steam ID Limit: 30fps
Click for More Info
(60fps with a close control once owned). Rows are identified by Id internally, so nothing in the UI
depends on the removed text. Tests updated to assert position/text rather than the heading.
This commit is contained in:
@@ -81,12 +81,9 @@
|
|||||||
Width="14" Height="14" Stretch="Uniform" VerticalAlignment="Top"
|
Width="14" Height="14" Stretch="Uniform" VerticalAlignment="Top"
|
||||||
Margin="6,2,2,0" ToolTip="Standing indicator"
|
Margin="6,2,2,0" ToolTip="Standing indicator"
|
||||||
Visibility="{Binding ShowsPin, Converter={StaticResource BoolToVis}}"/>
|
Visibility="{Binding ShowsPin, Converter={StaticResource BoolToVis}}"/>
|
||||||
<StackPanel>
|
<TextBlock Text="{Binding Text}" Style="{StaticResource AppLogEntryText}"
|
||||||
<TextBlock Text="{Binding Label}" Style="{StaticResource AppLogEntryText}"
|
TextWrapping="Wrap" VerticalAlignment="Center"
|
||||||
FontWeight="Bold" Foreground="{StaticResource Brush.Error}"/>
|
Foreground="{StaticResource Brush.Error}"/>
|
||||||
<TextBlock Text="{Binding Text}" Style="{StaticResource AppLogEntryText}"
|
|
||||||
TextWrapping="Wrap" Margin="0,2,0,0"/>
|
|
||||||
</StackPanel>
|
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
</Border>
|
</Border>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|||||||
@@ -8,10 +8,11 @@ namespace ytLive.Models;
|
|||||||
/// becomes dismissible — and its dismissal permanent — once ownership is verified).</para>
|
/// becomes dismissible — and its dismissal permanent — once ownership is verified).</para>
|
||||||
/// <para>Clicking a row runs its action: the licence indicator opens About; an event alert opens the
|
/// <para>Clicking a row runs its action: the licence indicator opens About; an event alert opens the
|
||||||
/// Logs tab with that event's lines highlighted.</para>
|
/// Logs tab with that event's lines highlighted.</para>
|
||||||
|
/// <para>Rows are text only — a heading line above the message was redundant and pushed the stack past
|
||||||
|
/// its two-line budget (creator, 2026-11-05).</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed record AttentionAlert(
|
public sealed record AttentionAlert(
|
||||||
string Id,
|
string Id,
|
||||||
string Label,
|
|
||||||
string Text,
|
string Text,
|
||||||
bool IsPinned,
|
bool IsPinned,
|
||||||
AttentionAlertAction Action,
|
AttentionAlertAction Action,
|
||||||
|
|||||||
@@ -46,8 +46,6 @@ public partial class MainViewModel
|
|||||||
return $"Steam ID Limit: {entitled}fps\nClick for More Info";
|
return $"Steam ID Limit: {entitled}fps\nClick for More Info";
|
||||||
}
|
}
|
||||||
|
|
||||||
private string SteamLicenceLabel() => IsAppOwned ? "STEAM ID PRESENT" : "STEAM ID NOT PRESENT";
|
|
||||||
|
|
||||||
/// <summary>Recomputes row 1 in place. Called at startup and whenever ownership changes.</summary>
|
/// <summary>Recomputes row 1 in place. Called at startup and whenever ownership changes.</summary>
|
||||||
private void RefreshSteamLicenceAlert()
|
private void RefreshSteamLicenceAlert()
|
||||||
{
|
{
|
||||||
@@ -63,7 +61,6 @@ public partial class MainViewModel
|
|||||||
|
|
||||||
var alert = new AttentionAlert(
|
var alert = new AttentionAlert(
|
||||||
SteamLicenceAlertId,
|
SteamLicenceAlertId,
|
||||||
SteamLicenceLabel(),
|
|
||||||
SteamLicenceText(),
|
SteamLicenceText(),
|
||||||
IsPinned: !IsAppOwned,
|
IsPinned: !IsAppOwned,
|
||||||
AttentionAlertAction.About);
|
AttentionAlertAction.About);
|
||||||
@@ -76,9 +73,9 @@ public partial class MainViewModel
|
|||||||
|
|
||||||
/// <summary>Pushes an event alert. Newest sits directly under the licence indicator; older ones
|
/// <summary>Pushes an event alert. Newest sits directly under the licence indicator; older ones
|
||||||
/// stack beneath, so the newest is the one nearest the eye.</summary>
|
/// stack beneath, so the newest is the one nearest the eye.</summary>
|
||||||
private AttentionAlert? PushAttentionAlert(string id, string label, string text, long logFrom, long logTo)
|
private AttentionAlert? PushAttentionAlert(string id, string text, long logFrom, long logTo)
|
||||||
{
|
{
|
||||||
var alert = new AttentionAlert(id, label, text, IsPinned: false, AttentionAlertAction.Logs, logFrom, logTo);
|
var alert = new AttentionAlert(id, text, IsPinned: false, AttentionAlertAction.Logs, logFrom, logTo);
|
||||||
var insertAt = _attentionAlerts.Count(alert => alert.Id == SteamLicenceAlertId);
|
var insertAt = _attentionAlerts.Count(alert => alert.Id == SteamLicenceAlertId);
|
||||||
var existing = _attentionAlerts.FirstOrDefault(candidate => candidate.Id == id);
|
var existing = _attentionAlerts.FirstOrDefault(candidate => candidate.Id == id);
|
||||||
if (existing != null)
|
if (existing != null)
|
||||||
@@ -95,8 +92,8 @@ public partial class MainViewModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Test seam: pushes an event row without a stream session behind it.</summary>
|
/// <summary>Test seam: pushes an event row without a stream session behind it.</summary>
|
||||||
internal AttentionAlert? PushAttentionAlertForTest(string id, string label, string text, long logFrom, long logTo)
|
internal AttentionAlert? PushAttentionAlertForTest(string id, string text, long logFrom, long logTo)
|
||||||
=> PushAttentionAlert(id, label, text, logFrom, logTo);
|
=> PushAttentionAlert(id, text, logFrom, logTo);
|
||||||
|
|
||||||
private void RemoveAttentionAlert(string id)
|
private void RemoveAttentionAlert(string id)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -87,7 +87,6 @@ public partial class MainViewModel
|
|||||||
: logFrom;
|
: logFrom;
|
||||||
PushAttentionAlert(
|
PushAttentionAlert(
|
||||||
"stream-recovery",
|
"stream-recovery",
|
||||||
"STREAM",
|
|
||||||
text,
|
text,
|
||||||
logFrom,
|
logFrom,
|
||||||
Math.Max(logFrom, to));
|
Math.Max(logFrom, to));
|
||||||
|
|||||||
@@ -4,9 +4,11 @@ A stack of red rows at the top of the left panel, **above both mode grids** (so
|
|||||||
and while live) and pushing the panel's content down. Only red rows belong there — it is the surface for
|
and while live) and pushing the panel's content down. Only red rows belong there — it is the surface for
|
||||||
"something needs you", never chatter.
|
"something needs you", never chatter.
|
||||||
|
|
||||||
|
- **Rows are text only, two lines at most** — no heading line above the message (creator, 2026-11-05:
|
||||||
|
the heading was redundant and pushed the stack past its budget).
|
||||||
- **Row 1 is always the Steam licence indicator** and nothing displaces it. It reads as status, not a
|
- **Row 1 is always the Steam licence indicator** and nothing displaces it. It reads as status, not a
|
||||||
warning: `Entitlement 30 fps · Maximum 60 fps`, both numbers derived from `QualityOptions` and
|
warning: `Steam ID Limit: 30fps` / `Click for More Info` (60fps once owned), both numbers derived from
|
||||||
`HasProOutput`. It shows a **pin** (standing indicator, no close control) while higher tiers are
|
`QualityOptions` and `HasProOutput`. It shows a **pin** (standing indicator, no close control) while higher tiers are
|
||||||
unavailable, and a **close control once ownership is verified** — that close is persisted
|
unavailable, and a **close control once ownership is verified** — that close is persisted
|
||||||
(`Steam.LicenceIndicatorDismissed`) and permanent *while ownership holds*; losing ownership restores
|
(`Steam.LicenceIndicatorDismissed`) and permanent *while ownership holds*; losing ownership restores
|
||||||
the row as pinned, because that is a change of state rather than a preference. Click → About.
|
the row as pinned, because that is a change of state rather than a preference. Click → About.
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ public sealed class AboutHubTests
|
|||||||
// The old red panel under the quality picker is gone (2026-11-05): the licence state
|
// The old red panel under the quality picker is gone (2026-11-05): the licence state
|
||||||
// lives in the left panel's alert stack now, as a pinned status indicator.
|
// lives in the left panel's alert stack now, as a pinned status indicator.
|
||||||
Assert.Null(window.FindName("EntitlementErrorPanel"));
|
Assert.Null(window.FindName("EntitlementErrorPanel"));
|
||||||
var licenceRow = vm.AttentionAlerts.Single(alert => alert.Label == "STEAM ID NOT PRESENT");
|
var licenceRow = vm.AttentionAlerts.Single(alert => alert.Id == "steam-licence");
|
||||||
Assert.True(licenceRow.IsPinned);
|
Assert.True(licenceRow.IsPinned);
|
||||||
Assert.Contains("Steam ID Limit: 30fps", licenceRow.Text);
|
Assert.Contains("Steam ID Limit: 30fps", licenceRow.Text);
|
||||||
Assert.Contains("Click for More Info", licenceRow.Text);
|
Assert.Contains("Click for More Info", licenceRow.Text);
|
||||||
|
|||||||
@@ -32,11 +32,13 @@ public sealed class AttentionAlertStackTests
|
|||||||
vm.EntitlementOverride = new FakeEntitlement(false, "Steam licensing is not configured in this build.");
|
vm.EntitlementOverride = new FakeEntitlement(false, "Steam licensing is not configured in this build.");
|
||||||
|
|
||||||
var row = Assert.Single(vm.AttentionAlerts);
|
var row = Assert.Single(vm.AttentionAlerts);
|
||||||
Assert.Equal("STEAM ID NOT PRESENT", row.Label);
|
Assert.Equal("steam-licence", row.Id);
|
||||||
Assert.True(row.IsPinned);
|
Assert.True(row.IsPinned);
|
||||||
Assert.True(row.ShowsPin);
|
Assert.True(row.ShowsPin);
|
||||||
Assert.False(row.CanDismiss);
|
Assert.False(row.CanDismiss);
|
||||||
Assert.Equal(AttentionAlertAction.About, row.Action);
|
Assert.Equal(AttentionAlertAction.About, row.Action);
|
||||||
|
// Two lines at most: the heading above the message was redundant.
|
||||||
|
Assert.DoesNotContain("\n\n", row.Text);
|
||||||
|
|
||||||
// Creator wording: the limit this build runs at, and where to find out why.
|
// Creator wording: the limit this build runs at, and where to find out why.
|
||||||
Assert.Contains("Steam ID Limit: 30fps", row.Text);
|
Assert.Contains("Steam ID Limit: 30fps", row.Text);
|
||||||
@@ -63,11 +65,11 @@ public sealed class AttentionAlertStackTests
|
|||||||
|
|
||||||
// Permanent: a fresh view-model on the same profile must not bring it back…
|
// Permanent: a fresh view-model on the same profile must not bring it back…
|
||||||
var reloaded = new MainViewModel();
|
var reloaded = new MainViewModel();
|
||||||
Assert.DoesNotContain(reloaded.AttentionAlerts, alert => alert.Label.StartsWith("STEAM ID"));
|
Assert.DoesNotContain(reloaded.AttentionAlerts, alert => alert.Id == "steam-licence");
|
||||||
|
|
||||||
// …but losing ownership restores it as a pinned indicator: a change of state, not a preference.
|
// …but losing ownership restores it as a pinned indicator: a change of state, not a preference.
|
||||||
reloaded.EntitlementOverride = new FakeEntitlement(false, "Steam licensing is not configured in this build.");
|
reloaded.EntitlementOverride = new FakeEntitlement(false, "Steam licensing is not configured in this build.");
|
||||||
var restored = reloaded.AttentionAlerts.First(alert => alert.Label == "STEAM ID NOT PRESENT");
|
var restored = reloaded.AttentionAlerts.First(alert => alert.Id == "steam-licence");
|
||||||
Assert.True(restored.IsPinned);
|
Assert.True(restored.IsPinned);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -90,22 +92,22 @@ public sealed class AttentionAlertStackTests
|
|||||||
WithVm(vm =>
|
WithVm(vm =>
|
||||||
{
|
{
|
||||||
vm.EntitlementOverride = new FakeEntitlement(false, "Steam licensing is not configured in this build.");
|
vm.EntitlementOverride = new FakeEntitlement(false, "Steam licensing is not configured in this build.");
|
||||||
vm.PushAttentionAlertForTest("drop-1", "STREAM", "Stream dropped — reconnecting (attempt 1 of 4)", 10, 12);
|
vm.PushAttentionAlertForTest("drop-1", "Stream dropped — reconnecting (attempt 1 of 4)", 10, 12);
|
||||||
vm.PushAttentionAlertForTest("drop-2", "STREAM", "Stream dropped — reconnecting (attempt 2 of 4)", 13, 15);
|
vm.PushAttentionAlertForTest("drop-2", "Stream dropped — reconnecting (attempt 2 of 4)", 13, 15);
|
||||||
|
|
||||||
Assert.Equal(new[] { "STEAM ID NOT PRESENT", "STREAM", "STREAM" }, vm.AttentionAlerts.Select(a => a.Label));
|
Assert.Equal(new[] { "steam-licence", "drop-2", "drop-1" }, vm.AttentionAlerts.Select(a => a.Id));
|
||||||
// Newest event sits directly under the indicator.
|
// Newest event sits directly under the indicator.
|
||||||
Assert.Contains("attempt 2", vm.AttentionAlerts[1].Text);
|
Assert.Contains("attempt 2", vm.AttentionAlerts[1].Text);
|
||||||
Assert.Contains("attempt 1", vm.AttentionAlerts[2].Text);
|
Assert.Contains("attempt 1", vm.AttentionAlerts[2].Text);
|
||||||
|
|
||||||
// The same event advancing updates its row instead of adding another.
|
// The same event advancing updates its row instead of adding another.
|
||||||
vm.PushAttentionAlertForTest("drop-2", "STREAM", "Stream recovered after 1.4 s", 13, 16);
|
vm.PushAttentionAlertForTest("drop-2", "Stream recovered after 1.4 s", 13, 16);
|
||||||
Assert.Equal(3, vm.AttentionAlerts.Count);
|
Assert.Equal(3, vm.AttentionAlerts.Count);
|
||||||
Assert.Contains("recovered", vm.AttentionAlerts[1].Text);
|
Assert.Contains("recovered", vm.AttentionAlerts[1].Text);
|
||||||
|
|
||||||
vm.DismissAlertCommand.Execute(vm.AttentionAlerts[1]);
|
vm.DismissAlertCommand.Execute(vm.AttentionAlerts[1]);
|
||||||
Assert.Equal(2, vm.AttentionAlerts.Count);
|
Assert.Equal(2, vm.AttentionAlerts.Count);
|
||||||
Assert.Equal("STEAM ID NOT PRESENT", vm.AttentionAlerts[0].Label);
|
Assert.Equal("steam-licence", vm.AttentionAlerts[0].Id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +116,7 @@ public sealed class AttentionAlertStackTests
|
|||||||
{
|
{
|
||||||
WithVm(vm =>
|
WithVm(vm =>
|
||||||
{
|
{
|
||||||
vm.PushAttentionAlertForTest("drop-1", "STREAM", "Stream dropped", 41, 47);
|
vm.PushAttentionAlertForTest("drop-1", "Stream dropped", 41, 47);
|
||||||
|
|
||||||
vm.ActivateAlertCommand.Execute(vm.AttentionAlerts[1]);
|
vm.ActivateAlertCommand.Execute(vm.AttentionAlerts[1]);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user