From 89cef25def27aa28155549e5d71bf3bf7027506f Mon Sep 17 00:00:00 2001 From: thepra Date: Tue, 6 Oct 2026 23:36:03 +0200 Subject: [PATCH] Nerd stats open with a tap On a touch screen a nerd-stats tooltip showed only through the browser faking hover, unreliably, and couldn't be scrolled. Where the pointer can't hover (AppStatusService.CanHover), a tap on a NerdTip now opens its rows in a sheet from the bottom; the trigger is focusable, says it opens a dialog, and is at least 44px on touch. A closed tooltip takes no room any more (daisyUI only made it transparent, and one past a card's edge made a phone's feed scroll sideways), and on touch it never shows. The rows are a component of their own (NerdRows), at 12px on phones. A card's More menu has a Details entry on phones that gathers the post's, its author's, its place's or CDN's and its counts' rows in one sheet. A picture's tip doesn't open on a tap, the picture does. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw --- Components/Content.razor | 36 +++++++++++++++++++- Components/Nerd/NerdRows.razor | 30 +++++++++++++++++ Components/Nerd/NerdTip.razor | 61 ++++++++++++++++++++++------------ Resources/AllStrings.it.resx | 3 ++ Resources/AllStrings.resx | 3 ++ Services/AppStatusService.cs | 3 ++ Styles/feed.css | 14 +++++++- wwwroot/main.js | 9 +++++ 8 files changed, 135 insertions(+), 24 deletions(-) create mode 100644 Components/Nerd/NerdRows.razor diff --git a/Components/Content.razor b/Components/Content.razor index 175830a..7828c01 100644 --- a/Components/Content.razor +++ b/Components/Content.razor @@ -325,6 +325,10 @@ @Localizer["Bookmark"] + + + @Localizer["Details"] + @if (status.Url is { Length: > 0 } url) { @@ -416,6 +420,29 @@ + @* a phone's way to the nerd stats the card's corners hold on hover, all in one sheet *@ + @if (isDetailsOpen) + { + + @Localizer["Details"] + +
+ + + @if (Post.Location is { Source: LocationSource.Cdn } detailsCdn) + { + + } + else if (Post.Location is not null) + { + + } + +
+
+
+ } @if (isReporting && status.Account is { } reported) { @@ -497,6 +524,7 @@ [Parameter] public FilterContext? FilterIn { get; set; } bool isReplying; + bool isDetailsOpen; bool isQuoting; bool isEditing; bool isActing; @@ -581,7 +609,7 @@
@foreach (var media in status.MediaAttachments) { - + @if ((status.Sensitive || IsBlurred) && !revealedMedia.Contains(media.Id)) {
+ +@code { + [Parameter] public string Heading { get; set; } + [Parameter] public IEnumerable Rows { get; set; } = []; + /// Shown under the rows while the stats being read on demand arrive. + [Parameter] public bool Pending { get; set; } + + protected override void OnInitialized() => IsLoading = false; +} diff --git a/Components/Nerd/NerdTip.razor b/Components/Nerd/NerdTip.razor index 8f10b56..f0e0914 100644 --- a/Components/Nerd/NerdTip.razor +++ b/Components/Nerd/NerdTip.razor @@ -1,10 +1,32 @@ @inherits LocalizableComponentBase -@* Nerd stats on hover or focus: a DTooltip whose content is a monospace list of label/value rows. OnOpen runs on the - first hover or focus, for stats read on demand (a post's provenance); rows with no value are left out. *@ - - @ChildContent - +@* Nerd stats on hover or focus: a DTooltip whose content is the rows (NerdRows). A touch screen has no hover, so there + a tap opens the same rows in a sheet from the bottom (TapOpens; not for a trigger that is a link itself, the + pictures). OnOpen runs on the first hover, focus or tap, for stats read on demand (a post's provenance). *@ +@if (TapOpens) +{ + @* (on touch the trigger is at least 44px: daisyUI's tooltip draws its arrow with ::after, so padding-free min sizes) *@ + + @ChildContent + +} +else +{ + + @ChildContent + +} +@if (isSheetOpen) +{ + + @Heading + + + + +} @code { [Parameter] public RenderFragment ChildContent { get; set; } @@ -16,8 +38,19 @@ [Parameter] public DTooltipPlacement Placement { get; set; } [Parameter] public DTooltipAlign Align { get; set; } [Parameter] public string Class { get; set; } + /// A tap opens the rows in a sheet where the screen has no hover. + [Parameter] public bool TapOpens { get; set; } = true; bool opened; + bool isSheetOpen; + + async Task Tap() + { + if (CascadingState.Status.CanHover()) + return; + isSheetOpen = true; + await Open(); + } protected override void OnInitialized() => IsLoading = false; @@ -29,21 +62,5 @@ await OnOpen.InvokeAsync(); } - RenderFragment Body => @
- @if (Heading is { Length: > 0 }) - { -

@Heading

- } -
- @foreach (var row in Rows.Where(row => !string.IsNullOrEmpty(row.Value))) - { -
@row.Label
-
@row.Value
- } -
- @if (Pending) - { -

@Localizer["loading"]…

- } -
; + RenderFragment Body => @; } diff --git a/Resources/AllStrings.it.resx b/Resources/AllStrings.it.resx index 89014ca..0137395 100644 --- a/Resources/AllStrings.it.resx +++ b/Resources/AllStrings.it.resx @@ -1856,4 +1856,7 @@ Feed + + Dettagli + diff --git a/Resources/AllStrings.resx b/Resources/AllStrings.resx index de4c92b..e1a785f 100644 --- a/Resources/AllStrings.resx +++ b/Resources/AllStrings.resx @@ -1856,4 +1856,7 @@ Feeds + + Details + diff --git a/Services/AppStatusService.cs b/Services/AppStatusService.cs index 125ab5f..8dcd26c 100644 --- a/Services/AppStatusService.cs +++ b/Services/AppStatusService.cs @@ -42,6 +42,9 @@ public class AppStatusService(IJSRuntime jsRuntime) public bool IsMobileMedia() => JsSyncRuntime.Invoke("isMobileMedia"); + // whether the pointer can hover (a mouse); a touch screen can't, so what shows on hover there opens on a tap + public bool CanHover() => JsSyncRuntime.Invoke("canHover"); + // a sideways-scrolling strip of tabs shows its selected tab public void RevealActive(ElementReference strip) => JsSyncRuntime.InvokeVoid("revealActive", strip); diff --git a/Styles/feed.css b/Styles/feed.css index 01dcae2..aee137a 100644 --- a/Styles/feed.css +++ b/Styles/feed.css @@ -62,13 +62,25 @@ padding-inline-start: 1.25rem; } - /* daisyUI's tooltip is a pill; a block of nerd stats needs the corners of a box. */ + /* daisyUI's tooltip is a pill; a block of nerd stats needs the corners of a box. Hidden, it takes no room (daisyUI + only makes it transparent, and one past a card's edge made a phone's feed scroll sideways), and on a touch screen + it never shows: a tap opens the rows in a sheet instead (NerdTip). */ .tooltip-content:has(> .nerd-stats) { border-radius: var(--radius-box); padding: 0.5rem 0.65rem; max-width: min(22rem, 90vw); } + .tooltip:not(:hover, :focus-within, .tooltip-open) > .tooltip-content:has(> .nerd-stats) { + display: none; + } + + @media (hover: none) { + .tooltip-content:has(> .nerd-stats) { + display: none; + } + } + .float-tooltip-kap { padding: 0 !important; background: transparent !important; diff --git a/wwwroot/main.js b/wwwroot/main.js index 97cd473..7db0b90 100644 --- a/wwwroot/main.js +++ b/wwwroot/main.js @@ -123,6 +123,15 @@ window.clearAppBadge = () => { window.logFromJs(e.message, "clearAppBadge") } } +window.canHover = () => { + try { + return window.matchMedia("(hover: hover)").matches + } catch (e) { + console.error(e) + window.logFromJs(e.message, "canHover") + return true + } +} // the selected tab of a sideways-scrolling strip, brought into its middle window.revealActive = strip => { try {