Files
noo/lib/widgets/settings/settings_section.dart
T
ayushyaandClaude Sonnet 5 f9653bc0f2 Fix share copy-link, destination-picker filters, and type-filter UX
- SharesController's fetchShares() built NextcloudShare objects through
  its own thin inline parser instead of _shareFromJson, so url/token/
  permissions/expireDate were always null - a public link share could
  never show "Copy link" in the Shares tab. Reuses _shareFromJson.
- Upload/Move/Copy's destination picker hid External storage in its
  filter sheet (showStorageScope: false) unlike Files' own controls
  row, despite the design doc already calling for full parity.
- Files' and Photos' type filter (All/Files/Folders, All/Photos/Videos)
  now render as one consistent style in both filter sheets: every icon
  always shown, label only on the selected segment
  (NooSegmentedControl's new labelOnlySelected), replacing Files' old
  checkmark list and Photos' own always-labelled track.
- Fixes Photos' type filter only visually updating after closing and
  reopening the filter sheet - it lived on PhotosView's own State, so
  Listenable.merge([photos, files]) never rebuilt the sheet when it
  changed; a StatefulBuilder now gives it that trigger.
- Replaces Settings' trailing section jump rail with per-section
  collapsible cards (NooGroupedList's new collapsible param, expanded
  by default) - one less parallel way to navigate a long screen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 03:49:24 -04:00

119 lines
4.0 KiB
Dart

import 'package:flutter/material.dart';
import '../../theme/design_tokens.dart';
import '../noo/lists/noo_grouped_list.dart';
import '../noo/noo_layout.dart';
import '../noo/overlays/noo_dialog.dart';
import '../noo/overlays/noo_sheet.dart';
/// One block of Settings (DESIGN_SYSTEM.md 4's 8-part order): a
/// [NooGroupedList] on mobile (label above a radius-20 card), or a titled,
/// bordered radius-20 card holding a flat row group on desktop
/// ("Mobile uses one column of grouped lists. Desktop uses a 2-column grid
/// of cards with a 1px line and radius 20."). Shared by every
/// `lib/widgets/settings/*.dart` section so both layouts stay in sync from
/// one place.
class SettingsSection extends StatelessWidget {
final String title;
final String? subtitle;
final List<Widget> children;
const SettingsSection({
super.key,
required this.title,
this.subtitle,
required this.children,
});
@override
Widget build(BuildContext context) {
final colors = context.nooColors;
if (!NooLayout.isDesktop(context)) {
return NooGroupedList(
label: title,
footer: subtitle != null ? Text(subtitle!) : null,
// Every mobile Settings section is individually collapsible,
// expanded by default - replaces the old trailing jump rail (see
// `account_view.dart`'s doc comment) as the way to navigate a long
// Settings screen quickly.
collapsible: true,
children: children,
);
}
return Container(
decoration: BoxDecoration(
color: colors.surface,
border: Border.all(color: colors.line),
borderRadius: BorderRadius.circular(NooRadii.card),
),
padding: const EdgeInsets.all(NooSpace.lg),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: NooText.cardTitle.copyWith(color: colors.fg1)),
if (subtitle != null) ...[
const SizedBox(height: 4),
Text(subtitle!, style: NooText.meta.copyWith(color: colors.fg3)),
],
const SizedBox(height: NooSpace.md),
FlatRowGroup(children: children),
],
),
);
}
}
/// The line-gapped row stack [NooGroupedList] draws internally, without its
/// own outer card/label - used for [SettingsSection]'s desktop card body,
/// which already supplies the surrounding card chrome, so nesting a second
/// radius-20 card there would double up the framing. Also reused directly
/// by `settings_tabs.dart`, whose desktop card mixes plain rows with a
/// reorderable list under one shared title/border instead of going through
/// [SettingsSection] itself.
class FlatRowGroup extends StatelessWidget {
final List<Widget> children;
const FlatRowGroup({super.key, required this.children});
@override
Widget build(BuildContext context) {
final colors = context.nooColors;
return ClipRRect(
borderRadius: BorderRadius.circular(NooRadii.input),
child: DecoratedBox(
decoration: BoxDecoration(color: colors.line),
child: Column(
children: [
for (var i = 0; i < children.length; i++) ...[
if (i > 0) const SizedBox(height: 1),
children[i],
],
],
),
),
);
}
}
/// The picker pattern for every "value + chevron" settings row (cache
/// policy, swipe actions, default tab...): a titled option list, a
/// [NooGroupedList] sheet on mobile or a [NooDialog] on desktop (mirrors
/// `files_controls_row.dart`'s sort/filter sheets). Each option row should
/// call `Navigator.pop(context)` itself after applying its choice.
void showSettingsPicker(
BuildContext context, {
required String title,
required List<Widget> options,
}) {
if (NooLayout.isDesktop(context)) {
showNooDialog(
context,
title: title,
children: [Column(children: options)],
);
} else {
showNooSheet(
context,
children: [NooGroupedList(label: title, children: options)],
);
}
}