feat(app): the card composer, for many cards in one sitting
Create flashcards, and tapping a card, open the composer: the stack of cards on the left (New flashcard on top, the new ones, then the stored ones; collapsible), the card being written on the right, saved as the person leaves it (another card, the pane, the screen, the app). The title says how saving stands; leaving with cards that couldn't be saved asks first. A card has its tags and their pin, an action menu (duplicate, clear, delete), the type switch (flashcard, vocabulary, matching), the front, and below a line its answer side: one answer or several to mark right or wrong, the reading and meanings, or categories and items. Fields changed differently on two devices show every version to keep one. The single-card editor goes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
a5515c13f7
commit
0afc22d454
1 changed files with 2701 additions and 735 deletions
|
|
@ -6,9 +6,8 @@ import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart';
|
|||
import 'package:halkyon_learn_flutter/src/core/router/tab_visibility.dart';
|
||||
import 'package:halkyon_learn_flutter/src/core/widget/found_by_id.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/auth/widget/profile_screen.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/card/bloc/card_edit_bloc.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/card/model/card.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/card/widget/card_screen.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/card/bloc/card_composer_bloc.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/card/widget/composer/card_composer_screen.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/explore/widget/explore_screen.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/home/widget/home_screen.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/settings/widget/settings_screen.dart';
|
||||
|
|
@ -17,8 +16,7 @@ import 'package:halkyon_learn_flutter/src/feature/study/widget/study_screen.dart
|
|||
import 'package:halkyon_learn_flutter/src/feature/studyset/model/studyset.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/studyset/widget/library_screen.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/studyset/widget/studyset_screen.dart';
|
||||
// Material's Card widget would clash with the domain's Card.
|
||||
import 'package:material_ui/material_ui.dart' hide Card;
|
||||
import 'package:material_ui/material_ui.dart';
|
||||
|
||||
/// The router of one person's app: the four tabs in a shell, each with its own
|
||||
/// stack of screens, and the focused surfaces over them. Every route is built
|
||||
|
|
@ -102,39 +100,21 @@ GoRouter buildRouter({String initialLocation = RoutePaths.home}) => GoRouter(
|
|||
);
|
||||
},
|
||||
),
|
||||
// Before the card route: its `new` would otherwise read as a card's id.
|
||||
// The composer: writing a studyset's cards, opened on a new card or on
|
||||
// one of them. Before the card route: its `new` would otherwise read as
|
||||
// a card's id.
|
||||
GoRoute(
|
||||
path: '/studysets/:studysetId/cards/new',
|
||||
builder: (context, state) => BlocProvider(
|
||||
create: (context) => CardEditBloc(
|
||||
PersonScope.of(context).cards,
|
||||
PersonScope.of(context).tags,
|
||||
state.pathParameters['studysetId']!,
|
||||
null,
|
||||
)..add(const CardEditEventStarted()),
|
||||
child: const CardScreen(card: null),
|
||||
),
|
||||
builder: (context, state) =>
|
||||
_composer(context, state.pathParameters['studysetId']!),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/studysets/:studysetId/cards/:cardId',
|
||||
builder: (context, state) {
|
||||
final person = PersonScope.of(context);
|
||||
final studysetId = state.pathParameters['studysetId']!;
|
||||
return FoundById<Card>(
|
||||
id: state.pathParameters['cardId']!,
|
||||
items: () => person.cards.watch(studysetId),
|
||||
idOf: (card) => card.id,
|
||||
builder: (card, _) => BlocProvider(
|
||||
create: (context) => CardEditBloc(
|
||||
person.cards,
|
||||
person.tags,
|
||||
studysetId,
|
||||
card,
|
||||
)..add(const CardEditEventStarted()),
|
||||
child: CardScreen(card: card),
|
||||
),
|
||||
);
|
||||
},
|
||||
builder: (context, state) => _composer(
|
||||
context,
|
||||
state.pathParameters['studysetId']!,
|
||||
cardId: state.pathParameters['cardId'],
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
path: RoutePaths.settings,
|
||||
|
|
@ -149,3 +129,23 @@ Widget _studysetScreen(BuildContext context, String id) => FoundById<Studyset>(
|
|||
idOf: (studyset) => studyset.id,
|
||||
builder: (studyset, all) => StudysetScreen(studyset: studyset, all: all),
|
||||
);
|
||||
|
||||
/// The composer of the studyset [studysetId]'s cards, opened on [cardId] if
|
||||
/// given; a studyset gone (deleted, or an old link) says so.
|
||||
Widget _composer(BuildContext context, String studysetId, {String? cardId}) {
|
||||
final person = PersonScope.of(context);
|
||||
return FoundById<Studyset>(
|
||||
id: studysetId,
|
||||
items: person.studysets.watchAll,
|
||||
idOf: (studyset) => studyset.id,
|
||||
builder: (studyset, _) => BlocProvider(
|
||||
create: (context) =>
|
||||
CardComposerBloc(person.cards, person.tags, studysetId)
|
||||
..add(const CardComposerEventStarted()),
|
||||
child: CardComposerScreen(
|
||||
studysetTitle: studyset.content.title,
|
||||
initialCardId: cardId,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue