feat(learn): a studyset's subject, picked from a fixed list (ADR 0041)
A top studyset is filed under one of 44 subjects in six groups (the developer's decisions of 9 and 10 Nov 2026): languages as one, computing in five, Other / not sure last. Each has a Lucide icon and a default colour, which picking it gives the studyset, its own to change from then on. The studyset's sheet picks it (pickSubject); a sub-studyset shows its top studyset's (subjectOf) and offers none. The payload keeps it as `subject` (schema 8), so archives and published studysets carry it; an ID this version doesn't know is kept and reads as none. Whats-New: A studyset can now be filed under a subject, which gives it that subject's colour. Whats-New-De: Ein Lernset lässt sich jetzt einem Fach zuordnen, das ihm dessen Farbe gibt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
a37ca123b1
commit
9868dc27c6
19 changed files with 1766 additions and 10 deletions
File diff suppressed because one or more lines are too long
|
|
@ -2112,5 +2112,58 @@
|
|||
"workStagePacking": "Packen",
|
||||
"importAborted": "Import abgebrochen: Nichts davon wurde behalten.",
|
||||
"exportAborted": "Export abgebrochen.",
|
||||
"exportProgressTitle": "Wird exportiert"
|
||||
"exportProgressTitle": "Wird exportiert",
|
||||
"subjectGroupSciences": "Naturwissenschaften und Gesundheit",
|
||||
"subjectGroupComputing": "Mathematik und Informatik",
|
||||
"subjectGroupSociety": "Gesellschaft",
|
||||
"subjectGroupArts": "Sprachen und Künste",
|
||||
"subjectGroupEveryday": "Alltag",
|
||||
"subjectGroupOther": "Sonstiges",
|
||||
"subjectBiology": "Biologie",
|
||||
"subjectChemistry": "Chemie",
|
||||
"subjectPhysics": "Physik",
|
||||
"subjectAstronomy": "Astronomie",
|
||||
"subjectEarthSciences": "Geowissenschaften",
|
||||
"subjectEnvironmentalScience": "Umweltwissenschaften",
|
||||
"subjectMedicine": "Medizin",
|
||||
"subjectNursing": "Pflege",
|
||||
"subjectPharmacy": "Pharmazie",
|
||||
"subjectNutrition": "Ernährung",
|
||||
"subjectPsychology": "Psychologie",
|
||||
"subjectSportsScience": "Sportwissenschaft",
|
||||
"subjectMathematics": "Mathematik",
|
||||
"subjectStatistics": "Statistik",
|
||||
"subjectComputerScience": "Informatik",
|
||||
"subjectSoftwareEngineering": "Softwareentwicklung",
|
||||
"subjectDataScienceAi": "Data Science und KI",
|
||||
"subjectItOperations": "IT und Betrieb",
|
||||
"subjectSecurityPrivacy": "Sicherheit und Datenschutz",
|
||||
"subjectEngineering": "Ingenieurwesen",
|
||||
"subjectArchitecture": "Architektur",
|
||||
"subjectHistory": "Geschichte",
|
||||
"subjectGeography": "Geografie",
|
||||
"subjectPolitics": "Politik",
|
||||
"subjectLaw": "Recht",
|
||||
"subjectEconomics": "Volkswirtschaft",
|
||||
"subjectBusiness": "Betriebswirtschaft",
|
||||
"subjectMarketing": "Marketing",
|
||||
"subjectAccountingFinance": "Rechnungswesen und Finanzen",
|
||||
"subjectSociology": "Soziologie",
|
||||
"subjectAnthropology": "Anthropologie",
|
||||
"subjectEducation": "Pädagogik",
|
||||
"subjectMediaStudies": "Medienwissenschaft",
|
||||
"subjectPhilosophy": "Philosophie",
|
||||
"subjectReligiousStudies": "Religionswissenschaft",
|
||||
"subjectLanguages": "Sprachen",
|
||||
"subjectLiterature": "Literatur",
|
||||
"subjectLinguistics": "Linguistik",
|
||||
"subjectArtDesign": "Kunst und Design",
|
||||
"subjectMusic": "Musik",
|
||||
"subjectTheatreFilm": "Theater und Film",
|
||||
"subjectCookingHospitality": "Kochen und Gastronomie",
|
||||
"subjectDrivingLicences": "Führerschein und Lizenzen",
|
||||
"subjectOther": "Sonstiges / unsicher",
|
||||
"studysetSheetSubject": "Fach",
|
||||
"studysetSheetSubjectNone": "Fach wählen",
|
||||
"subjectPickerTitle": "Fach"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2550,5 +2550,111 @@
|
|||
"exportAborted": "Export aborted.",
|
||||
"@exportAborted": {"description": "Snack bar after an export's Abort."},
|
||||
"exportProgressTitle": "Exporting",
|
||||
"@exportProgressTitle": {"description": "The sheet showing an export's progress."}
|
||||
"@exportProgressTitle": {"description": "The sheet showing an export's progress."},
|
||||
"subjectGroupSciences": "Sciences and health",
|
||||
"@subjectGroupSciences": {"description": "The group of subjects \"Sciences and health\" in the subject picker."},
|
||||
"subjectGroupComputing": "Mathematics and computing",
|
||||
"@subjectGroupComputing": {"description": "The group of subjects \"Mathematics and computing\" in the subject picker."},
|
||||
"subjectGroupSociety": "Society",
|
||||
"@subjectGroupSociety": {"description": "The group of subjects \"Society\" in the subject picker."},
|
||||
"subjectGroupArts": "Languages and arts",
|
||||
"@subjectGroupArts": {"description": "The group of subjects \"Languages and arts\" in the subject picker."},
|
||||
"subjectGroupEveryday": "Everyday",
|
||||
"@subjectGroupEveryday": {"description": "The group of subjects \"Everyday\" in the subject picker."},
|
||||
"subjectGroupOther": "Other",
|
||||
"@subjectGroupOther": {"description": "The group of subjects \"Other\" in the subject picker."},
|
||||
"subjectBiology": "Biology",
|
||||
"@subjectBiology": {"description": "The subject \"Biology\" a studyset is filed under."},
|
||||
"subjectChemistry": "Chemistry",
|
||||
"@subjectChemistry": {"description": "The subject \"Chemistry\" a studyset is filed under."},
|
||||
"subjectPhysics": "Physics",
|
||||
"@subjectPhysics": {"description": "The subject \"Physics\" a studyset is filed under."},
|
||||
"subjectAstronomy": "Astronomy",
|
||||
"@subjectAstronomy": {"description": "The subject \"Astronomy\" a studyset is filed under."},
|
||||
"subjectEarthSciences": "Earth sciences",
|
||||
"@subjectEarthSciences": {"description": "The subject \"Earth sciences\" a studyset is filed under."},
|
||||
"subjectEnvironmentalScience": "Environmental science",
|
||||
"@subjectEnvironmentalScience": {"description": "The subject \"Environmental science\" a studyset is filed under."},
|
||||
"subjectMedicine": "Medicine",
|
||||
"@subjectMedicine": {"description": "The subject \"Medicine\" a studyset is filed under."},
|
||||
"subjectNursing": "Nursing",
|
||||
"@subjectNursing": {"description": "The subject \"Nursing\" a studyset is filed under."},
|
||||
"subjectPharmacy": "Pharmacy",
|
||||
"@subjectPharmacy": {"description": "The subject \"Pharmacy\" a studyset is filed under."},
|
||||
"subjectNutrition": "Nutrition",
|
||||
"@subjectNutrition": {"description": "The subject \"Nutrition\" a studyset is filed under."},
|
||||
"subjectPsychology": "Psychology",
|
||||
"@subjectPsychology": {"description": "The subject \"Psychology\" a studyset is filed under."},
|
||||
"subjectSportsScience": "Sports science",
|
||||
"@subjectSportsScience": {"description": "The subject \"Sports science\" a studyset is filed under."},
|
||||
"subjectMathematics": "Mathematics",
|
||||
"@subjectMathematics": {"description": "The subject \"Mathematics\" a studyset is filed under."},
|
||||
"subjectStatistics": "Statistics",
|
||||
"@subjectStatistics": {"description": "The subject \"Statistics\" a studyset is filed under."},
|
||||
"subjectComputerScience": "Computer science",
|
||||
"@subjectComputerScience": {"description": "The subject \"Computer science\" a studyset is filed under."},
|
||||
"subjectSoftwareEngineering": "Software engineering",
|
||||
"@subjectSoftwareEngineering": {"description": "The subject \"Software engineering\" a studyset is filed under."},
|
||||
"subjectDataScienceAi": "Data science and AI",
|
||||
"@subjectDataScienceAi": {"description": "The subject \"Data science and AI\" a studyset is filed under."},
|
||||
"subjectItOperations": "IT and operations",
|
||||
"@subjectItOperations": {"description": "The subject \"IT and operations\" a studyset is filed under."},
|
||||
"subjectSecurityPrivacy": "Security and privacy",
|
||||
"@subjectSecurityPrivacy": {"description": "The subject \"Security and privacy\" a studyset is filed under."},
|
||||
"subjectEngineering": "Engineering",
|
||||
"@subjectEngineering": {"description": "The subject \"Engineering\" a studyset is filed under."},
|
||||
"subjectArchitecture": "Architecture",
|
||||
"@subjectArchitecture": {"description": "The subject \"Architecture\" a studyset is filed under."},
|
||||
"subjectHistory": "History",
|
||||
"@subjectHistory": {"description": "The subject \"History\" a studyset is filed under."},
|
||||
"subjectGeography": "Geography",
|
||||
"@subjectGeography": {"description": "The subject \"Geography\" a studyset is filed under."},
|
||||
"subjectPolitics": "Politics",
|
||||
"@subjectPolitics": {"description": "The subject \"Politics\" a studyset is filed under."},
|
||||
"subjectLaw": "Law",
|
||||
"@subjectLaw": {"description": "The subject \"Law\" a studyset is filed under."},
|
||||
"subjectEconomics": "Economics",
|
||||
"@subjectEconomics": {"description": "The subject \"Economics\" a studyset is filed under."},
|
||||
"subjectBusiness": "Business",
|
||||
"@subjectBusiness": {"description": "The subject \"Business\" a studyset is filed under."},
|
||||
"subjectMarketing": "Marketing",
|
||||
"@subjectMarketing": {"description": "The subject \"Marketing\" a studyset is filed under."},
|
||||
"subjectAccountingFinance": "Accounting and finance",
|
||||
"@subjectAccountingFinance": {"description": "The subject \"Accounting and finance\" a studyset is filed under."},
|
||||
"subjectSociology": "Sociology",
|
||||
"@subjectSociology": {"description": "The subject \"Sociology\" a studyset is filed under."},
|
||||
"subjectAnthropology": "Anthropology",
|
||||
"@subjectAnthropology": {"description": "The subject \"Anthropology\" a studyset is filed under."},
|
||||
"subjectEducation": "Education",
|
||||
"@subjectEducation": {"description": "The subject \"Education\" a studyset is filed under."},
|
||||
"subjectMediaStudies": "Media studies",
|
||||
"@subjectMediaStudies": {"description": "The subject \"Media studies\" a studyset is filed under."},
|
||||
"subjectPhilosophy": "Philosophy",
|
||||
"@subjectPhilosophy": {"description": "The subject \"Philosophy\" a studyset is filed under."},
|
||||
"subjectReligiousStudies": "Religious studies",
|
||||
"@subjectReligiousStudies": {"description": "The subject \"Religious studies\" a studyset is filed under."},
|
||||
"subjectLanguages": "Languages",
|
||||
"@subjectLanguages": {"description": "The subject \"Languages\" a studyset is filed under."},
|
||||
"subjectLiterature": "Literature",
|
||||
"@subjectLiterature": {"description": "The subject \"Literature\" a studyset is filed under."},
|
||||
"subjectLinguistics": "Linguistics",
|
||||
"@subjectLinguistics": {"description": "The subject \"Linguistics\" a studyset is filed under."},
|
||||
"subjectArtDesign": "Art and design",
|
||||
"@subjectArtDesign": {"description": "The subject \"Art and design\" a studyset is filed under."},
|
||||
"subjectMusic": "Music",
|
||||
"@subjectMusic": {"description": "The subject \"Music\" a studyset is filed under."},
|
||||
"subjectTheatreFilm": "Theatre and film",
|
||||
"@subjectTheatreFilm": {"description": "The subject \"Theatre and film\" a studyset is filed under."},
|
||||
"subjectCookingHospitality": "Cooking and hospitality",
|
||||
"@subjectCookingHospitality": {"description": "The subject \"Cooking and hospitality\" a studyset is filed under."},
|
||||
"subjectDrivingLicences": "Driving and licences",
|
||||
"@subjectDrivingLicences": {"description": "The subject \"Driving and licences\" a studyset is filed under."},
|
||||
"subjectOther": "Other / not sure",
|
||||
"@subjectOther": {"description": "The subject \"Other / not sure\" a studyset is filed under."},
|
||||
"studysetSheetSubject": "Subject",
|
||||
"@studysetSheetSubject": {"description": "The label of the subject field in the studyset sheet."},
|
||||
"studysetSheetSubjectNone": "Choose a subject",
|
||||
"@studysetSheetSubjectNone": {"description": "The subject field before one is chosen."},
|
||||
"subjectPickerTitle": "Subject",
|
||||
"@subjectPickerTitle": {"description": "The title of the sheet picking a studyset's subject."}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12457,6 +12457,324 @@ abstract class AppLocalizations {
|
|||
/// In en, this message translates to:
|
||||
/// **'Exporting'**
|
||||
String get exportProgressTitle;
|
||||
|
||||
/// The group of subjects "Sciences and health" in the subject picker.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Sciences and health'**
|
||||
String get subjectGroupSciences;
|
||||
|
||||
/// The group of subjects "Mathematics and computing" in the subject picker.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Mathematics and computing'**
|
||||
String get subjectGroupComputing;
|
||||
|
||||
/// The group of subjects "Society" in the subject picker.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Society'**
|
||||
String get subjectGroupSociety;
|
||||
|
||||
/// The group of subjects "Languages and arts" in the subject picker.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Languages and arts'**
|
||||
String get subjectGroupArts;
|
||||
|
||||
/// The group of subjects "Everyday" in the subject picker.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Everyday'**
|
||||
String get subjectGroupEveryday;
|
||||
|
||||
/// The group of subjects "Other" in the subject picker.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Other'**
|
||||
String get subjectGroupOther;
|
||||
|
||||
/// The subject "Biology" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Biology'**
|
||||
String get subjectBiology;
|
||||
|
||||
/// The subject "Chemistry" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Chemistry'**
|
||||
String get subjectChemistry;
|
||||
|
||||
/// The subject "Physics" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Physics'**
|
||||
String get subjectPhysics;
|
||||
|
||||
/// The subject "Astronomy" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Astronomy'**
|
||||
String get subjectAstronomy;
|
||||
|
||||
/// The subject "Earth sciences" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Earth sciences'**
|
||||
String get subjectEarthSciences;
|
||||
|
||||
/// The subject "Environmental science" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Environmental science'**
|
||||
String get subjectEnvironmentalScience;
|
||||
|
||||
/// The subject "Medicine" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Medicine'**
|
||||
String get subjectMedicine;
|
||||
|
||||
/// The subject "Nursing" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Nursing'**
|
||||
String get subjectNursing;
|
||||
|
||||
/// The subject "Pharmacy" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Pharmacy'**
|
||||
String get subjectPharmacy;
|
||||
|
||||
/// The subject "Nutrition" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Nutrition'**
|
||||
String get subjectNutrition;
|
||||
|
||||
/// The subject "Psychology" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Psychology'**
|
||||
String get subjectPsychology;
|
||||
|
||||
/// The subject "Sports science" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Sports science'**
|
||||
String get subjectSportsScience;
|
||||
|
||||
/// The subject "Mathematics" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Mathematics'**
|
||||
String get subjectMathematics;
|
||||
|
||||
/// The subject "Statistics" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Statistics'**
|
||||
String get subjectStatistics;
|
||||
|
||||
/// The subject "Computer science" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Computer science'**
|
||||
String get subjectComputerScience;
|
||||
|
||||
/// The subject "Software engineering" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Software engineering'**
|
||||
String get subjectSoftwareEngineering;
|
||||
|
||||
/// The subject "Data science and AI" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Data science and AI'**
|
||||
String get subjectDataScienceAi;
|
||||
|
||||
/// The subject "IT and operations" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'IT and operations'**
|
||||
String get subjectItOperations;
|
||||
|
||||
/// The subject "Security and privacy" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Security and privacy'**
|
||||
String get subjectSecurityPrivacy;
|
||||
|
||||
/// The subject "Engineering" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Engineering'**
|
||||
String get subjectEngineering;
|
||||
|
||||
/// The subject "Architecture" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Architecture'**
|
||||
String get subjectArchitecture;
|
||||
|
||||
/// The subject "History" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'History'**
|
||||
String get subjectHistory;
|
||||
|
||||
/// The subject "Geography" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Geography'**
|
||||
String get subjectGeography;
|
||||
|
||||
/// The subject "Politics" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Politics'**
|
||||
String get subjectPolitics;
|
||||
|
||||
/// The subject "Law" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Law'**
|
||||
String get subjectLaw;
|
||||
|
||||
/// The subject "Economics" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Economics'**
|
||||
String get subjectEconomics;
|
||||
|
||||
/// The subject "Business" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Business'**
|
||||
String get subjectBusiness;
|
||||
|
||||
/// The subject "Marketing" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Marketing'**
|
||||
String get subjectMarketing;
|
||||
|
||||
/// The subject "Accounting and finance" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Accounting and finance'**
|
||||
String get subjectAccountingFinance;
|
||||
|
||||
/// The subject "Sociology" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Sociology'**
|
||||
String get subjectSociology;
|
||||
|
||||
/// The subject "Anthropology" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Anthropology'**
|
||||
String get subjectAnthropology;
|
||||
|
||||
/// The subject "Education" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Education'**
|
||||
String get subjectEducation;
|
||||
|
||||
/// The subject "Media studies" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Media studies'**
|
||||
String get subjectMediaStudies;
|
||||
|
||||
/// The subject "Philosophy" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Philosophy'**
|
||||
String get subjectPhilosophy;
|
||||
|
||||
/// The subject "Religious studies" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Religious studies'**
|
||||
String get subjectReligiousStudies;
|
||||
|
||||
/// The subject "Languages" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Languages'**
|
||||
String get subjectLanguages;
|
||||
|
||||
/// The subject "Literature" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Literature'**
|
||||
String get subjectLiterature;
|
||||
|
||||
/// The subject "Linguistics" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Linguistics'**
|
||||
String get subjectLinguistics;
|
||||
|
||||
/// The subject "Art and design" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Art and design'**
|
||||
String get subjectArtDesign;
|
||||
|
||||
/// The subject "Music" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Music'**
|
||||
String get subjectMusic;
|
||||
|
||||
/// The subject "Theatre and film" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Theatre and film'**
|
||||
String get subjectTheatreFilm;
|
||||
|
||||
/// The subject "Cooking and hospitality" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Cooking and hospitality'**
|
||||
String get subjectCookingHospitality;
|
||||
|
||||
/// The subject "Driving and licences" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Driving and licences'**
|
||||
String get subjectDrivingLicences;
|
||||
|
||||
/// The subject "Other / not sure" a studyset is filed under.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Other / not sure'**
|
||||
String get subjectOther;
|
||||
|
||||
/// The label of the subject field in the studyset sheet.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Subject'**
|
||||
String get studysetSheetSubject;
|
||||
|
||||
/// The subject field before one is chosen.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Choose a subject'**
|
||||
String get studysetSheetSubjectNone;
|
||||
|
||||
/// The title of the sheet picking a studyset's subject.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Subject'**
|
||||
String get subjectPickerTitle;
|
||||
}
|
||||
|
||||
class _AppLocalizationsDelegate
|
||||
|
|
|
|||
|
|
@ -8011,4 +8011,163 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get exportProgressTitle => 'Wird exportiert';
|
||||
|
||||
@override
|
||||
String get subjectGroupSciences => 'Naturwissenschaften und Gesundheit';
|
||||
|
||||
@override
|
||||
String get subjectGroupComputing => 'Mathematik und Informatik';
|
||||
|
||||
@override
|
||||
String get subjectGroupSociety => 'Gesellschaft';
|
||||
|
||||
@override
|
||||
String get subjectGroupArts => 'Sprachen und Künste';
|
||||
|
||||
@override
|
||||
String get subjectGroupEveryday => 'Alltag';
|
||||
|
||||
@override
|
||||
String get subjectGroupOther => 'Sonstiges';
|
||||
|
||||
@override
|
||||
String get subjectBiology => 'Biologie';
|
||||
|
||||
@override
|
||||
String get subjectChemistry => 'Chemie';
|
||||
|
||||
@override
|
||||
String get subjectPhysics => 'Physik';
|
||||
|
||||
@override
|
||||
String get subjectAstronomy => 'Astronomie';
|
||||
|
||||
@override
|
||||
String get subjectEarthSciences => 'Geowissenschaften';
|
||||
|
||||
@override
|
||||
String get subjectEnvironmentalScience => 'Umweltwissenschaften';
|
||||
|
||||
@override
|
||||
String get subjectMedicine => 'Medizin';
|
||||
|
||||
@override
|
||||
String get subjectNursing => 'Pflege';
|
||||
|
||||
@override
|
||||
String get subjectPharmacy => 'Pharmazie';
|
||||
|
||||
@override
|
||||
String get subjectNutrition => 'Ernährung';
|
||||
|
||||
@override
|
||||
String get subjectPsychology => 'Psychologie';
|
||||
|
||||
@override
|
||||
String get subjectSportsScience => 'Sportwissenschaft';
|
||||
|
||||
@override
|
||||
String get subjectMathematics => 'Mathematik';
|
||||
|
||||
@override
|
||||
String get subjectStatistics => 'Statistik';
|
||||
|
||||
@override
|
||||
String get subjectComputerScience => 'Informatik';
|
||||
|
||||
@override
|
||||
String get subjectSoftwareEngineering => 'Softwareentwicklung';
|
||||
|
||||
@override
|
||||
String get subjectDataScienceAi => 'Data Science und KI';
|
||||
|
||||
@override
|
||||
String get subjectItOperations => 'IT und Betrieb';
|
||||
|
||||
@override
|
||||
String get subjectSecurityPrivacy => 'Sicherheit und Datenschutz';
|
||||
|
||||
@override
|
||||
String get subjectEngineering => 'Ingenieurwesen';
|
||||
|
||||
@override
|
||||
String get subjectArchitecture => 'Architektur';
|
||||
|
||||
@override
|
||||
String get subjectHistory => 'Geschichte';
|
||||
|
||||
@override
|
||||
String get subjectGeography => 'Geografie';
|
||||
|
||||
@override
|
||||
String get subjectPolitics => 'Politik';
|
||||
|
||||
@override
|
||||
String get subjectLaw => 'Recht';
|
||||
|
||||
@override
|
||||
String get subjectEconomics => 'Volkswirtschaft';
|
||||
|
||||
@override
|
||||
String get subjectBusiness => 'Betriebswirtschaft';
|
||||
|
||||
@override
|
||||
String get subjectMarketing => 'Marketing';
|
||||
|
||||
@override
|
||||
String get subjectAccountingFinance => 'Rechnungswesen und Finanzen';
|
||||
|
||||
@override
|
||||
String get subjectSociology => 'Soziologie';
|
||||
|
||||
@override
|
||||
String get subjectAnthropology => 'Anthropologie';
|
||||
|
||||
@override
|
||||
String get subjectEducation => 'Pädagogik';
|
||||
|
||||
@override
|
||||
String get subjectMediaStudies => 'Medienwissenschaft';
|
||||
|
||||
@override
|
||||
String get subjectPhilosophy => 'Philosophie';
|
||||
|
||||
@override
|
||||
String get subjectReligiousStudies => 'Religionswissenschaft';
|
||||
|
||||
@override
|
||||
String get subjectLanguages => 'Sprachen';
|
||||
|
||||
@override
|
||||
String get subjectLiterature => 'Literatur';
|
||||
|
||||
@override
|
||||
String get subjectLinguistics => 'Linguistik';
|
||||
|
||||
@override
|
||||
String get subjectArtDesign => 'Kunst und Design';
|
||||
|
||||
@override
|
||||
String get subjectMusic => 'Musik';
|
||||
|
||||
@override
|
||||
String get subjectTheatreFilm => 'Theater und Film';
|
||||
|
||||
@override
|
||||
String get subjectCookingHospitality => 'Kochen und Gastronomie';
|
||||
|
||||
@override
|
||||
String get subjectDrivingLicences => 'Führerschein und Lizenzen';
|
||||
|
||||
@override
|
||||
String get subjectOther => 'Sonstiges / unsicher';
|
||||
|
||||
@override
|
||||
String get studysetSheetSubject => 'Fach';
|
||||
|
||||
@override
|
||||
String get studysetSheetSubjectNone => 'Fach wählen';
|
||||
|
||||
@override
|
||||
String get subjectPickerTitle => 'Fach';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7938,6 +7938,165 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get exportProgressTitle => 'Exporting';
|
||||
|
||||
@override
|
||||
String get subjectGroupSciences => 'Sciences and health';
|
||||
|
||||
@override
|
||||
String get subjectGroupComputing => 'Mathematics and computing';
|
||||
|
||||
@override
|
||||
String get subjectGroupSociety => 'Society';
|
||||
|
||||
@override
|
||||
String get subjectGroupArts => 'Languages and arts';
|
||||
|
||||
@override
|
||||
String get subjectGroupEveryday => 'Everyday';
|
||||
|
||||
@override
|
||||
String get subjectGroupOther => 'Other';
|
||||
|
||||
@override
|
||||
String get subjectBiology => 'Biology';
|
||||
|
||||
@override
|
||||
String get subjectChemistry => 'Chemistry';
|
||||
|
||||
@override
|
||||
String get subjectPhysics => 'Physics';
|
||||
|
||||
@override
|
||||
String get subjectAstronomy => 'Astronomy';
|
||||
|
||||
@override
|
||||
String get subjectEarthSciences => 'Earth sciences';
|
||||
|
||||
@override
|
||||
String get subjectEnvironmentalScience => 'Environmental science';
|
||||
|
||||
@override
|
||||
String get subjectMedicine => 'Medicine';
|
||||
|
||||
@override
|
||||
String get subjectNursing => 'Nursing';
|
||||
|
||||
@override
|
||||
String get subjectPharmacy => 'Pharmacy';
|
||||
|
||||
@override
|
||||
String get subjectNutrition => 'Nutrition';
|
||||
|
||||
@override
|
||||
String get subjectPsychology => 'Psychology';
|
||||
|
||||
@override
|
||||
String get subjectSportsScience => 'Sports science';
|
||||
|
||||
@override
|
||||
String get subjectMathematics => 'Mathematics';
|
||||
|
||||
@override
|
||||
String get subjectStatistics => 'Statistics';
|
||||
|
||||
@override
|
||||
String get subjectComputerScience => 'Computer science';
|
||||
|
||||
@override
|
||||
String get subjectSoftwareEngineering => 'Software engineering';
|
||||
|
||||
@override
|
||||
String get subjectDataScienceAi => 'Data science and AI';
|
||||
|
||||
@override
|
||||
String get subjectItOperations => 'IT and operations';
|
||||
|
||||
@override
|
||||
String get subjectSecurityPrivacy => 'Security and privacy';
|
||||
|
||||
@override
|
||||
String get subjectEngineering => 'Engineering';
|
||||
|
||||
@override
|
||||
String get subjectArchitecture => 'Architecture';
|
||||
|
||||
@override
|
||||
String get subjectHistory => 'History';
|
||||
|
||||
@override
|
||||
String get subjectGeography => 'Geography';
|
||||
|
||||
@override
|
||||
String get subjectPolitics => 'Politics';
|
||||
|
||||
@override
|
||||
String get subjectLaw => 'Law';
|
||||
|
||||
@override
|
||||
String get subjectEconomics => 'Economics';
|
||||
|
||||
@override
|
||||
String get subjectBusiness => 'Business';
|
||||
|
||||
@override
|
||||
String get subjectMarketing => 'Marketing';
|
||||
|
||||
@override
|
||||
String get subjectAccountingFinance => 'Accounting and finance';
|
||||
|
||||
@override
|
||||
String get subjectSociology => 'Sociology';
|
||||
|
||||
@override
|
||||
String get subjectAnthropology => 'Anthropology';
|
||||
|
||||
@override
|
||||
String get subjectEducation => 'Education';
|
||||
|
||||
@override
|
||||
String get subjectMediaStudies => 'Media studies';
|
||||
|
||||
@override
|
||||
String get subjectPhilosophy => 'Philosophy';
|
||||
|
||||
@override
|
||||
String get subjectReligiousStudies => 'Religious studies';
|
||||
|
||||
@override
|
||||
String get subjectLanguages => 'Languages';
|
||||
|
||||
@override
|
||||
String get subjectLiterature => 'Literature';
|
||||
|
||||
@override
|
||||
String get subjectLinguistics => 'Linguistics';
|
||||
|
||||
@override
|
||||
String get subjectArtDesign => 'Art and design';
|
||||
|
||||
@override
|
||||
String get subjectMusic => 'Music';
|
||||
|
||||
@override
|
||||
String get subjectTheatreFilm => 'Theatre and film';
|
||||
|
||||
@override
|
||||
String get subjectCookingHospitality => 'Cooking and hospitality';
|
||||
|
||||
@override
|
||||
String get subjectDrivingLicences => 'Driving and licences';
|
||||
|
||||
@override
|
||||
String get subjectOther => 'Other / not sure';
|
||||
|
||||
@override
|
||||
String get studysetSheetSubject => 'Subject';
|
||||
|
||||
@override
|
||||
String get studysetSheetSubjectNone => 'Choose a subject';
|
||||
|
||||
@override
|
||||
String get subjectPickerTitle => 'Subject';
|
||||
}
|
||||
|
||||
/// The translations for English, as used in the United Kingdom (`en_GB`).
|
||||
|
|
|
|||
|
|
@ -673,6 +673,7 @@ class CopyUpdater(
|
|||
publishedAt: location,
|
||||
examPlans: content.examPlans,
|
||||
exam: content.exam,
|
||||
subject: content.subject,
|
||||
);
|
||||
|
||||
static LearningPathContent _pathWithOrigin(
|
||||
|
|
|
|||
|
|
@ -544,6 +544,7 @@ class ReleaseCopier(
|
|||
publishedAt: location,
|
||||
examPlans: content.examPlans,
|
||||
exam: content.exam,
|
||||
subject: content.subject,
|
||||
);
|
||||
|
||||
static LearningPathContent _pathWithOrigin(
|
||||
|
|
|
|||
|
|
@ -24,10 +24,12 @@ import 'package:halkyon_learn_flutter/src/feature/studyset/model/studyset_conten
|
|||
// added the exams it plans for (`examPlans`: id, name, the topics it covers
|
||||
// by their ids, the tags by their names; left out when none) and the one the
|
||||
// person prepares for (`exam`: the plan's id and the exam's day,
|
||||
// `YYYY-MM-DD`; theirs alone, never published).
|
||||
// `YYYY-MM-DD`; theirs alone, never published). Schema 8 added the subject
|
||||
// (`subject`: a Subject's ID, kept even where this app doesn't know it), left
|
||||
// out when none was chosen.
|
||||
|
||||
/// The schema this app writes.
|
||||
const studysetSchemaVersion = 7;
|
||||
const studysetSchemaVersion = 8;
|
||||
|
||||
/// Encodes a studyset's content into its payload.
|
||||
ByteData encodeStudysetPayload(StudysetContent content) => encodePayload(
|
||||
|
|
@ -60,6 +62,7 @@ ByteData encodeStudysetPayload(StudysetContent content) => encodePayload(
|
|||
if (content.examPlans.isNotEmpty)
|
||||
'examPlans': [for (final plan in content.examPlans) plan.toJson()],
|
||||
if (content.exam case final exam?) 'exam': exam.toJson(),
|
||||
'subject': ?content.subject,
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -115,6 +118,7 @@ StudysetContent decodeStudysetPayload(ByteData payload) {
|
|||
?ExamPlan.fromJson(plan),
|
||||
],
|
||||
exam: ExamChoice.fromJson(json['exam']),
|
||||
subject: _optional(json, 'subject'),
|
||||
);
|
||||
}
|
||||
throw const FormatException('Not a studyset payload.');
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import 'package:halkyon_learn_flutter/src/core/citation/model/source.dart';
|
|||
import 'package:halkyon_learn_flutter/src/core/content/published_ref.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/study/model/studyset_study_options.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/studyset/model/exam_plan.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/studyset/model/subject.dart';
|
||||
|
||||
/// What a person writes about a studyset: everything its synced payload
|
||||
/// holds. Where it sits and whether it is starred or archived are columns of
|
||||
|
|
@ -63,6 +64,12 @@ final class const StudysetContent({
|
|||
/// The exam the person prepares for and its date, theirs alone (never
|
||||
/// published); null without one.
|
||||
final ExamChoice? exam,
|
||||
|
||||
/// The ID of the [Subject] it is filed under (decided by the developer,
|
||||
/// 10 Nov 2026), on a top studyset alone, which its sub-studysets show
|
||||
/// (`subjectOf`); published with it. Null where none was chosen; one this
|
||||
/// app version doesn't know is kept and reads as none.
|
||||
final String? subject,
|
||||
}) with Equatable {
|
||||
/// Content with only a [title] and an empty description, as a new
|
||||
/// studyset starts.
|
||||
|
|
@ -86,6 +93,7 @@ final class const StudysetContent({
|
|||
publishedAt,
|
||||
examPlans,
|
||||
exam,
|
||||
subject,
|
||||
];
|
||||
|
||||
/// This content with a new [title] or [description]. The other fields can
|
||||
|
|
@ -107,6 +115,7 @@ final class const StudysetContent({
|
|||
publishedAt: publishedAt,
|
||||
examPlans: examPlans,
|
||||
exam: exam,
|
||||
subject: subject,
|
||||
);
|
||||
|
||||
/// This content studied with [options]; null for the defaults.
|
||||
|
|
@ -127,6 +136,7 @@ final class const StudysetContent({
|
|||
publishedAt: publishedAt,
|
||||
examPlans: examPlans,
|
||||
exam: exam,
|
||||
subject: subject,
|
||||
);
|
||||
|
||||
/// This content citing [bibliography].
|
||||
|
|
@ -147,6 +157,7 @@ final class const StudysetContent({
|
|||
publishedAt: publishedAt,
|
||||
examPlans: examPlans,
|
||||
exam: exam,
|
||||
subject: subject,
|
||||
);
|
||||
|
||||
/// This content planning for the exams [plans].
|
||||
|
|
@ -166,6 +177,7 @@ final class const StudysetContent({
|
|||
publishedAt: publishedAt,
|
||||
examPlans: plans,
|
||||
exam: exam,
|
||||
subject: subject,
|
||||
);
|
||||
|
||||
/// This content with the person preparing for [exam]; null for none.
|
||||
|
|
@ -185,5 +197,6 @@ final class const StudysetContent({
|
|||
publishedAt: publishedAt,
|
||||
examPlans: examPlans,
|
||||
exam: exam,
|
||||
subject: subject,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:halkyon_learn_flutter/src/feature/studyset/model/studyset.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/studyset/model/subject.dart';
|
||||
|
||||
/// The studysets of [all] that show at the top level, in their order: those
|
||||
/// without a parent, those whose parent is gone (deleted, or not synced
|
||||
|
|
@ -161,6 +162,16 @@ String? colorNameOf(String id, List<Studyset> all) {
|
|||
return null;
|
||||
}
|
||||
|
||||
/// The subject the studyset [id] is filed under: its top studyset's, which
|
||||
/// alone has one (decided by the developer, 10 Nov 2026); null where none
|
||||
/// was chosen or it is one this app version doesn't know.
|
||||
Subject? subjectOf(String id, List<Studyset> all) {
|
||||
final top =
|
||||
ancestorsOf(id, all).firstOrNull ??
|
||||
all.where((each) => each.id == id).firstOrNull;
|
||||
return Subject.byId(top?.content.subject);
|
||||
}
|
||||
|
||||
/// The languages of a studyset's cards: of their fronts and of their backs,
|
||||
/// as BCP 47 tags; either null when not given.
|
||||
typedef StudysetLanguages = ({String? front, String? back});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,440 @@
|
|||
import 'package:halkyon_learn_flutter/l10n/app_localizations.dart';
|
||||
import 'package:halkyon_ui/halkyon_ui.dart';
|
||||
import 'package:lucide_icons_flutter/lucide_icons.dart';
|
||||
import 'package:material_ui/material_ui.dart';
|
||||
|
||||
/// The groups the subject picker lists [Subject]s under.
|
||||
enum SubjectGroup() {
|
||||
/// Sciences and health.
|
||||
sciences,
|
||||
|
||||
/// Mathematics and computing.
|
||||
computing,
|
||||
|
||||
/// Society.
|
||||
society,
|
||||
|
||||
/// Languages and arts.
|
||||
arts,
|
||||
|
||||
/// Everyday.
|
||||
everyday,
|
||||
|
||||
/// Other.
|
||||
other;
|
||||
|
||||
/// The group's name in [l10n]'s language.
|
||||
String nameIn(AppLocalizations l10n) => switch (this) {
|
||||
SubjectGroup.sciences => l10n.subjectGroupSciences,
|
||||
SubjectGroup.computing => l10n.subjectGroupComputing,
|
||||
SubjectGroup.society => l10n.subjectGroupSociety,
|
||||
SubjectGroup.arts => l10n.subjectGroupArts,
|
||||
SubjectGroup.everyday => l10n.subjectGroupEveryday,
|
||||
SubjectGroup.other => l10n.subjectGroupOther,
|
||||
};
|
||||
}
|
||||
|
||||
/// What a studyset is about, filed under one of a fixed list (decided by
|
||||
/// the developer, 9 and 10 Nov 2026: Vaia's breadth, languages as one,
|
||||
/// computing in five): each with its Lucide icon and the colour a studyset
|
||||
/// takes when it is chosen. A studyset keeps the [id], never renamed; one
|
||||
/// this app version doesn't know (a newer one's) is kept and read as none.
|
||||
enum Subject(
|
||||
/// How studysets keep it.
|
||||
final String id,
|
||||
|
||||
/// Where the picker lists it.
|
||||
final SubjectGroup group,
|
||||
|
||||
/// Its icon.
|
||||
final IconData icon,
|
||||
|
||||
/// The colour a studyset takes when it is chosen, its own to change from
|
||||
/// then on; none for [other].
|
||||
final PaletteColor? color,
|
||||
) {
|
||||
/// Biology.
|
||||
biology('biology', SubjectGroup.sciences, LucideIcons.dna, PaletteColor.fern),
|
||||
|
||||
/// Chemistry.
|
||||
chemistry(
|
||||
'chemistry',
|
||||
SubjectGroup.sciences,
|
||||
LucideIcons.flaskConical,
|
||||
PaletteColor.reef,
|
||||
),
|
||||
|
||||
/// Physics.
|
||||
physics(
|
||||
'physics',
|
||||
SubjectGroup.sciences,
|
||||
LucideIcons.atom,
|
||||
PaletteColor.slateblue,
|
||||
),
|
||||
|
||||
/// Astronomy.
|
||||
astronomy(
|
||||
'astronomy',
|
||||
SubjectGroup.sciences,
|
||||
LucideIcons.telescope,
|
||||
PaletteColor.enzian,
|
||||
),
|
||||
|
||||
/// Earth sciences.
|
||||
earthSciences(
|
||||
'earth-sciences',
|
||||
SubjectGroup.sciences,
|
||||
LucideIcons.mountain,
|
||||
PaletteColor.sahara,
|
||||
),
|
||||
|
||||
/// Environmental science.
|
||||
environmentalScience(
|
||||
'environmental-science',
|
||||
SubjectGroup.sciences,
|
||||
LucideIcons.leaf,
|
||||
PaletteColor.forest,
|
||||
),
|
||||
|
||||
/// Medicine.
|
||||
medicine(
|
||||
'medicine',
|
||||
SubjectGroup.sciences,
|
||||
LucideIcons.stethoscope,
|
||||
PaletteColor.cerise,
|
||||
),
|
||||
|
||||
/// Nursing.
|
||||
nursing(
|
||||
'nursing',
|
||||
SubjectGroup.sciences,
|
||||
LucideIcons.heartPulse,
|
||||
PaletteColor.strawberry,
|
||||
),
|
||||
|
||||
/// Pharmacy.
|
||||
pharmacy(
|
||||
'pharmacy',
|
||||
SubjectGroup.sciences,
|
||||
LucideIcons.pill,
|
||||
PaletteColor.pink,
|
||||
),
|
||||
|
||||
/// Nutrition.
|
||||
nutrition(
|
||||
'nutrition',
|
||||
SubjectGroup.sciences,
|
||||
LucideIcons.apple,
|
||||
PaletteColor.carrot,
|
||||
),
|
||||
|
||||
/// Psychology.
|
||||
psychology(
|
||||
'psychology',
|
||||
SubjectGroup.sciences,
|
||||
LucideIcons.brain,
|
||||
PaletteColor.plum,
|
||||
),
|
||||
|
||||
/// Sports science.
|
||||
sportsScience(
|
||||
'sports-science',
|
||||
SubjectGroup.sciences,
|
||||
LucideIcons.dumbbell,
|
||||
PaletteColor.copper,
|
||||
),
|
||||
|
||||
/// Mathematics.
|
||||
mathematics(
|
||||
'mathematics',
|
||||
SubjectGroup.computing,
|
||||
LucideIcons.sigma,
|
||||
PaletteColor.cobalt,
|
||||
),
|
||||
|
||||
/// Statistics.
|
||||
statistics(
|
||||
'statistics',
|
||||
SubjectGroup.computing,
|
||||
LucideIcons.chartColumn,
|
||||
PaletteColor.ocean,
|
||||
),
|
||||
|
||||
/// Computer science.
|
||||
computerScience(
|
||||
'computer-science',
|
||||
SubjectGroup.computing,
|
||||
LucideIcons.cpu,
|
||||
PaletteColor.slateblue,
|
||||
),
|
||||
|
||||
/// Software engineering.
|
||||
softwareEngineering(
|
||||
'software-engineering',
|
||||
SubjectGroup.computing,
|
||||
LucideIcons.code,
|
||||
PaletteColor.enzian,
|
||||
),
|
||||
|
||||
/// Data science and AI.
|
||||
dataScienceAi(
|
||||
'data-science-ai',
|
||||
SubjectGroup.computing,
|
||||
LucideIcons.brainCircuit,
|
||||
PaletteColor.purple,
|
||||
),
|
||||
|
||||
/// IT and operations.
|
||||
itOperations(
|
||||
'it-operations',
|
||||
SubjectGroup.computing,
|
||||
LucideIcons.server,
|
||||
PaletteColor.pine,
|
||||
),
|
||||
|
||||
/// Security and privacy.
|
||||
securityPrivacy(
|
||||
'security-privacy',
|
||||
SubjectGroup.computing,
|
||||
LucideIcons.shieldCheck,
|
||||
PaletteColor.forest,
|
||||
),
|
||||
|
||||
/// Engineering.
|
||||
engineering(
|
||||
'engineering',
|
||||
SubjectGroup.computing,
|
||||
LucideIcons.cog,
|
||||
PaletteColor.soil,
|
||||
),
|
||||
|
||||
/// Architecture.
|
||||
architecture(
|
||||
'architecture',
|
||||
SubjectGroup.computing,
|
||||
LucideIcons.building2,
|
||||
PaletteColor.sahara,
|
||||
),
|
||||
|
||||
/// History.
|
||||
history(
|
||||
'history',
|
||||
SubjectGroup.society,
|
||||
LucideIcons.landmark,
|
||||
PaletteColor.soil,
|
||||
),
|
||||
|
||||
/// Geography.
|
||||
geography(
|
||||
'geography',
|
||||
SubjectGroup.society,
|
||||
LucideIcons.globe,
|
||||
PaletteColor.pacific,
|
||||
),
|
||||
|
||||
/// Politics.
|
||||
politics(
|
||||
'politics',
|
||||
SubjectGroup.society,
|
||||
LucideIcons.vote,
|
||||
PaletteColor.cobalt,
|
||||
),
|
||||
|
||||
/// Law.
|
||||
law('law', SubjectGroup.society, LucideIcons.scale, PaletteColor.pickle),
|
||||
|
||||
/// Economics.
|
||||
economics(
|
||||
'economics',
|
||||
SubjectGroup.society,
|
||||
LucideIcons.trendingUp,
|
||||
PaletteColor.olive,
|
||||
),
|
||||
|
||||
/// Business.
|
||||
business(
|
||||
'business',
|
||||
SubjectGroup.society,
|
||||
LucideIcons.briefcase,
|
||||
PaletteColor.copper,
|
||||
),
|
||||
|
||||
/// Marketing.
|
||||
marketing(
|
||||
'marketing',
|
||||
SubjectGroup.society,
|
||||
LucideIcons.megaphone,
|
||||
PaletteColor.carrot,
|
||||
),
|
||||
|
||||
/// Accounting and finance.
|
||||
accountingFinance(
|
||||
'accounting-finance',
|
||||
SubjectGroup.society,
|
||||
LucideIcons.calculator,
|
||||
PaletteColor.pine,
|
||||
),
|
||||
|
||||
/// Sociology.
|
||||
sociology(
|
||||
'sociology',
|
||||
SubjectGroup.society,
|
||||
LucideIcons.users,
|
||||
PaletteColor.plum,
|
||||
),
|
||||
|
||||
/// Anthropology.
|
||||
anthropology(
|
||||
'anthropology',
|
||||
SubjectGroup.society,
|
||||
LucideIcons.footprints,
|
||||
PaletteColor.sahara,
|
||||
),
|
||||
|
||||
/// Education.
|
||||
education(
|
||||
'education',
|
||||
SubjectGroup.society,
|
||||
LucideIcons.school,
|
||||
PaletteColor.ocean,
|
||||
),
|
||||
|
||||
/// Media studies.
|
||||
mediaStudies(
|
||||
'media-studies',
|
||||
SubjectGroup.society,
|
||||
LucideIcons.tv,
|
||||
PaletteColor.pink,
|
||||
),
|
||||
|
||||
/// Philosophy.
|
||||
philosophy(
|
||||
'philosophy',
|
||||
SubjectGroup.society,
|
||||
LucideIcons.lightbulb,
|
||||
PaletteColor.purple,
|
||||
),
|
||||
|
||||
/// Religious studies.
|
||||
religiousStudies(
|
||||
'religious-studies',
|
||||
SubjectGroup.society,
|
||||
LucideIcons.bookOpen,
|
||||
PaletteColor.olive,
|
||||
),
|
||||
|
||||
/// Languages.
|
||||
languages(
|
||||
'languages',
|
||||
SubjectGroup.arts,
|
||||
LucideIcons.languages,
|
||||
PaletteColor.cerise,
|
||||
),
|
||||
|
||||
/// Literature.
|
||||
literature(
|
||||
'literature',
|
||||
SubjectGroup.arts,
|
||||
LucideIcons.bookMarked,
|
||||
PaletteColor.strawberry,
|
||||
),
|
||||
|
||||
/// Linguistics.
|
||||
linguistics(
|
||||
'linguistics',
|
||||
SubjectGroup.arts,
|
||||
LucideIcons.speech,
|
||||
PaletteColor.enzian,
|
||||
),
|
||||
|
||||
/// Art and design.
|
||||
artDesign(
|
||||
'art-design',
|
||||
SubjectGroup.arts,
|
||||
LucideIcons.palette,
|
||||
PaletteColor.pink,
|
||||
),
|
||||
|
||||
/// Music.
|
||||
music('music', SubjectGroup.arts, LucideIcons.music, PaletteColor.plum),
|
||||
|
||||
/// Theatre and film.
|
||||
theatreFilm(
|
||||
'theatre-film',
|
||||
SubjectGroup.arts,
|
||||
LucideIcons.clapperboard,
|
||||
PaletteColor.cerise,
|
||||
),
|
||||
|
||||
/// Cooking and hospitality.
|
||||
cookingHospitality(
|
||||
'cooking-hospitality',
|
||||
SubjectGroup.everyday,
|
||||
LucideIcons.chefHat,
|
||||
PaletteColor.carrot,
|
||||
),
|
||||
|
||||
/// Driving and licences.
|
||||
drivingLicences(
|
||||
'driving-licences',
|
||||
SubjectGroup.everyday,
|
||||
LucideIcons.car,
|
||||
PaletteColor.soil,
|
||||
),
|
||||
|
||||
/// Other / not sure.
|
||||
other('other', SubjectGroup.other, LucideIcons.circleQuestionMark, null);
|
||||
|
||||
/// The subject kept as [id]; null for none or one this app doesn't know.
|
||||
static Subject? byId(String? id) => _byId[id];
|
||||
|
||||
static final _byId = {for (final subject in values) subject.id: subject};
|
||||
|
||||
/// Its name in [l10n]'s language.
|
||||
String nameIn(AppLocalizations l10n) => switch (this) {
|
||||
Subject.biology => l10n.subjectBiology,
|
||||
Subject.chemistry => l10n.subjectChemistry,
|
||||
Subject.physics => l10n.subjectPhysics,
|
||||
Subject.astronomy => l10n.subjectAstronomy,
|
||||
Subject.earthSciences => l10n.subjectEarthSciences,
|
||||
Subject.environmentalScience => l10n.subjectEnvironmentalScience,
|
||||
Subject.medicine => l10n.subjectMedicine,
|
||||
Subject.nursing => l10n.subjectNursing,
|
||||
Subject.pharmacy => l10n.subjectPharmacy,
|
||||
Subject.nutrition => l10n.subjectNutrition,
|
||||
Subject.psychology => l10n.subjectPsychology,
|
||||
Subject.sportsScience => l10n.subjectSportsScience,
|
||||
Subject.mathematics => l10n.subjectMathematics,
|
||||
Subject.statistics => l10n.subjectStatistics,
|
||||
Subject.computerScience => l10n.subjectComputerScience,
|
||||
Subject.softwareEngineering => l10n.subjectSoftwareEngineering,
|
||||
Subject.dataScienceAi => l10n.subjectDataScienceAi,
|
||||
Subject.itOperations => l10n.subjectItOperations,
|
||||
Subject.securityPrivacy => l10n.subjectSecurityPrivacy,
|
||||
Subject.engineering => l10n.subjectEngineering,
|
||||
Subject.architecture => l10n.subjectArchitecture,
|
||||
Subject.history => l10n.subjectHistory,
|
||||
Subject.geography => l10n.subjectGeography,
|
||||
Subject.politics => l10n.subjectPolitics,
|
||||
Subject.law => l10n.subjectLaw,
|
||||
Subject.economics => l10n.subjectEconomics,
|
||||
Subject.business => l10n.subjectBusiness,
|
||||
Subject.marketing => l10n.subjectMarketing,
|
||||
Subject.accountingFinance => l10n.subjectAccountingFinance,
|
||||
Subject.sociology => l10n.subjectSociology,
|
||||
Subject.anthropology => l10n.subjectAnthropology,
|
||||
Subject.education => l10n.subjectEducation,
|
||||
Subject.mediaStudies => l10n.subjectMediaStudies,
|
||||
Subject.philosophy => l10n.subjectPhilosophy,
|
||||
Subject.religiousStudies => l10n.subjectReligiousStudies,
|
||||
Subject.languages => l10n.subjectLanguages,
|
||||
Subject.literature => l10n.subjectLiterature,
|
||||
Subject.linguistics => l10n.subjectLinguistics,
|
||||
Subject.artDesign => l10n.subjectArtDesign,
|
||||
Subject.music => l10n.subjectMusic,
|
||||
Subject.theatreFilm => l10n.subjectTheatreFilm,
|
||||
Subject.cookingHospitality => l10n.subjectCookingHospitality,
|
||||
Subject.drivingLicences => l10n.subjectDrivingLicences,
|
||||
Subject.other => l10n.subjectOther,
|
||||
};
|
||||
}
|
||||
|
|
@ -7,7 +7,9 @@ import 'package:halkyon_learn_flutter/src/core/l10n/content_languages.dart';
|
|||
import 'package:halkyon_learn_flutter/src/feature/studyset/model/license.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/studyset/model/studyset_content.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/studyset/model/studyset_tree.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/studyset/model/subject.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/studyset/widget/license_chooser_sheet.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/studyset/widget/subject_picker.dart';
|
||||
import 'package:halkyon_ui/halkyon_ui.dart';
|
||||
import 'package:lucide_icons_flutter/lucide_icons.dart';
|
||||
import 'package:material_ui/material_ui.dart';
|
||||
|
|
@ -113,6 +115,10 @@ class _StudysetSheetState() extends State<_StudysetSheet> {
|
|||
null => widget.insideAnother ? null : _defaultColor.name,
|
||||
};
|
||||
|
||||
/// The ID of the subject it is filed under, on a top studyset alone;
|
||||
/// one only a newer app version knows is kept until another is picked.
|
||||
late String? _subject = widget.initial?.subject;
|
||||
|
||||
/// Whether the studyset gives languages of its own: off, it takes those
|
||||
/// of the studyset it is in, or has none at the top level.
|
||||
late var _setLanguages =
|
||||
|
|
@ -222,6 +228,7 @@ class _StudysetSheetState() extends State<_StudysetSheet> {
|
|||
publishedAt: initial?.publishedAt,
|
||||
examPlans: initial?.examPlans ?? const [],
|
||||
exam: initial?.exam,
|
||||
subject: _subject,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -233,6 +240,7 @@ class _StudysetSheetState() extends State<_StudysetSheet> {
|
|||
_description != RichContent.empty() ||
|
||||
_colorName != (widget.insideAnother ? null : _defaultColor.name) ||
|
||||
_licenseNow != _licenseBefore ||
|
||||
_subject != null ||
|
||||
(_setLanguages && (_frontLanguage ?? _backLanguage) != null),
|
||||
};
|
||||
|
||||
|
|
@ -243,6 +251,19 @@ class _StudysetSheetState() extends State<_StudysetSheet> {
|
|||
PaletteColor.byName(widget.inheritedColorName) ??
|
||||
_defaultColor;
|
||||
|
||||
/// Asks for the subject; one picked gives the studyset its colour, the
|
||||
/// studyset's to change from then on (decided by the developer, 9 Nov
|
||||
/// 2026).
|
||||
Future<void> _pickSubject() async {
|
||||
if (await pickSubject(context, selected: Subject.byId(_subject))
|
||||
case final subject?) {
|
||||
setState(() {
|
||||
_subject = subject.id;
|
||||
_colorName = subject.color?.name ?? _colorName;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _editDescription() async {
|
||||
if (await editRichText(
|
||||
context,
|
||||
|
|
@ -330,6 +351,19 @@ class _StudysetSheetState() extends State<_StudysetSheet> {
|
|||
description: _description,
|
||||
onTap: _editDescription,
|
||||
),
|
||||
// A sub-studyset's subject is its top studyset's.
|
||||
if (!widget.insideAnother) ...[
|
||||
const SizedBox(height: 24),
|
||||
Text(
|
||||
l10n.studysetSheetSubject,
|
||||
style: theme.textTheme.labelLarge,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_SubjectField(
|
||||
subject: Subject.byId(_subject),
|
||||
onTap: _pickSubject,
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 24),
|
||||
Text(l10n.studysetSheetColor, style: theme.textTheme.labelLarge),
|
||||
const SizedBox(height: 8),
|
||||
|
|
@ -552,6 +586,37 @@ class _StudysetSheetState() extends State<_StudysetSheet> {
|
|||
|
||||
/// The licence, typed: any text, suggesting the [knownLicenses] that hold
|
||||
/// what is typed so far.
|
||||
/// The subject chosen, or an invitation to choose one, opening the picker.
|
||||
class const _SubjectField({
|
||||
required final Subject? subject,
|
||||
required final VoidCallback onTap,
|
||||
}) extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context);
|
||||
final theme = Theme.of(context);
|
||||
return ListTile(
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(color: theme.colorScheme.outline),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
contentPadding: const EdgeInsetsDirectional.only(start: 12, end: 8),
|
||||
leading: switch (subject) {
|
||||
final subject? => SubjectBadge(subject),
|
||||
null => const Icon(LucideIcons.shapes),
|
||||
},
|
||||
title: Text(
|
||||
subject?.nameIn(l10n) ?? l10n.studysetSheetSubjectNone,
|
||||
style: subject == null
|
||||
? TextStyle(color: theme.colorScheme.onSurfaceVariant)
|
||||
: null,
|
||||
),
|
||||
trailing: const Icon(LucideIcons.chevronDown),
|
||||
onTap: onTap,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class const _LicenseField({
|
||||
required final TextEditingController controller,
|
||||
required final FocusNode focusNode,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,81 @@
|
|||
import 'package:halkyon_learn_flutter/l10n/app_localizations.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/studyset/model/subject.dart';
|
||||
import 'package:halkyon_ui/halkyon_ui.dart';
|
||||
import 'package:lucide_icons_flutter/lucide_icons.dart';
|
||||
import 'package:material_ui/material_ui.dart';
|
||||
|
||||
/// Asks for a studyset's subject among every [Subject], under their groups,
|
||||
/// [selected] ticked; completes with the one tapped, or null when the sheet
|
||||
/// is closed.
|
||||
Future<Subject?> pickSubject(BuildContext context, {Subject? selected}) =>
|
||||
showAdaptiveSheet<Subject>(
|
||||
context,
|
||||
builder: (context) => _SubjectPicker(selected: selected),
|
||||
);
|
||||
|
||||
class const _SubjectPicker({required final Subject? selected})
|
||||
extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context);
|
||||
final theme = Theme.of(context);
|
||||
return SheetScaffold(
|
||||
title: l10n.subjectPickerTitle,
|
||||
heightFactor: 0.85,
|
||||
body: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
for (final group in SubjectGroup.values) ...[
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 16, 16, 4),
|
||||
child: Text(
|
||||
group.nameIn(l10n),
|
||||
style: theme.textTheme.titleSmall?.copyWith(
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
for (final subject in Subject.values)
|
||||
if (subject.group == group)
|
||||
ListTile(
|
||||
leading: SubjectBadge(subject),
|
||||
title: Text(subject.nameIn(l10n)),
|
||||
trailing: subject == selected
|
||||
? const Icon(LucideIcons.check)
|
||||
: null,
|
||||
selected: subject == selected,
|
||||
onTap: () => Navigator.of(context).pop(subject),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// A subject's icon on its colour, as a round badge; on the surface's own
|
||||
/// tone for one without a colour.
|
||||
class const SubjectBadge(
|
||||
final Subject subject, {
|
||||
final double size = 32,
|
||||
super.key,
|
||||
}) extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final scheme = Theme.of(context).colorScheme;
|
||||
final color = subject.color;
|
||||
return Container(
|
||||
width: size,
|
||||
height: size,
|
||||
decoration: BoxDecoration(
|
||||
color: color?.background ?? scheme.surfaceContainerHighest,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(
|
||||
subject.icon,
|
||||
size: size * 0.55,
|
||||
color: color?.foreground ?? scheme.onSurfaceVariant,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -46,11 +46,27 @@ void main() {
|
|||
licenseYear: 2006,
|
||||
frontLanguage: 'ja',
|
||||
backLanguage: 'en',
|
||||
subject: 'languages',
|
||||
);
|
||||
|
||||
expect(decodeStudysetPayload(encodeStudysetPayload(content)), content);
|
||||
});
|
||||
|
||||
test('keeps a subject this app version does not know, as a newer one '
|
||||
'may add subjects', () {
|
||||
final content = StudysetContent(
|
||||
title: 'Origami',
|
||||
description: RichContent.empty(),
|
||||
subject: 'paper-folding',
|
||||
);
|
||||
|
||||
expect(json(encodeStudysetPayload(content))['subject'], 'paper-folding');
|
||||
expect(
|
||||
decodeStudysetPayload(encodeStudysetPayload(content)).subject,
|
||||
'paper-folding',
|
||||
);
|
||||
});
|
||||
|
||||
test('leaves out what is not given', () {
|
||||
final payload = encodeStudysetPayload(
|
||||
StudysetContent(title: 'Japanese', description: RichContent.empty()),
|
||||
|
|
@ -59,7 +75,7 @@ void main() {
|
|||
expect(json(payload).keys, {'schemaVersion', 'title', 'description'});
|
||||
});
|
||||
|
||||
test('names its schema version: the seventh, which exam plans need', () {
|
||||
test('names its schema version: the eighth, which subjects need', () {
|
||||
final payload = encodeStudysetPayload(
|
||||
StudysetContent(
|
||||
title: 'Japanese',
|
||||
|
|
@ -68,17 +84,17 @@ void main() {
|
|||
),
|
||||
);
|
||||
|
||||
expect(studysetSchemaVersion, 7);
|
||||
expect(studysetSchemaVersion, 8);
|
||||
expect(json(payload)['schemaVersion'], studysetSchemaVersion);
|
||||
});
|
||||
|
||||
test('without study options, a bibliography or exam plans writes the '
|
||||
'seventh schema all the same', () {
|
||||
test('without study options, a bibliography, exam plans or a subject '
|
||||
'writes the eighth schema all the same', () {
|
||||
final payload = encodeStudysetPayload(
|
||||
StudysetContent(title: 'Japanese', description: RichContent.empty()),
|
||||
);
|
||||
|
||||
expect(json(payload)['schemaVersion'], 7);
|
||||
expect(json(payload)['schemaVersion'], 8);
|
||||
expect(json(payload).keys, isNot(contains('study')));
|
||||
expect(json(payload).keys, isNot(contains('bibliography')));
|
||||
expect(json(payload).keys, isNot(contains('examPlans')));
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import 'package:halkyon_editor/halkyon_editor.dart';
|
|||
import 'package:halkyon_learn_flutter/src/feature/studyset/model/studyset.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/studyset/model/studyset_content.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/studyset/model/studyset_tree.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/studyset/model/subject.dart';
|
||||
|
||||
Studyset studyset(
|
||||
String id, {
|
||||
|
|
@ -196,6 +197,32 @@ void main() {
|
|||
expect(colorNameOf('german', all), isNull);
|
||||
});
|
||||
|
||||
test("a studyset's subject is its top studyset's, which alone has one "
|
||||
"(the developer's decision of 10 Nov 2026)", () {
|
||||
Studyset of(String id, {String? subject, String? parent}) => Studyset(
|
||||
id: id,
|
||||
content: StudysetContent(
|
||||
title: id,
|
||||
description: RichContent.empty(),
|
||||
subject: subject,
|
||||
),
|
||||
parentId: parent,
|
||||
);
|
||||
final all = [
|
||||
of('japanese', subject: 'languages'),
|
||||
of('kana', parent: 'japanese'),
|
||||
// Left from an import: a sub-studyset's own subject counts for nothing.
|
||||
of('hiragana', subject: 'music', parent: 'kana'),
|
||||
of('origami', subject: 'paper-folding'),
|
||||
of('notes'),
|
||||
];
|
||||
|
||||
expect(subjectOf('hiragana', all), Subject.languages);
|
||||
expect(subjectOf('japanese', all), Subject.languages);
|
||||
expect(subjectOf('origami', all), isNull, reason: 'one of a newer app');
|
||||
expect(subjectOf('notes', all), isNull);
|
||||
});
|
||||
|
||||
test('a studyset without languages of its own takes those of the nearest '
|
||||
'studyset above it that gives any, both together', () {
|
||||
Studyset speaking(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,93 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:halkyon_learn_flutter/l10n/app_localizations.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/studyset/model/subject.dart';
|
||||
import 'package:halkyon_ui/halkyon_ui.dart';
|
||||
import 'package:material_ui/material_ui.dart';
|
||||
|
||||
/// The subjects a studyset is filed under (the developer's decisions of 9
|
||||
/// and 10 Nov 2026): a fixed list, each with its icon and default colour.
|
||||
void main() {
|
||||
test('the IDs studysets keep are these, never renamed: a renamed one '
|
||||
'would read as one of a newer app', () {
|
||||
expect(
|
||||
[for (final subject in Subject.values) subject.id],
|
||||
[
|
||||
'biology',
|
||||
'chemistry',
|
||||
'physics',
|
||||
'astronomy',
|
||||
'earth-sciences',
|
||||
'environmental-science',
|
||||
'medicine',
|
||||
'nursing',
|
||||
'pharmacy',
|
||||
'nutrition',
|
||||
'psychology',
|
||||
'sports-science',
|
||||
'mathematics',
|
||||
'statistics',
|
||||
'computer-science',
|
||||
'software-engineering',
|
||||
'data-science-ai',
|
||||
'it-operations',
|
||||
'security-privacy',
|
||||
'engineering',
|
||||
'architecture',
|
||||
'history',
|
||||
'geography',
|
||||
'politics',
|
||||
'law',
|
||||
'economics',
|
||||
'business',
|
||||
'marketing',
|
||||
'accounting-finance',
|
||||
'sociology',
|
||||
'anthropology',
|
||||
'education',
|
||||
'media-studies',
|
||||
'philosophy',
|
||||
'religious-studies',
|
||||
'languages',
|
||||
'literature',
|
||||
'linguistics',
|
||||
'art-design',
|
||||
'music',
|
||||
'theatre-film',
|
||||
'cooking-hospitality',
|
||||
'driving-licences',
|
||||
'other',
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
test('read back by ID; one it does not know is none', () {
|
||||
expect(Subject.byId('languages'), Subject.languages);
|
||||
expect(Subject.byId('paper-folding'), isNull);
|
||||
expect(Subject.byId(null), isNull);
|
||||
});
|
||||
|
||||
test('every group holds some, in the order the list has them; Other, '
|
||||
'without a colour of its own, comes last', () {
|
||||
for (final group in SubjectGroup.values) {
|
||||
expect(Subject.values.where((s) => s.group == group), isNotEmpty);
|
||||
}
|
||||
expect(Subject.values.last, Subject.other);
|
||||
expect(Subject.other.color, isNull);
|
||||
expect(Subject.languages.color, PaletteColor.cerise);
|
||||
});
|
||||
|
||||
test('every subject and group is named in English and German, each name '
|
||||
'its own', () async {
|
||||
for (final locale in AppLocalizations.supportedLocales) {
|
||||
final l10n = await AppLocalizations.delegate.load(locale);
|
||||
final names = {for (final s in Subject.values) s.nameIn(l10n)};
|
||||
expect(names, hasLength(Subject.values.length), reason: '$locale');
|
||||
expect(
|
||||
{for (final g in SubjectGroup.values) g.nameIn(l10n)},
|
||||
hasLength(SubjectGroup.values.length),
|
||||
);
|
||||
}
|
||||
final german = await AppLocalizations.delegate.load(const Locale('de'));
|
||||
expect(Subject.computerScience.nameIn(german), 'Informatik');
|
||||
});
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@ import 'package:halkyon_learn_flutter/src/feature/studyset/data/studyset_payload
|
|||
import 'package:halkyon_learn_flutter/src/feature/studyset/model/studyset_content.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/studyset/widget/deleted_studyset_card.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/studyset/widget/studyset_card.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/studyset/widget/subject_picker.dart';
|
||||
import 'package:halkyon_ui/halkyon_ui.dart';
|
||||
import 'package:lucide_icons_flutter/lucide_icons.dart';
|
||||
import 'package:material_ui/material_ui.dart';
|
||||
|
|
@ -74,6 +75,7 @@ void main() {
|
|||
DateTime? archivedAt,
|
||||
DateTime? starredAt,
|
||||
String? publishedAt,
|
||||
String? subject,
|
||||
bool private = false,
|
||||
}) => gateway.receiveRemoteChange(
|
||||
id,
|
||||
|
|
@ -86,6 +88,7 @@ void main() {
|
|||
? null
|
||||
: const PublishedRef(publication: 'p', item: 'i', version: 1),
|
||||
publishedAt: publishedAt,
|
||||
subject: subject,
|
||||
),
|
||||
),
|
||||
archivedAt: archivedAt,
|
||||
|
|
@ -267,6 +270,109 @@ void main() {
|
|||
);
|
||||
});
|
||||
|
||||
group("a studyset's subject (the developer's decisions of 9 and 10 Nov "
|
||||
'2026)', () {
|
||||
Future<void> pickSubject(WidgetTester tester, String name) async {
|
||||
await tester.tap(find.text('Choose a subject'));
|
||||
await tester.pumpAndSettle();
|
||||
final row = find.widgetWithText(ListTile, name);
|
||||
await tester.scrollUntilVisible(
|
||||
row,
|
||||
200,
|
||||
scrollable: find
|
||||
.descendant(
|
||||
of: find.byType(SheetScaffold).last,
|
||||
matching: find.byType(Scrollable),
|
||||
)
|
||||
.first,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(row);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
/// The subject the sheet's field shows.
|
||||
Finder subjectShown(String name) => find.descendant(
|
||||
of: find.ancestor(
|
||||
of: find.byType(SubjectBadge),
|
||||
matching: find.byType(ListTile),
|
||||
),
|
||||
matching: find.text(name),
|
||||
);
|
||||
|
||||
testWidgets('is picked from the subjects in their groups, and gives the '
|
||||
'studyset its colour', (tester) async {
|
||||
final semantics = tester.ensureSemantics();
|
||||
await showLibrary(tester);
|
||||
await tester.tap(find.text('Create studyset'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.enterText(find.widgetWithText(TextField, 'Title'), 'Kanji');
|
||||
|
||||
await tester.tap(find.text('Choose a subject'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Sciences and health'), findsOneWidget);
|
||||
await tester.tap(find.byTooltip('Close').last);
|
||||
await tester.pumpAndSettle();
|
||||
await pickSubject(tester, 'Languages');
|
||||
|
||||
expect(subjectShown('Languages'), findsOneWidget);
|
||||
expect(find.bySemanticsLabel('Color: Cerise'), findsOneWidget);
|
||||
await tester.tap(find.text('Create'));
|
||||
await tester.pumpAndSettle();
|
||||
final content = decodeStudysetPayload(gateway.writes.single.$2);
|
||||
expect(content.subject, 'languages');
|
||||
expect(content.colorName, 'cerise');
|
||||
semantics.dispose();
|
||||
});
|
||||
|
||||
testWidgets("its colour stays the studyset's to change", (tester) async {
|
||||
final semantics = tester.ensureSemantics();
|
||||
await showLibrary(tester);
|
||||
await tester.tap(find.text('Create studyset'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.enterText(find.widgetWithText(TextField, 'Title'), 'Rust');
|
||||
await pickSubject(tester, 'Software engineering');
|
||||
|
||||
await tester.tap(find.bySemanticsLabel('Color: Enzian'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.bySemanticsLabel('Fern'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('Create'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final content = decodeStudysetPayload(gateway.writes.single.$2);
|
||||
expect(content.subject, 'software-engineering');
|
||||
expect(content.colorName, 'fern');
|
||||
semantics.dispose();
|
||||
});
|
||||
|
||||
testWidgets('editing shows the one chosen, and keeps one this app '
|
||||
'version does not know', (tester) async {
|
||||
stored('origami', 'Origami', subject: 'paper-folding');
|
||||
stored('japanese', 'Japanese', subject: 'languages');
|
||||
await showLibrary(tester);
|
||||
|
||||
await choose(tester, 'Japanese', 'Edit');
|
||||
expect(subjectShown('Languages'), findsOneWidget);
|
||||
await tester.tap(find.byTooltip('Close'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await choose(tester, 'Origami', 'Edit');
|
||||
expect(find.text('Choose a subject'), findsOneWidget);
|
||||
await tester.enterText(
|
||||
find.widgetWithText(TextField, 'Origami'),
|
||||
'Paper',
|
||||
);
|
||||
await tester.pump();
|
||||
await tester.tap(find.text('Save'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(
|
||||
decodeStudysetPayload(gateway.writes.last.$2).subject,
|
||||
'paper-folding',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('creating waits for a title', (tester) async {
|
||||
await showLibrary(tester);
|
||||
await tester.tap(find.text('Create studyset'));
|
||||
|
|
|
|||
|
|
@ -396,6 +396,11 @@ void main() {
|
|||
tester.getRect(find.text('Inherit')).left,
|
||||
greaterThan(tester.getRect(find.byType(PaletteColorPicker)).right),
|
||||
);
|
||||
expect(
|
||||
find.text('Choose a subject'),
|
||||
findsNothing,
|
||||
reason: "a sub-studyset's subject is its top studyset's",
|
||||
);
|
||||
await tester.enterText(find.widgetWithText(TextField, 'Title'), 'Kana');
|
||||
await tester.pump();
|
||||
await tester.tap(find.text('Create'));
|
||||
|
|
|
|||
Loading…
Reference in a new issue