Fix sheet peek for async content, and rework several screens' details

showNooSheet's peek detection previously measured content once, right
after the first frame - Details' Versions/Activity tabs fetch their
data asynchronously, so that one-shot measurement only ever saw the
initial loading-spinner height, never the eventual (often long) list,
and the sheet still snapped to full height once data arrived. Replaced
it with a ScrollMetricsNotification-based check that keeps content
capped at the peek height from frame one and upgrades to a draggable
sheet whenever it actually overflows that cap, however and whenever
that happens - fixed for good this time, with a regression test that
mirrors the async-load shape.

Also, in the same pass:
- Upload/Move/Copy's bottom bar meta line now always reads as a count
  ("Uploading 3 items"), never a filename for the single-item case,
  and is centered rather than left-aligned - matches the approved
  design and removes a bespoke marquee/chip implementation that only
  Upload had.
- Upload/Move/Copy now show the same FilesControlsRow (sort, filter,
  List/Grid) Files itself uses, including a folder-grid rendering
  path for when List/Grid is set to grid.
- Files' filter chip reads "Filters" instead of "All types" when idle.
- Trash's overflow button - whose only action is "Delete forever" -
  is now a direct delete icon (mobile) / "Delete" danger pill
  (desktop) instead of a generic "..." menu that hid a single action
  behind an extra tap.
- The media viewer's floating chrome (top bar, bottom action bar,
  video transport row) now follows the app's light/dark theme instead
  of a fixed dark panel - a dark panel read as out of place over a
  light-theme stage. Panel tint and icon/text colors come from the
  theme's own surface/fg1 tokens (at a higher blur opacity to stay
  legible over arbitrary photo/video brightness), and the edge scrim
  gradients fade toward the panel's own tint instead of a fixed black.
  Mirrored the same change into the design canvas's Main.dc.html/
  MainAndroid.dc.html mockups (now with a theme prop) so they match.
This commit is contained in:
2026-09-28 19:26:18 -04:00
parent c0f702b00e
commit 0abea46b1c
12 changed files with 441 additions and 207 deletions
+4 -8
View File
@@ -40,11 +40,7 @@ class MediaActionBar extends StatelessWidget {
@override
Widget build(BuildContext context) {
// This panel always sits on a black media stage regardless of the
// app's own light/dark theme (see FrostedGlassContainer's doc
// comment) - fixed dark-palette values, not `context.nooColors`, so
// favorite/delete stay legible even when the ambient theme is light.
const dark = NooColors.dark;
final colors = context.nooColors;
// The blurred/tinted background lives outside the SafeArea (not inside
// it) so it extends all the way to the physical bottom edge, behind the
@@ -59,7 +55,7 @@ class MediaActionBar extends StatelessWidget {
children: [
if (transportControls != null) ...[
transportControls!,
Divider(height: 1, color: Colors.white.withValues(alpha: 0.14)),
Divider(height: 1, color: colors.fg1.withValues(alpha: 0.14)),
],
Padding(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 6),
@@ -80,7 +76,7 @@ class MediaActionBar extends StatelessWidget {
tooltip: isFavorite
? 'Remove from favorites'
: 'Favorite',
color: isFavorite ? dark.accentText : null,
color: isFavorite ? colors.accentText : null,
onTap: onFavorite,
),
ViewerIconButton(
@@ -98,7 +94,7 @@ class MediaActionBar extends StatelessWidget {
ViewerIconButton(
icon: LucideIcons.trash2,
tooltip: 'Delete',
color: dark.danger,
color: colors.danger,
onTap: isBusy ? null : onDelete,
),
ViewerIconButton(