feat(app): a studyset's languages, and Chinese and Korean fonts

The studyset sheet gains Languages, as before: off until "Set
languages", then the language of the cards' fronts and of their backs
(22 languages, Chinese in its two scripts). A studyset without its own
takes those of the nearest studyset above it, both together; set for the
first time, they start from those it took. The payload held both fields
already.

They set its text: Noto Sans SC, TC and KR join Noto Sans JP (bundled,
never fetched; 34 MB more, fonts 42 MB in all, your call), and since
Han characters take the shapes of the first font that has them, every
screen inside a studyset (its screen, cards, notes, paths, studying it)
puts the font of its front's language first, else of its back's,
Japanese otherwise. Every studyset route goes through one helper now.

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:
Tizian 「ティツィアーン」 2026-10-08 23:30:31 +02:00
commit b1ef9b9d3a
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -27,6 +27,7 @@ import 'package:halkyon_learn_flutter/src/feature/study/model/study_focus.dart';
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_language_scope.dart';
import 'package:halkyon_learn_flutter/src/feature/studyset/widget/studyset_screen.dart';
import 'package:material_ui/material_ui.dart';
@ -144,11 +145,10 @@ GoRouter buildRouter({String initialLocation = RoutePaths.home}) => GoRouter(
builder: (context, state) {
final person = PersonScope.of(context);
final studysetId = state.pathParameters['studysetId']!;
return FoundById<Studyset>(
id: studysetId,
items: person.studysets.watchAll,
idOf: (studyset) => studyset.id,
builder: (studyset, _) => BlocProvider(
return _inStudyset(
context,
studysetId,
(studyset, _) => BlocProvider(
create: (context) => StudySessionBloc(
person.studysets,
person.cards,
@ -206,11 +206,10 @@ GoRouter buildRouter({String initialLocation = RoutePaths.home}) => GoRouter(
/// through; a studyset gone says so.
Widget _pathRunner(BuildContext context, String studysetId, String pathId) {
final person = PersonScope.of(context);
return FoundById<Studyset>(
id: studysetId,
items: person.studysets.watchAll,
idOf: (studyset) => studyset.id,
builder: (studyset, _) => BlocProvider(
return _inStudyset(
context,
studysetId,
(studyset, _) => BlocProvider(
create: (context) =>
PathRunnerBloc(person.paths, person.pathProgress, studysetId, pathId)
..add(const PathRunnerEventStarted()),
@ -223,11 +222,10 @@ Widget _pathRunner(BuildContext context, String studysetId, String pathId) {
/// Leaving closes its bloc, which saves what is unsaved.
Widget _pathEditor(BuildContext context, String studysetId, String pathId) {
final person = PersonScope.of(context);
return FoundById<Studyset>(
id: studysetId,
items: person.studysets.watchAll,
idOf: (studyset) => studyset.id,
builder: (studyset, _) => BlocProvider(
return _inStudyset(
context,
studysetId,
(studyset, _) => BlocProvider(
create: (context) => PathEditorBloc(
person.paths,
person.tags,
@ -249,11 +247,10 @@ Widget _stepEditor(
String stepId,
) {
final person = PersonScope.of(context);
return FoundById<Studyset>(
id: studysetId,
items: person.studysets.watchAll,
idOf: (studyset) => studyset.id,
builder: (studyset, _) => BlocProvider(
return _inStudyset(
context,
studysetId,
(studyset, _) => BlocProvider(
create: (context) =>
StepEditorBloc(person.paths, pathId, stepId)
..add(const StepEditorEventStarted()),
@ -262,15 +259,34 @@ Widget _stepEditor(
);
}
Widget _studysetScreen(BuildContext context, String id) => FoundById<Studyset>(
id: id,
items: PersonScope.of(context).studysets.watchAll,
Widget _studysetScreen(BuildContext context, String id) => _inStudyset(
context,
id,
(studyset, all) => StudysetScreen(studyset: studyset, all: all),
wayBack: (
label: AppLocalizations.of(context).studysetBackToLibrary,
location: RoutePaths.library,
),
);
/// The studyset [id] among the person's, for [builder]'s screen, its text
/// set for the studyset's languages ([StudysetLanguageScope]); a studyset
/// gone (deleted, or an old link) says so, leading back by [wayBack].
Widget _inStudyset(
BuildContext context,
String id,
Widget Function(Studyset studyset, List<Studyset> all) builder, {
({String label, String location})? wayBack,
}) => FoundById<Studyset>(
id: id,
items: PersonScope.of(context).studysets.watchAll,
idOf: (studyset) => studyset.id,
builder: (studyset, all) => StudysetScreen(studyset: studyset, all: all),
wayBack: wayBack,
builder: (studyset, all) => StudysetLanguageScope(
studyset: studyset,
all: all,
child: builder(studyset, all),
),
);
/// The note [noteId] of the studyset [studysetId], or a new one without; a
@ -278,11 +294,10 @@ Widget _studysetScreen(BuildContext context, String id) => FoundById<Studyset>(
/// its bloc, which saves what is unsaved.
Widget _noteEditor(BuildContext context, String studysetId, {String? noteId}) {
final person = PersonScope.of(context);
return FoundById<Studyset>(
id: studysetId,
items: person.studysets.watchAll,
idOf: (studyset) => studyset.id,
builder: (_, _) => BlocProvider(
return _inStudyset(
context,
studysetId,
(_, _) => BlocProvider(
create: (context) =>
NoteEditorBloc(person.notes, person.tags, studysetId, noteId: noteId)
..add(const NoteEditorEventStarted()),
@ -295,11 +310,10 @@ Widget _noteEditor(BuildContext context, String studysetId, {String? noteId}) {
/// 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(
return _inStudyset(
context,
studysetId,
(studyset, _) => BlocProvider(
create: (context) =>
CardComposerBloc(person.cards, person.tags, studysetId)
..add(const CardComposerEventStarted()),