Files
noo/lib/views/account_view.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

143 lines
4.6 KiB
Dart

import 'package:flutter/material.dart';
import '../theme/design_tokens.dart';
import '../widgets/noo/nav/noo_top_bar.dart';
import '../widgets/noo/nav/noo_toolbar.dart';
import '../widgets/noo/noo_layout.dart';
import '../widgets/settings/settings_account_card.dart';
import '../widgets/settings/settings_accounts.dart';
import '../widgets/settings/settings_action_bar.dart';
import '../widgets/settings/settings_appearance.dart';
import '../widgets/settings/settings_file_sync.dart';
import '../widgets/settings/settings_files_cache.dart';
import '../widgets/settings/settings_security.dart';
import '../widgets/settings/settings_swipe.dart';
import '../widgets/settings/settings_tabs.dart';
/// Settings, pushed on top of the shell (DESIGN_SYSTEM.md 4's 9-section
/// order: account card, accounts, security, file sync, files cache,
/// appearance, tabs, action bar, swipe on a file). One column of
/// [SettingsSection]s (and [SettingsActionBarSection], which has no option
/// rows of its own to put in one - just the reorder list) on mobile, each
/// individually collapsible (expanded by default, tap its label to
/// collapse - see `SettingsSection`/`NooGroupedList`'s `collapsible` param)
/// so a long Settings screen can be collapsed down rather than needing a
/// separate jump rail (an earlier version had one; it added a second,
/// redundant way to navigate on top of plain scrolling and still didn't
/// shorten the page). Desktop uses a 2-column grid of cards instead, wide
/// enough to see most sections without scrolling, so it gets neither. See
/// each `widgets/settings/*.dart` file for a section's own content and any
/// setting that had to be slotted in or grouped under "Advanced appearance".
class AccountView extends StatelessWidget {
const AccountView({super.key});
static const _sections = <Widget>[
SettingsAccountCard(),
SettingsAccountsSection(),
SettingsSecuritySection(),
SettingsFileSyncSection(),
SettingsFilesCacheSection(),
SettingsAppearanceSection(),
SettingsTabsSection(),
SettingsActionBarSection(),
SettingsSwipeSection(),
];
@override
Widget build(BuildContext context) {
final colors = context.nooColors;
final desktop = NooLayout.isDesktop(context);
return Scaffold(
backgroundColor: colors.bg,
appBar: desktop
? const NooToolbar(title: 'Settings')
: NooTopBar(
style: NooLayout.navStyle(context),
title: 'Settings',
leading: const NooTopBarBack(),
),
body: SafeArea(
top: false,
child: desktop
? _DesktopGrid(sections: _sections)
: _MobileList(sections: _sections),
),
);
}
}
class _MobileList extends StatelessWidget {
final List<Widget> sections;
const _MobileList({required this.sections});
@override
Widget build(BuildContext context) {
return ListView(
padding: const EdgeInsets.fromLTRB(
NooSpace.sm,
NooSpace.sm,
NooSpace.sm,
NooSpace.xxl,
),
physics: const BouncingScrollPhysics(),
children: [
for (final section in sections) ...[
section,
const SizedBox(height: NooSpace.xl),
],
],
);
}
}
/// Splits the sections between two columns rather than a strict grid, since
/// each card's content height varies a lot (the tab reorder list and the
/// accounts list can both run much taller than, say, Security) - a fixed
/// grid would either clip content or leave large gaps.
class _DesktopGrid extends StatelessWidget {
final List<Widget> sections;
const _DesktopGrid({required this.sections});
@override
Widget build(BuildContext context) {
final left = <Widget>[];
final right = <Widget>[];
for (var i = 0; i < sections.length; i++) {
(i.isEven ? left : right).add(sections[i]);
}
return SingleChildScrollView(
padding: const EdgeInsets.all(NooSpace.xl),
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 1040),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(child: _Column(children: left)),
const SizedBox(width: NooSpace.xl),
Expanded(child: _Column(children: right)),
],
),
),
);
}
}
class _Column extends StatelessWidget {
final List<Widget> children;
const _Column({required this.children});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
for (final child in children) ...[
child,
const SizedBox(height: NooSpace.xl),
],
],
);
}
}