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:
@@ -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(
|
||||
|
||||
@@ -161,7 +161,7 @@ class VideoTransportControls extends StatelessWidget {
|
||||
return AnimatedBuilder(
|
||||
animation: controller,
|
||||
builder: (context, _) {
|
||||
const fg = Colors.white;
|
||||
final fg = context.nooColors.fg1;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 4),
|
||||
child: Column(
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../theme/design_tokens.dart';
|
||||
|
||||
/// Icon-only action button for the media viewer's floating chrome (top bar,
|
||||
/// bottom action bar, video transport controls) - a fixed 22px icon in a
|
||||
/// round hit box. Colored white by default, not from [NooColors]/
|
||||
/// [ColorScheme]: this chrome always sits on the dark translucent panel
|
||||
/// over a black media stage (see `FrostedGlassContainer`'s doc comment),
|
||||
/// regardless of the app's light/dark theme, so a theme-derived color would
|
||||
/// go near-invisible in light mode. Kept as one small widget so every
|
||||
/// floating control in this screen looks the same instead of drifting
|
||||
/// apart across the three call sites that need one.
|
||||
/// round hit box. Colored from [NooColors.fg1] by default, matching
|
||||
/// [FrostedGlassContainer]'s own theme-following panel underneath it (both
|
||||
/// used to be fixed white-on-black regardless of theme - see their git
|
||||
/// history). Kept as one small widget so every floating control in this
|
||||
/// screen looks the same instead of drifting apart across the three call
|
||||
/// sites that need one.
|
||||
class ViewerIconButton extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final String tooltip;
|
||||
final VoidCallback? onTap;
|
||||
|
||||
/// Overrides the default white (e.g. `danger` for delete, `accent-text`
|
||||
/// for a favorited state). Ignored while [onTap] is null - a disabled
|
||||
/// button always dims to faded white regardless of its normal color.
|
||||
/// Overrides the default [NooColors.fg1] (e.g. `danger` for delete,
|
||||
/// `accent-text` for a favorited state). Ignored while [onTap] is null -
|
||||
/// a disabled button always dims regardless of its normal color.
|
||||
final Color? color;
|
||||
|
||||
const ViewerIconButton({
|
||||
@@ -29,9 +29,8 @@ class ViewerIconButton extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final fg = onTap == null
|
||||
? Colors.white.withValues(alpha: 0.4)
|
||||
: (color ?? Colors.white);
|
||||
final fg1 = context.nooColors.fg1;
|
||||
final fg = onTap == null ? fg1.withValues(alpha: 0.4) : (color ?? fg1);
|
||||
|
||||
return Tooltip(
|
||||
message: tooltip,
|
||||
|
||||
Reference in New Issue
Block a user