feat(app): a note as one card, as before
A note is one card on the tinted ground: its title, its tags behind an Add tags chip, a line, then its text. The app bar says only how saving stands; its "Note in …" title goes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
4cd047f81a
commit
0d46c04a44
8 changed files with 123 additions and 119 deletions
|
|
@ -210,8 +210,6 @@
|
||||||
"noteDeleteMessage": "Diese Notiz wird aus dem Lernset entfernt. Du kannst dies für einige Sekunden rückgängig machen.",
|
"noteDeleteMessage": "Diese Notiz wird aus dem Lernset entfernt. Du kannst dies für einige Sekunden rückgängig machen.",
|
||||||
"noteDeleteConfirm": "Löschen",
|
"noteDeleteConfirm": "Löschen",
|
||||||
"noteDeleted": "Notiz gelöscht",
|
"noteDeleted": "Notiz gelöscht",
|
||||||
"noteNewTitleFor": "Neue Notiz in „{title}“",
|
|
||||||
"noteTitleFor": "Notiz in „{title}“",
|
|
||||||
"noteTitleHint": "Titel",
|
"noteTitleHint": "Titel",
|
||||||
"noteEditing": "Wird bearbeitet…",
|
"noteEditing": "Wird bearbeitet…",
|
||||||
"noteSaving": "Wird gespeichert…",
|
"noteSaving": "Wird gespeichert…",
|
||||||
|
|
@ -222,7 +220,7 @@
|
||||||
"noteFieldBody": "Text",
|
"noteFieldBody": "Text",
|
||||||
"noteKeepThis": "Diese behalten",
|
"noteKeepThis": "Diese behalten",
|
||||||
"noteAddTags": "Tags hinzufügen",
|
"noteAddTags": "Tags hinzufügen",
|
||||||
"noteTags": "Tags",
|
"noteEditTags": "Tags bearbeiten",
|
||||||
"studysetFlashcardsEmpty": "Noch keine Karteikarten",
|
"studysetFlashcardsEmpty": "Noch keine Karteikarten",
|
||||||
"studysetFlashcardsEmptyHint": "Füge welche hinzu, um mit dem Lernen zu beginnen.",
|
"studysetFlashcardsEmptyHint": "Füge welche hinzu, um mit dem Lernen zu beginnen.",
|
||||||
"studysetAddMaterial": "Material hinzufügen",
|
"studysetAddMaterial": "Material hinzufügen",
|
||||||
|
|
|
||||||
|
|
@ -232,10 +232,6 @@
|
||||||
"noteDeleteMessage": "This note will be removed from the studyset. You can undo this for a few seconds.",
|
"noteDeleteMessage": "This note will be removed from the studyset. You can undo this for a few seconds.",
|
||||||
"noteDeleteConfirm": "Delete",
|
"noteDeleteConfirm": "Delete",
|
||||||
"noteDeleted": "Note deleted",
|
"noteDeleted": "Note deleted",
|
||||||
"noteNewTitleFor": "New note in \"{title}\"",
|
|
||||||
"@noteNewTitleFor": {"placeholders": {"title": {"type": "String"}}},
|
|
||||||
"noteTitleFor": "Note in \"{title}\"",
|
|
||||||
"@noteTitleFor": {"placeholders": {"title": {"type": "String"}}},
|
|
||||||
"noteTitleHint": "Title",
|
"noteTitleHint": "Title",
|
||||||
"noteEditing": "Editing…",
|
"noteEditing": "Editing…",
|
||||||
"noteSaving": "Saving…",
|
"noteSaving": "Saving…",
|
||||||
|
|
@ -246,7 +242,7 @@
|
||||||
"noteFieldBody": "Text",
|
"noteFieldBody": "Text",
|
||||||
"noteKeepThis": "Keep this",
|
"noteKeepThis": "Keep this",
|
||||||
"noteAddTags": "Add tags",
|
"noteAddTags": "Add tags",
|
||||||
"noteTags": "Tags",
|
"noteEditTags": "Edit tags",
|
||||||
"studysetFlashcardsEmpty": "No flashcards yet",
|
"studysetFlashcardsEmpty": "No flashcards yet",
|
||||||
"studysetFlashcardsEmptyHint": "Add some to start studying.",
|
"studysetFlashcardsEmptyHint": "Add some to start studying.",
|
||||||
"studysetAddMaterial": "Add material",
|
"studysetAddMaterial": "Add material",
|
||||||
|
|
|
||||||
|
|
@ -1318,18 +1318,6 @@ abstract class AppLocalizations {
|
||||||
/// **'Note deleted'**
|
/// **'Note deleted'**
|
||||||
String get noteDeleted;
|
String get noteDeleted;
|
||||||
|
|
||||||
/// No description provided for @noteNewTitleFor.
|
|
||||||
///
|
|
||||||
/// In en, this message translates to:
|
|
||||||
/// **'New note in \"{title}\"'**
|
|
||||||
String noteNewTitleFor(String title);
|
|
||||||
|
|
||||||
/// No description provided for @noteTitleFor.
|
|
||||||
///
|
|
||||||
/// In en, this message translates to:
|
|
||||||
/// **'Note in \"{title}\"'**
|
|
||||||
String noteTitleFor(String title);
|
|
||||||
|
|
||||||
/// No description provided for @noteTitleHint.
|
/// No description provided for @noteTitleHint.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
|
|
@ -1390,11 +1378,11 @@ abstract class AppLocalizations {
|
||||||
/// **'Add tags'**
|
/// **'Add tags'**
|
||||||
String get noteAddTags;
|
String get noteAddTags;
|
||||||
|
|
||||||
/// No description provided for @noteTags.
|
/// No description provided for @noteEditTags.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Tags'**
|
/// **'Edit tags'**
|
||||||
String get noteTags;
|
String get noteEditTags;
|
||||||
|
|
||||||
/// No description provided for @studysetFlashcardsEmpty.
|
/// No description provided for @studysetFlashcardsEmpty.
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -775,16 +775,6 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get noteDeleted => 'Notiz gelöscht';
|
String get noteDeleted => 'Notiz gelöscht';
|
||||||
|
|
||||||
@override
|
|
||||||
String noteNewTitleFor(String title) {
|
|
||||||
return 'Neue Notiz in „$title“';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String noteTitleFor(String title) {
|
|
||||||
return 'Notiz in „$title“';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get noteTitleHint => 'Titel';
|
String get noteTitleHint => 'Titel';
|
||||||
|
|
||||||
|
|
@ -817,7 +807,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
String get noteAddTags => 'Tags hinzufügen';
|
String get noteAddTags => 'Tags hinzufügen';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get noteTags => 'Tags';
|
String get noteEditTags => 'Tags bearbeiten';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get studysetFlashcardsEmpty => 'Noch keine Karteikarten';
|
String get studysetFlashcardsEmpty => 'Noch keine Karteikarten';
|
||||||
|
|
|
||||||
|
|
@ -771,16 +771,6 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get noteDeleted => 'Note deleted';
|
String get noteDeleted => 'Note deleted';
|
||||||
|
|
||||||
@override
|
|
||||||
String noteNewTitleFor(String title) {
|
|
||||||
return 'New note in \"$title\"';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String noteTitleFor(String title) {
|
|
||||||
return 'Note in \"$title\"';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get noteTitleHint => 'Title';
|
String get noteTitleHint => 'Title';
|
||||||
|
|
||||||
|
|
@ -813,7 +803,7 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
String get noteAddTags => 'Add tags';
|
String get noteAddTags => 'Add tags';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get noteTags => 'Tags';
|
String get noteEditTags => 'Edit tags';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get studysetFlashcardsEmpty => 'No flashcards yet';
|
String get studysetFlashcardsEmpty => 'No flashcards yet';
|
||||||
|
|
|
||||||
|
|
@ -282,11 +282,11 @@ Widget _noteEditor(BuildContext context, String studysetId, {String? noteId}) {
|
||||||
id: studysetId,
|
id: studysetId,
|
||||||
items: person.studysets.watchAll,
|
items: person.studysets.watchAll,
|
||||||
idOf: (studyset) => studyset.id,
|
idOf: (studyset) => studyset.id,
|
||||||
builder: (studyset, _) => BlocProvider(
|
builder: (_, _) => BlocProvider(
|
||||||
create: (context) =>
|
create: (context) =>
|
||||||
NoteEditorBloc(person.notes, person.tags, studysetId, noteId: noteId)
|
NoteEditorBloc(person.notes, person.tags, studysetId, noteId: noteId)
|
||||||
..add(const NoteEditorEventStarted()),
|
..add(const NoteEditorEventStarted()),
|
||||||
child: NoteEditorScreen(studysetTitle: studyset.content.title),
|
child: const NoteEditorScreen(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import 'package:halkyon_learn_flutter/src/core/editor/rich_content_editor.dart';
|
||||||
import 'package:halkyon_learn_flutter/src/core/editor/rich_content_view.dart';
|
import 'package:halkyon_learn_flutter/src/core/editor/rich_content_view.dart';
|
||||||
import 'package:halkyon_learn_flutter/src/core/layout/readable_width.dart';
|
import 'package:halkyon_learn_flutter/src/core/layout/readable_width.dart';
|
||||||
import 'package:halkyon_learn_flutter/src/core/widget/app_loading_indicator.dart';
|
import 'package:halkyon_learn_flutter/src/core/widget/app_loading_indicator.dart';
|
||||||
|
import 'package:halkyon_learn_flutter/src/core/widget/section_card.dart';
|
||||||
import 'package:halkyon_learn_flutter/src/feature/note/bloc/note_editor_bloc.dart';
|
import 'package:halkyon_learn_flutter/src/feature/note/bloc/note_editor_bloc.dart';
|
||||||
import 'package:halkyon_learn_flutter/src/feature/note/model/note_content.dart';
|
import 'package:halkyon_learn_flutter/src/feature/note/model/note_content.dart';
|
||||||
import 'package:halkyon_learn_flutter/src/feature/tag/widget/tag_chip.dart';
|
import 'package:halkyon_learn_flutter/src/feature/tag/widget/tag_chip.dart';
|
||||||
|
|
@ -12,16 +13,13 @@ import 'package:halkyon_learn_flutter/src/feature/tag/widget/tag_picker_sheet.da
|
||||||
import 'package:lucide_icons_flutter/lucide_icons.dart';
|
import 'package:lucide_icons_flutter/lucide_icons.dart';
|
||||||
import 'package:material_ui/material_ui.dart';
|
import 'package:material_ui/material_ui.dart';
|
||||||
|
|
||||||
/// A note of the studyset [studysetTitle] in the whole window, over the
|
/// A note in the whole window, over the tabs: one card on the tinted
|
||||||
/// tabs: its title, its tags once it exists, and its text, with the format
|
/// ground holding its title, its tags once it exists, a line, and its text,
|
||||||
/// bar while writing; the app bar says how saving stands. It saves as the
|
/// with the format bar while writing; the app bar says only how saving
|
||||||
/// person goes, when the app goes to the background, and when they leave
|
/// stands. It saves as the person goes, when the app goes to the
|
||||||
/// (`NoteEditorBloc`). A note devices changed differently shows the
|
/// background, and when they leave (`NoteEditorBloc`). A note devices
|
||||||
/// versions to keep first.
|
/// changed differently shows the versions to keep first.
|
||||||
class const NoteEditorScreen({
|
class const NoteEditorScreen({super.key}) extends StatefulWidget {
|
||||||
required final String studysetTitle,
|
|
||||||
super.key,
|
|
||||||
}) extends StatefulWidget {
|
|
||||||
@override
|
@override
|
||||||
State<NoteEditorScreen> createState() => _NoteEditorScreenState();
|
State<NoteEditorScreen> createState() => _NoteEditorScreenState();
|
||||||
}
|
}
|
||||||
|
|
@ -62,14 +60,8 @@ class _NoteEditorScreenState() extends State<NoteEditorScreen> {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
builder: (context, state) => Scaffold(
|
builder: (context, state) => Scaffold(
|
||||||
appBar: AppBar(
|
backgroundColor: Theme.of(context).colorScheme.surfaceContainerLow,
|
||||||
title: Text(
|
appBar: AppBar(actions: [_SaveStatus(status: state.status)]),
|
||||||
state.noteId == null
|
|
||||||
? l10n.noteNewTitleFor(widget.studysetTitle)
|
|
||||||
: l10n.noteTitleFor(widget.studysetTitle),
|
|
||||||
),
|
|
||||||
actions: [_SaveStatus(status: state.status)],
|
|
||||||
),
|
|
||||||
body: switch (state) {
|
body: switch (state) {
|
||||||
NoteEditorState(loaded: false) => const Center(
|
NoteEditorState(loaded: false) => const Center(
|
||||||
child: AppLoadingIndicator(),
|
child: AppLoadingIndicator(),
|
||||||
|
|
@ -87,8 +79,8 @@ class _NoteEditorScreenState() extends State<NoteEditorScreen> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The note itself: the versions to keep while devices disagree, the title,
|
/// The note itself: the versions to keep while devices disagree, then one
|
||||||
/// the tags, and the text.
|
/// card of the title, the tags, a line and the text.
|
||||||
class const _Note({
|
class const _Note({
|
||||||
required final NoteEditorState state,
|
required final NoteEditorState state,
|
||||||
required final TextEditingController title,
|
required final TextEditingController title,
|
||||||
|
|
@ -98,59 +90,78 @@ class const _Note({
|
||||||
final l10n = AppLocalizations.of(context);
|
final l10n = AppLocalizations.of(context);
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final readOnly = state.unresolved.isNotEmpty;
|
final readOnly = state.unresolved.isNotEmpty;
|
||||||
return Align(
|
return Padding(
|
||||||
alignment: Alignment.topCenter,
|
padding: const EdgeInsets.fromLTRB(12, 8, 12, 12),
|
||||||
child: ConstrainedBox(
|
child: Align(
|
||||||
constraints: const BoxConstraints(maxWidth: readableContentMaxWidth),
|
alignment: Alignment.topCenter,
|
||||||
child: Column(
|
child: ConstrainedBox(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
constraints: const BoxConstraints(
|
||||||
children: [
|
maxWidth: readableContentMaxWidth,
|
||||||
if (readOnly) _Conflicts(state: state),
|
),
|
||||||
Padding(
|
child: Column(
|
||||||
padding: const EdgeInsets.fromLTRB(16, 16, 16, 0),
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
child: TextField(
|
children: [
|
||||||
controller: title,
|
if (readOnly) _Conflicts(state: state),
|
||||||
readOnly: readOnly,
|
Expanded(
|
||||||
style: theme.textTheme.headlineSmall,
|
child: SectionCard(
|
||||||
decoration: InputDecoration(
|
padding: EdgeInsets.zero,
|
||||||
hintText: l10n.noteTitleHint,
|
child: Column(
|
||||||
border: InputBorder.none,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
),
|
children: [
|
||||||
textInputAction: TextInputAction.next,
|
Padding(
|
||||||
onChanged: (text) => context.read<NoteEditorBloc>().add(
|
padding: const EdgeInsets.fromLTRB(16, 8, 16, 8),
|
||||||
NoteEditorEventTitleChanged(text),
|
child: TextField(
|
||||||
),
|
controller: title,
|
||||||
),
|
readOnly: readOnly,
|
||||||
),
|
style: theme.textTheme.headlineSmall,
|
||||||
if (state.noteId != null) _Tags(state: state),
|
decoration: InputDecoration(
|
||||||
Expanded(
|
hintText: l10n.noteTitleHint,
|
||||||
child: Padding(
|
border: InputBorder.none,
|
||||||
padding: const EdgeInsets.fromLTRB(16, 8, 16, 0),
|
),
|
||||||
child: readOnly
|
textInputAction: TextInputAction.next,
|
||||||
? SingleChildScrollView(
|
onChanged: (text) => context
|
||||||
child: RichContentView(content: state.content.body),
|
.read<NoteEditorBloc>()
|
||||||
)
|
.add(NoteEditorEventTitleChanged(text)),
|
||||||
: RichContentEditor(
|
|
||||||
// Shown anew whenever the text is replaced from
|
|
||||||
// outside: the editor reads its content once.
|
|
||||||
key: ValueKey(state.revision),
|
|
||||||
initialContent: state.content.body,
|
|
||||||
onChanged: (body) => context.read<NoteEditorBloc>().add(
|
|
||||||
NoteEditorEventBodyChanged(body),
|
|
||||||
),
|
),
|
||||||
autofocus: state.noteId == null,
|
|
||||||
formatBar: true,
|
|
||||||
),
|
),
|
||||||
|
if (state.noteId != null) _Tags(state: state),
|
||||||
|
const Divider(height: 1),
|
||||||
|
Expanded(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(16, 8, 16, 0),
|
||||||
|
child: readOnly
|
||||||
|
? SingleChildScrollView(
|
||||||
|
child: RichContentView(
|
||||||
|
content: state.content.body,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: RichContentEditor(
|
||||||
|
// Shown anew whenever the text is replaced
|
||||||
|
// from outside: the editor reads its
|
||||||
|
// content once.
|
||||||
|
key: ValueKey(state.revision),
|
||||||
|
initialContent: state.content.body,
|
||||||
|
onChanged: (body) => context
|
||||||
|
.read<NoteEditorBloc>()
|
||||||
|
.add(NoteEditorEventBodyChanged(body)),
|
||||||
|
autofocus: state.noteId == null,
|
||||||
|
formatBar: true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The note's tags as chips, and the way to choose them.
|
/// The note's tags as chips, and a chip to choose them.
|
||||||
class const _Tags({required final NoteEditorState state})
|
class const _Tags({required final NoteEditorState state})
|
||||||
extends StatelessWidget {
|
extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
|
|
@ -161,16 +172,16 @@ class const _Tags({required final NoteEditorState state})
|
||||||
if (state.tagIds.contains(tag.id)) tag,
|
if (state.tagIds.contains(tag.id)) tag,
|
||||||
];
|
];
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
padding: const EdgeInsets.fromLTRB(16, 0, 16, 8),
|
||||||
child: Wrap(
|
child: Wrap(
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
runSpacing: 4,
|
runSpacing: 8,
|
||||||
crossAxisAlignment: WrapCrossAlignment.center,
|
crossAxisAlignment: WrapCrossAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
for (final tag in chosen) TagChip(tag: tag),
|
for (final tag in chosen) TagChip(tag: tag),
|
||||||
TextButton.icon(
|
ActionChip(
|
||||||
icon: const Icon(LucideIcons.tag, size: 16),
|
avatar: const Icon(LucideIcons.pencil, size: 16),
|
||||||
label: Text(chosen.isEmpty ? l10n.noteAddTags : l10n.noteTags),
|
label: Text(chosen.isEmpty ? l10n.noteAddTags : l10n.noteEditTags),
|
||||||
onPressed: () => showTagPicker(
|
onPressed: () => showTagPicker(
|
||||||
context,
|
context,
|
||||||
selected: state.tagIds,
|
selected: state.tagIds,
|
||||||
|
|
@ -194,7 +205,7 @@ class const _Conflicts({required final NoteEditorState state})
|
||||||
final l10n = AppLocalizations.of(context);
|
final l10n = AppLocalizations.of(context);
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
return Card(
|
return Card(
|
||||||
margin: const EdgeInsets.fromLTRB(16, 16, 16, 0),
|
margin: const EdgeInsets.only(bottom: 8),
|
||||||
color: theme.colorScheme.errorContainer,
|
color: theme.colorScheme.errorContainer,
|
||||||
child: ConstrainedBox(
|
child: ConstrainedBox(
|
||||||
// Long versions scroll here, so the note stays in view below.
|
// Long versions scroll here, so the note stays in view below.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart';
|
import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart';
|
||||||
|
import 'package:halkyon_learn_flutter/src/core/editor/rich_content_editor.dart';
|
||||||
import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart';
|
import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart';
|
||||||
|
import 'package:halkyon_learn_flutter/src/core/widget/section_card.dart';
|
||||||
import 'package:halkyon_learn_flutter/src/feature/note/data/note_payload.dart';
|
import 'package:halkyon_learn_flutter/src/feature/note/data/note_payload.dart';
|
||||||
import 'package:halkyon_learn_flutter/src/feature/note/data/note_repository.dart';
|
import 'package:halkyon_learn_flutter/src/feature/note/data/note_repository.dart';
|
||||||
import 'package:halkyon_learn_flutter/src/feature/note/model/note_content.dart';
|
import 'package:halkyon_learn_flutter/src/feature/note/model/note_content.dart';
|
||||||
|
|
@ -109,7 +111,7 @@ void main() {
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
await tester.pump(const Duration(seconds: 1));
|
await tester.pump(const Duration(seconds: 1));
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
expect(find.text('New note in "Japanese"'), findsOneWidget);
|
expect(find.byType(RichContentEditor), findsOneWidget);
|
||||||
|
|
||||||
await tester.enterText(find.byType(TextField), 'Particles');
|
await tester.enterText(find.byType(TextField), 'Particles');
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
|
|
@ -121,6 +123,35 @@ void main() {
|
||||||
expect(noteGateway.written, hasLength(1));
|
expect(noteGateway.written, hasLength(1));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('a note is one card below a bare app bar: its title, its tags '
|
||||||
|
'behind Add tags, a line, then its text', (tester) async {
|
||||||
|
final created = await stored('Particles', 'は: topic');
|
||||||
|
await showApp(tester, RoutePaths.note('japanese', created.noteId));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
final card = find.byType(SectionCard);
|
||||||
|
expect(card, findsOneWidget);
|
||||||
|
for (final part in [
|
||||||
|
find.text('Particles'),
|
||||||
|
find.widgetWithText(ActionChip, 'Add tags'),
|
||||||
|
find.byType(Divider),
|
||||||
|
find.byType(RichContentEditor),
|
||||||
|
]) {
|
||||||
|
expect(find.descendant(of: card, matching: part), findsOneWidget);
|
||||||
|
}
|
||||||
|
final title = tester.getRect(find.text('Particles'));
|
||||||
|
final tags = tester.getRect(find.text('Add tags'));
|
||||||
|
final line = tester.getRect(find.byType(Divider));
|
||||||
|
final text = tester.getRect(find.byType(RichContentEditor));
|
||||||
|
expect(title.top, lessThan(tags.top));
|
||||||
|
expect(tags.bottom, lessThanOrEqualTo(line.top));
|
||||||
|
expect(line.bottom, lessThanOrEqualTo(text.top));
|
||||||
|
expect(
|
||||||
|
find.descendant(of: find.byType(AppBar), matching: find.byType(Text)),
|
||||||
|
findsNothing,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('deleting asks, takes the note from the list, and Undo brings '
|
testWidgets('deleting asks, takes the note from the list, and Undo brings '
|
||||||
'it back', (tester) async {
|
'it back', (tester) async {
|
||||||
await stored('Particles', 'は');
|
await stored('Particles', 'は');
|
||||||
|
|
@ -190,7 +221,7 @@ void main() {
|
||||||
) async {
|
) async {
|
||||||
final created = await stored('Particles', 'は');
|
final created = await stored('Particles', 'は');
|
||||||
await showApp(tester, RoutePaths.note('japanese', created.noteId));
|
await showApp(tester, RoutePaths.note('japanese', created.noteId));
|
||||||
expect(find.text('Note in "Japanese"'), findsOneWidget);
|
expect(find.text('Particles'), findsOneWidget);
|
||||||
|
|
||||||
noteGateway.receiveRemoteDeletion(created.noteId);
|
noteGateway.receiveRemoteDeletion(created.noteId);
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue