Rebuild UI on the Noo design system
Replaces the Material 3 screens with the Noo design system: warm neutrals, one violet accent, pill controls, Schibsted Grotesk/ Instrument Sans, Lucide icons, no gradients/shadows. - New lib/widgets/noo/ component kit (core, lists, files, media, nav, overlays) plus lib/theme/design_tokens.dart for the color/type/space/ radius/motion tokens. - Rebuilt the app shell (top/bottom bars, drawer, desktop sidebar and toolbar), every tab (Files/Offline, Photos, Favorites, Recent, Activity, Trash, Shares), Settings, the lock and login screens, the media viewer, search, the details/share sheets, and the share-upload and move/copy destination pickers. - Added a design canvas (linked from DESIGN_SYSTEM.md) covering the screens the spec didn't already describe, with matching Android and iOS chrome; wrote up the approved recipes into DESIGN_SYSTEM.md §4. - Removed now-dead legacy widgets (media_grid_tile, swipeable_item, sync_status_badge, selectable_thumbnail) and updated architecture.md/styling.md/standards.md to describe the new structure and component/testing conventions. - Added widget tests for the noo/ component kit. This is a UI-only rework: no provider/model/service behavior changed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lucide_icons_flutter/lucide_icons.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../providers/sync_status_controller.dart';
|
||||
import '../theme/design_tokens.dart';
|
||||
import 'noo/lists/noo_grouped_list.dart';
|
||||
import 'noo/lists/noo_settings_row.dart';
|
||||
import 'noo/overlays/noo_sheet.dart';
|
||||
|
||||
/// The configured sync targets (folders/files) with a "stop syncing"
|
||||
/// action per target - what used to be Settings' Device Sync card's own
|
||||
@@ -14,16 +19,7 @@ class ManageSyncedFoldersSheet extends StatelessWidget {
|
||||
|
||||
static void show(BuildContext context) {
|
||||
final sync = context.read<SyncStatusController>();
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||
showDragHandle: true,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(28)),
|
||||
),
|
||||
builder: (sheetContext) => ManageSyncedFoldersSheet(sync: sync),
|
||||
);
|
||||
showNooSheet(context, children: [ManageSyncedFoldersSheet(sync: sync)]);
|
||||
}
|
||||
|
||||
void _remove(BuildContext context, String path) {
|
||||
@@ -38,69 +34,52 @@ class ManageSyncedFoldersSheet extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colors = context.nooColors;
|
||||
// AnimatedBuilder, not a one-shot read: removing a target should update
|
||||
// this list in place without closing the sheet, and showNooSheet's
|
||||
// `children` is built once by the caller - this widget owns its own
|
||||
// rebuild instead.
|
||||
return AnimatedBuilder(
|
||||
animation: sync,
|
||||
builder: (context, _) {
|
||||
final theme = Theme.of(context);
|
||||
final colorScheme = theme.colorScheme;
|
||||
final everything = sync.syncEverything;
|
||||
final targets = sync.syncedPaths;
|
||||
|
||||
return SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(20, 4, 20, 20),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Synced to this device',
|
||||
style: theme.textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
if (everything)
|
||||
Text(
|
||||
'Every folder in this account is being synced to this '
|
||||
'device.',
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
)
|
||||
else if (targets.isEmpty)
|
||||
Text(
|
||||
'Nothing synced yet - select a folder or file in Files '
|
||||
'and use "Sync to device" to mirror it here for offline '
|
||||
'access.',
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
)
|
||||
else
|
||||
Flexible(
|
||||
child: ListView.separated(
|
||||
shrinkWrap: true,
|
||||
itemCount: targets.length,
|
||||
separatorBuilder: (_, _) => const Divider(height: 1),
|
||||
itemBuilder: (context, index) {
|
||||
final target = targets[index];
|
||||
return ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
leading: const Icon(Icons.sync_rounded),
|
||||
title: Text(target),
|
||||
trailing: IconButton(
|
||||
icon: const Icon(Icons.close_rounded),
|
||||
tooltip: 'Stop syncing',
|
||||
onPressed: () => _remove(context, target),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Synced to this device',
|
||||
style: NooText.cardTitle.copyWith(color: colors.fg1),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: NooSpace.sm),
|
||||
if (everything)
|
||||
Text(
|
||||
'Every folder in this account is being synced to this device.',
|
||||
style: NooText.body.copyWith(color: colors.fg3),
|
||||
)
|
||||
else if (targets.isEmpty)
|
||||
Text(
|
||||
'Nothing synced yet. Select a folder or file in Files and '
|
||||
'use "Sync to device" to mirror it here for offline access.',
|
||||
style: NooText.body.copyWith(color: colors.fg3),
|
||||
)
|
||||
else
|
||||
NooGroupedList(
|
||||
children: [
|
||||
for (final target in targets)
|
||||
NooSettingsRow(
|
||||
icon: LucideIcons.folderSync,
|
||||
label: Text(target),
|
||||
trailing: IconButton(
|
||||
icon: Icon(LucideIcons.x, size: 18, color: colors.fg3),
|
||||
tooltip: 'Stop syncing',
|
||||
onPressed: () => _remove(context, target),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user