feat(app): More info for studysets, paths, notes and PDFs
Wherever the old menus offered it: the studyset's options, a path's (in the list and in the runner), a note's, and a PDF's, all on the shared sheet the cards use: when it was made (from its UUID v7 id) and last changed where that is kept, its ids to copy (parent, studyset, PDF), and for a PDF its file. 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
252110832a
commit
a35318a5a6
19 changed files with 332 additions and 40 deletions
|
|
@ -681,6 +681,8 @@
|
|||
"moreInfoItemId": "Element-ID",
|
||||
"moreInfoStudysetId": "Lernset-ID",
|
||||
"moreInfoPdfId": "PDF-Quellen-ID",
|
||||
"moreInfoParentId": "Übergeordnete ID",
|
||||
"moreInfoFile": "Datei",
|
||||
"moreInfoReviews": "Wiederholungen",
|
||||
"moreInfoReviewCount": "Anzahl Wiederholungen",
|
||||
"moreInfoLastReviewed": "Zuletzt wiederholt",
|
||||
|
|
|
|||
|
|
@ -764,6 +764,8 @@
|
|||
"moreInfoItemId": "Item ID",
|
||||
"moreInfoStudysetId": "Studyset ID",
|
||||
"moreInfoPdfId": "PDF source ID",
|
||||
"moreInfoParentId": "Parent ID",
|
||||
"moreInfoFile": "File",
|
||||
"moreInfoReviews": "Reviews",
|
||||
"moreInfoReviewCount": "Review count",
|
||||
"moreInfoLastReviewed": "Last reviewed",
|
||||
|
|
|
|||
|
|
@ -4066,6 +4066,18 @@ abstract class AppLocalizations {
|
|||
/// **'PDF source ID'**
|
||||
String get moreInfoPdfId;
|
||||
|
||||
/// No description provided for @moreInfoParentId.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Parent ID'**
|
||||
String get moreInfoParentId;
|
||||
|
||||
/// No description provided for @moreInfoFile.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'File'**
|
||||
String get moreInfoFile;
|
||||
|
||||
/// No description provided for @moreInfoReviews.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
|
|
|||
|
|
@ -2467,6 +2467,12 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
@override
|
||||
String get moreInfoPdfId => 'PDF-Quellen-ID';
|
||||
|
||||
@override
|
||||
String get moreInfoParentId => 'Übergeordnete ID';
|
||||
|
||||
@override
|
||||
String get moreInfoFile => 'Datei';
|
||||
|
||||
@override
|
||||
String get moreInfoReviews => 'Wiederholungen';
|
||||
|
||||
|
|
|
|||
|
|
@ -2448,6 +2448,12 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
@override
|
||||
String get moreInfoPdfId => 'PDF source ID';
|
||||
|
||||
@override
|
||||
String get moreInfoParentId => 'Parent ID';
|
||||
|
||||
@override
|
||||
String get moreInfoFile => 'File';
|
||||
|
||||
@override
|
||||
String get moreInfoReviews => 'Reviews';
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import 'package:flutter/services.dart';
|
|||
import 'package:halkyon_learn_flutter/l10n/app_localizations.dart';
|
||||
import 'package:halkyon_learn_flutter/src/core/widget/adaptive_sheet.dart';
|
||||
import 'package:halkyon_learn_flutter/src/core/widget/section_card.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:lucide_icons_flutter/lucide_icons.dart';
|
||||
import 'package:material_ui/material_ui.dart';
|
||||
|
||||
|
|
@ -12,6 +13,11 @@ typedef InfoRow = ({String label, String value, bool copyable});
|
|||
/// Facts that belong together, under a title if any.
|
||||
typedef InfoSection = ({String? title, List<InfoRow> rows});
|
||||
|
||||
/// [at] as a fact shows a moment: the date and the time of day, local.
|
||||
String infoTime(BuildContext context, DateTime at) => DateFormat.yMMMd(
|
||||
Localizations.localeOf(context).toLanguageTag(),
|
||||
).add_jm().format(at.toLocal());
|
||||
|
||||
/// Shows [sections] of facts about something, each in a card of its own:
|
||||
/// "More info".
|
||||
Future<void> showMoreInfoSheet(
|
||||
|
|
|
|||
|
|
@ -15,9 +15,6 @@ Future<void> showCardInfo(
|
|||
required String studysetId,
|
||||
}) async {
|
||||
final l10n = AppLocalizations.of(context);
|
||||
final format = DateFormat.yMMMd(
|
||||
Localizations.localeOf(context).toLanguageTag(),
|
||||
).add_jm();
|
||||
final ratings = [
|
||||
for (final review in await PersonScope.of(
|
||||
context,
|
||||
|
|
@ -27,7 +24,7 @@ Future<void> showCardInfo(
|
|||
if (!context.mounted) {
|
||||
return;
|
||||
}
|
||||
String when(DateTime at) => format.format(at.toLocal());
|
||||
String when(DateTime at) => infoTime(context, at);
|
||||
final created = uuidV7Time(card.id);
|
||||
// Revision ids sort by when they were written: the last is the latest.
|
||||
final modified = uuidV7Time((card.heads.toList()..sort()).last);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
import 'package:halkyon_learn_flutter/l10n/app_localizations.dart';
|
||||
import 'package:halkyon_learn_flutter/src/core/sync/uuid_time.dart';
|
||||
import 'package:halkyon_learn_flutter/src/core/widget/more_info_sheet.dart';
|
||||
import 'package:material_ui/material_ui.dart';
|
||||
|
||||
/// Shows what there is to know about the learning path [pathId] of the
|
||||
/// studyset [studysetId]: when it was made, and its ids. A path's content
|
||||
/// keeps no time of its last change.
|
||||
Future<void> showPathInfo(
|
||||
BuildContext context, {
|
||||
required String pathId,
|
||||
required String studysetId,
|
||||
}) {
|
||||
final l10n = AppLocalizations.of(context);
|
||||
final created = uuidV7Time(pathId);
|
||||
return showMoreInfoSheet(context, [
|
||||
(
|
||||
title: null,
|
||||
rows: [
|
||||
if (created != null)
|
||||
(
|
||||
label: l10n.moreInfoCreated,
|
||||
value: infoTime(context, created),
|
||||
copyable: false,
|
||||
),
|
||||
],
|
||||
),
|
||||
(
|
||||
title: null,
|
||||
rows: [
|
||||
(label: l10n.moreInfoItemId, value: pathId, copyable: true),
|
||||
(label: l10n.moreInfoStudysetId, value: studysetId, copyable: true),
|
||||
],
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:halkyon_learn_flutter/l10n/app_localizations.dart';
|
||||
|
|
@ -10,6 +12,7 @@ import 'package:halkyon_learn_flutter/src/core/widget/title_with_kicker.dart';
|
|||
import 'package:halkyon_learn_flutter/src/core/widget/undo_snack_bar.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/learning_path/bloc/path_list_bloc.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/learning_path/model/learning_path.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/learning_path/widget/path_info.dart';
|
||||
import 'package:lucide_icons_flutter/lucide_icons.dart';
|
||||
import 'package:material_ui/material_ui.dart';
|
||||
|
||||
|
|
@ -131,6 +134,13 @@ class const _PathTile({
|
|||
onPressed: () => _delete(context),
|
||||
child: Text(l10n.pathDelete, style: TextStyle(color: error)),
|
||||
),
|
||||
MenuItemButton(
|
||||
leadingIcon: const Icon(LucideIcons.info),
|
||||
onPressed: () => unawaited(
|
||||
showPathInfo(context, pathId: path.id, studysetId: studysetId),
|
||||
),
|
||||
child: Text(l10n.moreInfo),
|
||||
),
|
||||
],
|
||||
builder: (context, menu, _) => GestureDetector(
|
||||
onLongPressStart: (details) =>
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import 'package:halkyon_learn_flutter/src/core/widget/undo_snack_bar.dart';
|
|||
import 'package:halkyon_learn_flutter/src/feature/learning_path/bloc/path_runner_bloc.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/learning_path/model/learning_path_step.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/learning_path/widget/exercise_views.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/learning_path/widget/path_info.dart';
|
||||
import 'package:lucide_icons_flutter/lucide_icons.dart';
|
||||
import 'package:material_ui/material_ui.dart';
|
||||
|
||||
|
|
@ -51,7 +52,7 @@ class const PathRunnerScreen({
|
|||
RoutePaths.pathStep(studysetId, pathId, step.id),
|
||||
),
|
||||
),
|
||||
if (state.loaded) _PathMenu(pathId: pathId),
|
||||
if (state.loaded) _PathMenu(studysetId: studysetId, pathId: pathId),
|
||||
],
|
||||
),
|
||||
body: switch (state) {
|
||||
|
|
@ -73,8 +74,12 @@ class const PathRunnerScreen({
|
|||
}
|
||||
}
|
||||
|
||||
/// The path's options, behind the … button: start it afresh, or delete it.
|
||||
class const _PathMenu({required final String pathId}) extends StatelessWidget {
|
||||
/// The path's options, behind the … button: start it afresh, delete it, or
|
||||
/// see what there is to know about it.
|
||||
class const _PathMenu({
|
||||
required final String studysetId,
|
||||
required final String pathId,
|
||||
}) extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context);
|
||||
|
|
@ -91,6 +96,13 @@ class const _PathMenu({required final String pathId}) extends StatelessWidget {
|
|||
onPressed: () => unawaited(_delete(context)),
|
||||
child: Text(l10n.pathDelete, style: TextStyle(color: error)),
|
||||
),
|
||||
MenuItemButton(
|
||||
leadingIcon: const Icon(LucideIcons.info),
|
||||
onPressed: () => unawaited(
|
||||
showPathInfo(context, pathId: pathId, studysetId: studysetId),
|
||||
),
|
||||
child: Text(l10n.moreInfo),
|
||||
),
|
||||
],
|
||||
builder: (context, menu, _) => IconButton(
|
||||
icon: const Icon(LucideIcons.ellipsis),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
import 'package:halkyon_learn_flutter/l10n/app_localizations.dart';
|
||||
import 'package:halkyon_learn_flutter/src/core/sync/uuid_time.dart';
|
||||
import 'package:halkyon_learn_flutter/src/core/widget/more_info_sheet.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/note/model/note.dart';
|
||||
import 'package:material_ui/material_ui.dart';
|
||||
|
||||
/// Shows what there is to know about [note], in the studyset [studysetId]:
|
||||
/// when it was made and last changed, and its ids.
|
||||
Future<void> showNoteInfo(
|
||||
BuildContext context,
|
||||
Note note, {
|
||||
required String studysetId,
|
||||
}) {
|
||||
final l10n = AppLocalizations.of(context);
|
||||
final created = uuidV7Time(note.id);
|
||||
return showMoreInfoSheet(context, [
|
||||
(
|
||||
title: null,
|
||||
rows: [
|
||||
if (created != null)
|
||||
(
|
||||
label: l10n.moreInfoCreated,
|
||||
value: infoTime(context, created),
|
||||
copyable: false,
|
||||
),
|
||||
if (note.editedAt case final modified?)
|
||||
(
|
||||
label: l10n.moreInfoModified,
|
||||
value: infoTime(context, modified),
|
||||
copyable: false,
|
||||
),
|
||||
],
|
||||
),
|
||||
(
|
||||
title: null,
|
||||
rows: [
|
||||
(label: l10n.moreInfoItemId, value: note.id, copyable: true),
|
||||
(label: l10n.moreInfoStudysetId, value: studysetId, copyable: true),
|
||||
if (note.content.source case (:final pdfId, page: _))
|
||||
(label: l10n.moreInfoPdfId, value: pdfId, copyable: true),
|
||||
],
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:clock/clock.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
|
@ -10,6 +12,7 @@ import 'package:halkyon_learn_flutter/src/core/widget/undo_snack_bar.dart';
|
|||
import 'package:halkyon_learn_flutter/src/feature/note/bloc/note_list_bloc.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/note/model/note.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/note/widget/edited_ago.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/note/widget/note_info.dart';
|
||||
import 'package:lucide_icons_flutter/lucide_icons.dart';
|
||||
import 'package:material_ui/material_ui.dart';
|
||||
|
||||
|
|
@ -67,7 +70,8 @@ class const NoteList({required final String studysetId, super.key})
|
|||
}
|
||||
|
||||
/// One note: its title ("Untitled note" without one) and when it was last
|
||||
/// edited; its options behind the … button, or a long press.
|
||||
/// edited; its options (edit, delete, more info) behind the … button, or a
|
||||
/// long press.
|
||||
class const _NoteTile({
|
||||
required final String studysetId,
|
||||
required final Note note,
|
||||
|
|
@ -96,6 +100,12 @@ class const _NoteTile({
|
|||
style: TextStyle(color: Theme.of(context).colorScheme.error),
|
||||
),
|
||||
),
|
||||
MenuItemButton(
|
||||
leadingIcon: const Icon(LucideIcons.info),
|
||||
onPressed: () =>
|
||||
unawaited(showNoteInfo(context, note, studysetId: studysetId)),
|
||||
child: Text(l10n.moreInfo),
|
||||
),
|
||||
],
|
||||
builder: (context, menu, _) => ListTile(
|
||||
title: Text(
|
||||
|
|
|
|||
|
|
@ -7,11 +7,12 @@ import 'package:halkyon_learn_flutter/l10n/app_localizations.dart';
|
|||
import 'package:halkyon_learn_flutter/src/core/dependencies/dependencies_scope.dart';
|
||||
import 'package:halkyon_learn_flutter/src/core/dependencies/person_scope.dart';
|
||||
import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart';
|
||||
import 'package:halkyon_learn_flutter/src/core/sync/uuid_time.dart';
|
||||
import 'package:halkyon_learn_flutter/src/core/theme/theme_extras.dart';
|
||||
import 'package:halkyon_learn_flutter/src/core/widget/adaptive_sheet.dart';
|
||||
import 'package:halkyon_learn_flutter/src/core/widget/app_loading_indicator.dart';
|
||||
import 'package:halkyon_learn_flutter/src/core/widget/confirm_dialog.dart';
|
||||
import 'package:halkyon_learn_flutter/src/core/widget/content_counts_line.dart';
|
||||
import 'package:halkyon_learn_flutter/src/core/widget/more_info_sheet.dart';
|
||||
import 'package:halkyon_learn_flutter/src/core/widget/undo_snack_bar.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/pdf_source/bloc/pdf_list_bloc.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/pdf_source/data/pdf_engine.dart';
|
||||
|
|
@ -303,37 +304,60 @@ class const _PdfTile({
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> _moreInfo(BuildContext context) => showAdaptiveSheet<void>(
|
||||
context,
|
||||
builder: (context) {
|
||||
final l10n = AppLocalizations.of(context);
|
||||
final content = row.source.content;
|
||||
return SheetScaffold(
|
||||
title: content.title,
|
||||
body: Column(
|
||||
children: [
|
||||
ListTile(
|
||||
title: Text(l10n.pdfInfoFileName),
|
||||
subtitle: Text(content.fileName),
|
||||
/// What there is to know about the PDF: when it was added, its ids, and
|
||||
/// its file.
|
||||
Future<void> _moreInfo(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context);
|
||||
final source = row.source;
|
||||
final created = uuidV7Time(source.id);
|
||||
return showMoreInfoSheet(context, [
|
||||
(
|
||||
title: null,
|
||||
rows: [
|
||||
if (created != null)
|
||||
(
|
||||
label: l10n.moreInfoCreated,
|
||||
value: infoTime(context, created),
|
||||
copyable: false,
|
||||
),
|
||||
ListTile(
|
||||
title: Text(l10n.pdfInfoSize),
|
||||
subtitle: Text(switch (row.byteSize) {
|
||||
final size? => formatFileSize(l10n, size),
|
||||
null => l10n.pdfInfoUnknown,
|
||||
}),
|
||||
),
|
||||
ListTile(
|
||||
title: Text(l10n.pdfInfoPages),
|
||||
subtitle: Text(
|
||||
content.pageCount?.toString() ?? l10n.pdfInfoUnknown,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
],
|
||||
),
|
||||
(
|
||||
title: null,
|
||||
rows: [
|
||||
(label: l10n.moreInfoItemId, value: source.id, copyable: true),
|
||||
(
|
||||
label: l10n.moreInfoStudysetId,
|
||||
value: source.studysetId,
|
||||
copyable: true,
|
||||
),
|
||||
],
|
||||
),
|
||||
(
|
||||
title: l10n.moreInfoFile,
|
||||
rows: [
|
||||
(
|
||||
label: l10n.pdfInfoFileName,
|
||||
value: source.content.fileName,
|
||||
copyable: false,
|
||||
),
|
||||
(
|
||||
label: l10n.pdfInfoSize,
|
||||
value: switch (row.byteSize) {
|
||||
final size? => formatFileSize(l10n, size),
|
||||
null => l10n.pdfInfoUnknown,
|
||||
},
|
||||
copyable: false,
|
||||
),
|
||||
(
|
||||
label: l10n.pdfInfoPages,
|
||||
value: source.content.pageCount?.toString() ?? l10n.pdfInfoUnknown,
|
||||
copyable: false,
|
||||
),
|
||||
],
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
/// Asks, then hands the deletion to the person's PDF deletions and offers
|
||||
/// to undo it for a moment; the deletion outlives this tile.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
import 'package:halkyon_learn_flutter/l10n/app_localizations.dart';
|
||||
import 'package:halkyon_learn_flutter/src/core/sync/uuid_time.dart';
|
||||
import 'package:halkyon_learn_flutter/src/core/widget/more_info_sheet.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/studyset/model/studyset.dart';
|
||||
import 'package:material_ui/material_ui.dart';
|
||||
|
||||
/// Shows what there is to know about [studyset]: when it was made and last
|
||||
/// changed, its id and its parent's.
|
||||
Future<void> showStudysetInfo(BuildContext context, Studyset studyset) {
|
||||
final l10n = AppLocalizations.of(context);
|
||||
final created = uuidV7Time(studyset.id);
|
||||
return showMoreInfoSheet(context, [
|
||||
(
|
||||
title: null,
|
||||
rows: [
|
||||
if (created != null)
|
||||
(
|
||||
label: l10n.moreInfoCreated,
|
||||
value: infoTime(context, created),
|
||||
copyable: false,
|
||||
),
|
||||
if (studyset.updatedAt case final modified?)
|
||||
(
|
||||
label: l10n.moreInfoModified,
|
||||
value: infoTime(context, modified),
|
||||
copyable: false,
|
||||
),
|
||||
],
|
||||
),
|
||||
(
|
||||
title: null,
|
||||
rows: [
|
||||
(label: l10n.moreInfoItemId, value: studyset.id, copyable: true),
|
||||
if (studyset.parentId case final parentId?)
|
||||
(label: l10n.moreInfoParentId, value: parentId, copyable: true),
|
||||
],
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
|
@ -40,6 +40,7 @@ import 'package:halkyon_learn_flutter/src/feature/studyset/widget/delete_studyse
|
|||
import 'package:halkyon_learn_flutter/src/feature/studyset/widget/license_footer.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/studyset/widget/studyset_actions.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/studyset/widget/studyset_breadcrumb.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/studyset/widget/studyset_info.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/studyset/widget/studyset_rail.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/studyset/widget/studyset_sheet.dart';
|
||||
import 'package:lucide_icons_flutter/lucide_icons.dart';
|
||||
|
|
@ -1349,10 +1350,12 @@ enum _Option() {
|
|||
archive,
|
||||
unarchive,
|
||||
delete,
|
||||
info,
|
||||
}
|
||||
|
||||
/// The studyset's options: edit, move, its study options, import content,
|
||||
/// reset its progress, archive or unarchive, delete.
|
||||
/// reset its progress, archive or unarchive, delete, and what there is to
|
||||
/// know about it.
|
||||
class const _Options({
|
||||
required final Studyset studyset,
|
||||
required final List<Studyset> all,
|
||||
|
|
@ -1439,6 +1442,7 @@ class const _Options({
|
|||
studyset,
|
||||
onDeleted: () => _leave(context),
|
||||
),
|
||||
_Option.info => showStudysetInfo(context, studyset),
|
||||
},
|
||||
itemBuilder: (context) => [
|
||||
item(_Option.edit, LucideIcons.pencil, l10n.studysetMenuEdit),
|
||||
|
|
@ -1468,6 +1472,7 @@ class const _Options({
|
|||
l10n.studysetMenuDelete,
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
item(_Option.info, LucideIcons.info, l10n.moreInfo),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -409,6 +409,32 @@ void main() {
|
|||
expect(pathGateway.deletions, isEmpty);
|
||||
});
|
||||
|
||||
testWidgets("a path's More info, from its tile and from its runner, shows "
|
||||
'its ids', (tester) async {
|
||||
final paths = repository();
|
||||
final id = await paths.createPath(
|
||||
'japanese',
|
||||
LearningPathContent.titled('Particles'),
|
||||
);
|
||||
await paths.createStep(id, StepKind.info, step(StepKind.info, 'Read'));
|
||||
await showApp(tester, RoutePaths.studyset('japanese'));
|
||||
|
||||
await tester.tap(find.byTooltip('Path options'));
|
||||
await tester.pumpAndSettle();
|
||||
await chooseInMenu(tester, 'More info');
|
||||
expect(find.text(id), findsOneWidget);
|
||||
expect(find.text('japanese'), findsOneWidget);
|
||||
await tester.tap(find.byTooltip('Close'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.text('Particles'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.byTooltip('Path options'));
|
||||
await tester.pumpAndSettle();
|
||||
await chooseInMenu(tester, 'More info');
|
||||
expect(find.text(id), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets("a step's sources open from below its text, cited as the "
|
||||
'device cites, in the order of a reference list', (tester) async {
|
||||
final paths = repository();
|
||||
|
|
|
|||
|
|
@ -152,6 +152,25 @@ void main() {
|
|||
);
|
||||
});
|
||||
|
||||
testWidgets("a note's More info shows its ids, to copy", (
|
||||
tester,
|
||||
) async {
|
||||
final created = await stored('Particles', 'は');
|
||||
await showApp(tester, RoutePaths.studyset('japanese'));
|
||||
await openNotesTab(tester);
|
||||
|
||||
await tester.tap(find.byTooltip('Note options'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('More info'));
|
||||
await tester.pump();
|
||||
await tester.pump();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Item ID'), findsOneWidget);
|
||||
expect(find.text(created.noteId), findsOneWidget);
|
||||
expect(find.text('japanese'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('deleting asks, takes the note from the list, and Undo brings '
|
||||
'it back', (tester) async {
|
||||
await stored('Particles', 'は');
|
||||
|
|
|
|||
|
|
@ -219,6 +219,26 @@ void main() {
|
|||
expect(pdfGateway.deletions, isEmpty);
|
||||
});
|
||||
|
||||
testWidgets("a PDF's More info shows its ids and its file, as other "
|
||||
"things' More info does", (tester) async {
|
||||
await repository().import(
|
||||
'japanese',
|
||||
bytes: pdf('kana'),
|
||||
fileName: 'kana.pdf',
|
||||
);
|
||||
await showApp(tester);
|
||||
|
||||
await tester.tap(find.byTooltip('Actions'));
|
||||
await tester.pumpAndSettle();
|
||||
await choose(tester, 'More info');
|
||||
|
||||
expect(find.text('Item ID'), findsOneWidget);
|
||||
expect(find.text('japanese'), findsOneWidget);
|
||||
expect(find.text('File'), findsOneWidget);
|
||||
expect(find.text('kana.pdf'), findsOneWidget);
|
||||
expect(find.text('Pages'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('a deadline may be to finish a chapter, read from the PDF', (
|
||||
tester,
|
||||
) async {
|
||||
|
|
|
|||
|
|
@ -364,6 +364,22 @@ void main() {
|
|||
expect(find.text('Learning path'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets("a studyset's More info shows its id and its parent's, to "
|
||||
'copy', (tester) async {
|
||||
stored('japanese', 'Japanese');
|
||||
stored('kana', 'Kana', parent: 'japanese');
|
||||
await showApp(tester, location: RoutePaths.studyset('kana'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await choose(tester, 'More info');
|
||||
|
||||
expect(find.text('Item ID'), findsOneWidget);
|
||||
expect(find.text('kana'), findsOneWidget);
|
||||
expect(find.text('Parent ID'), findsOneWidget);
|
||||
expect(find.text('japanese'), findsOneWidget);
|
||||
expect(find.byTooltip('Copy'), findsNWidgets(2));
|
||||
});
|
||||
|
||||
testWidgets('the bibliography is a button above the content, not in the '
|
||||
'app bar; on a phone at the foot of the topics', (tester) async {
|
||||
stored('japanese', 'Japanese');
|
||||
|
|
|
|||
Loading…
Reference in a new issue