mirror of
https://github.com/kuhyx/todo-app.git
synced 2026-11-10 20:07:02 +01:00
An app-bar toggle enters selection mode: a tap toggles a note (tint + check on grid tiles, ListTile.selected + check in the list), Ctrl/Cmd- click toggles and Shift-click range-selects on desktop, long-press outside the mode still opens the per-note sheet. While anything is selected an icon-only bottom bar sets status or priority for the whole selection in one bulkUpdate, assigns plain notes to a repo picked from the repos on notes or typed as owner/name, or clears the selection. A refused assign (note already in a repo) flashes a block icon. Labels are semantic only; menus pass an empty tooltip. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WzxdWXdsB7CQPNXVN829G1
79 lines
2.2 KiB
Dart
79 lines
2.2 KiB
Dart
/// One row of the notes list.
|
|
///
|
|
/// Split out of `notes_list_screen.dart` for file size; public only because
|
|
/// Dart privacy is library-scoped.
|
|
library;
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:todo/data/note.dart';
|
|
import 'package:todo/data/note_template.dart';
|
|
|
|
/// One row in the notes list: title, metadata line, and an actions button.
|
|
class NoteTile extends StatelessWidget {
|
|
/// Creates a tile for [note].
|
|
const NoteTile({
|
|
required this.note,
|
|
required this.onTap,
|
|
required this.onActions,
|
|
this.selected = false,
|
|
super.key,
|
|
});
|
|
|
|
/// Whether the row is part of the multi-selection: highlighted + a check.
|
|
final bool selected;
|
|
|
|
/// The note this row displays.
|
|
final Note note;
|
|
|
|
/// Open the full note for reading/editing.
|
|
/// Called when the row body is tapped (opens the note).
|
|
final VoidCallback onTap;
|
|
|
|
/// Open the quick-actions sheet (priority/status/delete).
|
|
/// Called when the trailing actions button is tapped.
|
|
final VoidCallback onActions;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final firstLine = noteTitle(note.text);
|
|
// Every note has a status and a priority now, so both are always shown.
|
|
final meta = <String>[
|
|
note.status.label,
|
|
note.priority.label,
|
|
'edited ${_relative(note.updatedAt)}',
|
|
?note.repoName,
|
|
].join(' · ');
|
|
return ListTile(
|
|
selected: selected,
|
|
leading: selected
|
|
? const Icon(Icons.check_circle, semanticLabel: 'Selected')
|
|
: null,
|
|
title: Text(
|
|
firstLine.isEmpty ? '(empty)' : firstLine,
|
|
maxLines: 2,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
subtitle: Text(meta),
|
|
trailing: IconButton(
|
|
icon: const Icon(Icons.more_vert),
|
|
tooltip: 'Quick actions',
|
|
onPressed: onActions,
|
|
),
|
|
onTap: onTap,
|
|
onLongPress: onActions,
|
|
);
|
|
}
|
|
|
|
/// Compact relative time like "2m ago" for the list subtitle.
|
|
String _relative(DateTime t) {
|
|
final d = DateTime.now().difference(t);
|
|
if (d.inMinutes < 1) return 'just now';
|
|
if (d.inHours < 1) return '${d.inMinutes}m ago';
|
|
if (d.inDays < 1) return '${d.inHours}h ago';
|
|
return '${d.inDays}d ago';
|
|
}
|
|
}
|
|
|
|
/// Bottom sheet for editing one note's priority/status or deleting it.
|