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
20 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",
|
"workStagePacking": "Packen",
|
||||||
"importAborted": "Import abgebrochen: Nichts davon wurde behalten.",
|
"importAborted": "Import abgebrochen: Nichts davon wurde behalten.",
|
||||||
"exportAborted": "Export abgebrochen.",
|
"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": "Export aborted.",
|
||||||
"@exportAborted": {"description": "Snack bar after an export's Abort."},
|
"@exportAborted": {"description": "Snack bar after an export's Abort."},
|
||||||
"exportProgressTitle": "Exporting",
|
"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:
|
/// In en, this message translates to:
|
||||||
/// **'Exporting'**
|
/// **'Exporting'**
|
||||||
String get exportProgressTitle;
|
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
|
class _AppLocalizationsDelegate
|
||||||
|
|
|
||||||
|
|
@ -8011,4 +8011,163 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get exportProgressTitle => 'Wird exportiert';
|
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
|
@override
|
||||||
String get exportProgressTitle => 'Exporting';
|
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`).
|
/// The translations for English, as used in the United Kingdom (`en_GB`).
|
||||||
|
|
|
||||||
|
|
@ -673,6 +673,7 @@ class CopyUpdater(
|
||||||
publishedAt: location,
|
publishedAt: location,
|
||||||
examPlans: content.examPlans,
|
examPlans: content.examPlans,
|
||||||
exam: content.exam,
|
exam: content.exam,
|
||||||
|
subject: content.subject,
|
||||||
);
|
);
|
||||||
|
|
||||||
static LearningPathContent _pathWithOrigin(
|
static LearningPathContent _pathWithOrigin(
|
||||||
|
|
|
||||||
|
|
@ -544,6 +544,7 @@ class ReleaseCopier(
|
||||||
publishedAt: location,
|
publishedAt: location,
|
||||||
examPlans: content.examPlans,
|
examPlans: content.examPlans,
|
||||||
exam: content.exam,
|
exam: content.exam,
|
||||||
|
subject: content.subject,
|
||||||
);
|
);
|
||||||
|
|
||||||
static LearningPathContent _pathWithOrigin(
|
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
|
// 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
|
// 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,
|
// 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.
|
/// The schema this app writes.
|
||||||
const studysetSchemaVersion = 7;
|
const studysetSchemaVersion = 8;
|
||||||
|
|
||||||
/// Encodes a studyset's content into its payload.
|
/// Encodes a studyset's content into its payload.
|
||||||
ByteData encodeStudysetPayload(StudysetContent content) => encodePayload(
|
ByteData encodeStudysetPayload(StudysetContent content) => encodePayload(
|
||||||
|
|
@ -60,6 +62,7 @@ ByteData encodeStudysetPayload(StudysetContent content) => encodePayload(
|
||||||
if (content.examPlans.isNotEmpty)
|
if (content.examPlans.isNotEmpty)
|
||||||
'examPlans': [for (final plan in content.examPlans) plan.toJson()],
|
'examPlans': [for (final plan in content.examPlans) plan.toJson()],
|
||||||
if (content.exam case final exam?) 'exam': exam.toJson(),
|
if (content.exam case final exam?) 'exam': exam.toJson(),
|
||||||
|
'subject': ?content.subject,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -115,6 +118,7 @@ StudysetContent decodeStudysetPayload(ByteData payload) {
|
||||||
?ExamPlan.fromJson(plan),
|
?ExamPlan.fromJson(plan),
|
||||||
],
|
],
|
||||||
exam: ExamChoice.fromJson(json['exam']),
|
exam: ExamChoice.fromJson(json['exam']),
|
||||||
|
subject: _optional(json, 'subject'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
throw const FormatException('Not a studyset payload.');
|
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/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/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/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
|
/// 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
|
/// 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
|
/// The exam the person prepares for and its date, theirs alone (never
|
||||||
/// published); null without one.
|
/// published); null without one.
|
||||||
final ExamChoice? exam,
|
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 {
|
}) with Equatable {
|
||||||
/// Content with only a [title] and an empty description, as a new
|
/// Content with only a [title] and an empty description, as a new
|
||||||
/// studyset starts.
|
/// studyset starts.
|
||||||
|
|
@ -86,6 +93,7 @@ final class const StudysetContent({
|
||||||
publishedAt,
|
publishedAt,
|
||||||
examPlans,
|
examPlans,
|
||||||
exam,
|
exam,
|
||||||
|
subject,
|
||||||
];
|
];
|
||||||
|
|
||||||
/// This content with a new [title] or [description]. The other fields can
|
/// This content with a new [title] or [description]. The other fields can
|
||||||
|
|
@ -107,6 +115,7 @@ final class const StudysetContent({
|
||||||
publishedAt: publishedAt,
|
publishedAt: publishedAt,
|
||||||
examPlans: examPlans,
|
examPlans: examPlans,
|
||||||
exam: exam,
|
exam: exam,
|
||||||
|
subject: subject,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// This content studied with [options]; null for the defaults.
|
/// This content studied with [options]; null for the defaults.
|
||||||
|
|
@ -127,6 +136,7 @@ final class const StudysetContent({
|
||||||
publishedAt: publishedAt,
|
publishedAt: publishedAt,
|
||||||
examPlans: examPlans,
|
examPlans: examPlans,
|
||||||
exam: exam,
|
exam: exam,
|
||||||
|
subject: subject,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// This content citing [bibliography].
|
/// This content citing [bibliography].
|
||||||
|
|
@ -147,6 +157,7 @@ final class const StudysetContent({
|
||||||
publishedAt: publishedAt,
|
publishedAt: publishedAt,
|
||||||
examPlans: examPlans,
|
examPlans: examPlans,
|
||||||
exam: exam,
|
exam: exam,
|
||||||
|
subject: subject,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// This content planning for the exams [plans].
|
/// This content planning for the exams [plans].
|
||||||
|
|
@ -166,6 +177,7 @@ final class const StudysetContent({
|
||||||
publishedAt: publishedAt,
|
publishedAt: publishedAt,
|
||||||
examPlans: plans,
|
examPlans: plans,
|
||||||
exam: exam,
|
exam: exam,
|
||||||
|
subject: subject,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// This content with the person preparing for [exam]; null for none.
|
/// This content with the person preparing for [exam]; null for none.
|
||||||
|
|
@ -185,5 +197,6 @@ final class const StudysetContent({
|
||||||
publishedAt: publishedAt,
|
publishedAt: publishedAt,
|
||||||
examPlans: examPlans,
|
examPlans: examPlans,
|
||||||
exam: exam,
|
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/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
|
/// 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
|
/// 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;
|
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,
|
/// The languages of a studyset's cards: of their fronts and of their backs,
|
||||||
/// as BCP 47 tags; either null when not given.
|
/// as BCP 47 tags; either null when not given.
|
||||||
typedef StudysetLanguages = ({String? front, String? back});
|
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/license.dart';
|
||||||
import 'package:halkyon_learn_flutter/src/feature/studyset/model/studyset_content.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/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/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:halkyon_ui/halkyon_ui.dart';
|
||||||
import 'package:lucide_icons_flutter/lucide_icons.dart';
|
import 'package:lucide_icons_flutter/lucide_icons.dart';
|
||||||
import 'package:material_ui/material_ui.dart';
|
import 'package:material_ui/material_ui.dart';
|
||||||
|
|
@ -113,6 +115,10 @@ class _StudysetSheetState() extends State<_StudysetSheet> {
|
||||||
null => widget.insideAnother ? null : _defaultColor.name,
|
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
|
/// 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.
|
/// of the studyset it is in, or has none at the top level.
|
||||||
late var _setLanguages =
|
late var _setLanguages =
|
||||||
|
|
@ -222,6 +228,7 @@ class _StudysetSheetState() extends State<_StudysetSheet> {
|
||||||
publishedAt: initial?.publishedAt,
|
publishedAt: initial?.publishedAt,
|
||||||
examPlans: initial?.examPlans ?? const [],
|
examPlans: initial?.examPlans ?? const [],
|
||||||
exam: initial?.exam,
|
exam: initial?.exam,
|
||||||
|
subject: _subject,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -233,6 +240,7 @@ class _StudysetSheetState() extends State<_StudysetSheet> {
|
||||||
_description != RichContent.empty() ||
|
_description != RichContent.empty() ||
|
||||||
_colorName != (widget.insideAnother ? null : _defaultColor.name) ||
|
_colorName != (widget.insideAnother ? null : _defaultColor.name) ||
|
||||||
_licenseNow != _licenseBefore ||
|
_licenseNow != _licenseBefore ||
|
||||||
|
_subject != null ||
|
||||||
(_setLanguages && (_frontLanguage ?? _backLanguage) != null),
|
(_setLanguages && (_frontLanguage ?? _backLanguage) != null),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -243,6 +251,19 @@ class _StudysetSheetState() extends State<_StudysetSheet> {
|
||||||
PaletteColor.byName(widget.inheritedColorName) ??
|
PaletteColor.byName(widget.inheritedColorName) ??
|
||||||
_defaultColor;
|
_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 {
|
Future<void> _editDescription() async {
|
||||||
if (await editRichText(
|
if (await editRichText(
|
||||||
context,
|
context,
|
||||||
|
|
@ -330,6 +351,19 @@ class _StudysetSheetState() extends State<_StudysetSheet> {
|
||||||
description: _description,
|
description: _description,
|
||||||
onTap: _editDescription,
|
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),
|
const SizedBox(height: 24),
|
||||||
Text(l10n.studysetSheetColor, style: theme.textTheme.labelLarge),
|
Text(l10n.studysetSheetColor, style: theme.textTheme.labelLarge),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
|
|
@ -552,6 +586,37 @@ class _StudysetSheetState() extends State<_StudysetSheet> {
|
||||||
|
|
||||||
/// The licence, typed: any text, suggesting the [knownLicenses] that hold
|
/// The licence, typed: any text, suggesting the [knownLicenses] that hold
|
||||||
/// what is typed so far.
|
/// 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({
|
class const _LicenseField({
|
||||||
required final TextEditingController controller,
|
required final TextEditingController controller,
|
||||||
required final FocusNode focusNode,
|
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,
|
licenseYear: 2006,
|
||||||
frontLanguage: 'ja',
|
frontLanguage: 'ja',
|
||||||
backLanguage: 'en',
|
backLanguage: 'en',
|
||||||
|
subject: 'languages',
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(decodeStudysetPayload(encodeStudysetPayload(content)), content);
|
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', () {
|
test('leaves out what is not given', () {
|
||||||
final payload = encodeStudysetPayload(
|
final payload = encodeStudysetPayload(
|
||||||
StudysetContent(title: 'Japanese', description: RichContent.empty()),
|
StudysetContent(title: 'Japanese', description: RichContent.empty()),
|
||||||
|
|
@ -59,7 +75,7 @@ void main() {
|
||||||
expect(json(payload).keys, {'schemaVersion', 'title', 'description'});
|
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(
|
final payload = encodeStudysetPayload(
|
||||||
StudysetContent(
|
StudysetContent(
|
||||||
title: 'Japanese',
|
title: 'Japanese',
|
||||||
|
|
@ -68,17 +84,17 @@ void main() {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(studysetSchemaVersion, 7);
|
expect(studysetSchemaVersion, 8);
|
||||||
expect(json(payload)['schemaVersion'], studysetSchemaVersion);
|
expect(json(payload)['schemaVersion'], studysetSchemaVersion);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('without study options, a bibliography or exam plans writes the '
|
test('without study options, a bibliography, exam plans or a subject '
|
||||||
'seventh schema all the same', () {
|
'writes the eighth schema all the same', () {
|
||||||
final payload = encodeStudysetPayload(
|
final payload = encodeStudysetPayload(
|
||||||
StudysetContent(title: 'Japanese', description: RichContent.empty()),
|
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('study')));
|
||||||
expect(json(payload).keys, isNot(contains('bibliography')));
|
expect(json(payload).keys, isNot(contains('bibliography')));
|
||||||
expect(json(payload).keys, isNot(contains('examPlans')));
|
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.dart';
|
||||||
import 'package:halkyon_learn_flutter/src/feature/studyset/model/studyset_content.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/studyset_tree.dart';
|
||||||
|
import 'package:halkyon_learn_flutter/src/feature/studyset/model/subject.dart';
|
||||||
|
|
||||||
Studyset studyset(
|
Studyset studyset(
|
||||||
String id, {
|
String id, {
|
||||||
|
|
@ -196,6 +197,32 @@ void main() {
|
||||||
expect(colorNameOf('german', all), isNull);
|
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 '
|
test('a studyset without languages of its own takes those of the nearest '
|
||||||
'studyset above it that gives any, both together', () {
|
'studyset above it that gives any, both together', () {
|
||||||
Studyset speaking(
|
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/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/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/studyset_card.dart';
|
||||||
|
import 'package:halkyon_learn_flutter/src/feature/studyset/widget/subject_picker.dart';
|
||||||
import 'package:halkyon_ui/halkyon_ui.dart';
|
import 'package:halkyon_ui/halkyon_ui.dart';
|
||||||
import 'package:lucide_icons_flutter/lucide_icons.dart';
|
import 'package:lucide_icons_flutter/lucide_icons.dart';
|
||||||
import 'package:material_ui/material_ui.dart';
|
import 'package:material_ui/material_ui.dart';
|
||||||
|
|
@ -74,6 +75,7 @@ void main() {
|
||||||
DateTime? archivedAt,
|
DateTime? archivedAt,
|
||||||
DateTime? starredAt,
|
DateTime? starredAt,
|
||||||
String? publishedAt,
|
String? publishedAt,
|
||||||
|
String? subject,
|
||||||
bool private = false,
|
bool private = false,
|
||||||
}) => gateway.receiveRemoteChange(
|
}) => gateway.receiveRemoteChange(
|
||||||
id,
|
id,
|
||||||
|
|
@ -86,6 +88,7 @@ void main() {
|
||||||
? null
|
? null
|
||||||
: const PublishedRef(publication: 'p', item: 'i', version: 1),
|
: const PublishedRef(publication: 'p', item: 'i', version: 1),
|
||||||
publishedAt: publishedAt,
|
publishedAt: publishedAt,
|
||||||
|
subject: subject,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
archivedAt: archivedAt,
|
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 {
|
testWidgets('creating waits for a title', (tester) async {
|
||||||
await showLibrary(tester);
|
await showLibrary(tester);
|
||||||
await tester.tap(find.text('Create studyset'));
|
await tester.tap(find.text('Create studyset'));
|
||||||
|
|
|
||||||
|
|
@ -396,6 +396,11 @@ void main() {
|
||||||
tester.getRect(find.text('Inherit')).left,
|
tester.getRect(find.text('Inherit')).left,
|
||||||
greaterThan(tester.getRect(find.byType(PaletteColorPicker)).right),
|
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.enterText(find.widgetWithText(TextField, 'Title'), 'Kana');
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
await tester.tap(find.text('Create'));
|
await tester.tap(find.text('Create'));
|
||||||
|
|
|
||||||
98
docs/adr/0041-learn-subjects.md
Normal file
98
docs/adr/0041-learn-subjects.md
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
# ADR 0041: Learn's subjects: one per studyset, from a fixed list
|
||||||
|
|
||||||
|
- **Status:** decided 9 and 10 Nov 2026 by the developer: D1 to D4 and D6 are their own
|
||||||
|
choices, D5 and D7 to D9 the agent's recommendations, which they confirmed. D1 to D7
|
||||||
|
are built; D8 (the library) is next, and D10 later.
|
||||||
|
- **Builds on:** ADR 0002 (payloads and their schemas), ADR 0005 (publishing), ADR 0031
|
||||||
|
(the palette's twenty colours).
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
A library of a hundred studysets, most of them computing books and language courses,
|
||||||
|
shows them by title alone. Vaia files every studyset under a subject from a fixed
|
||||||
|
list, shown on its card with a stripe in the subject's colour, and lets the library be
|
||||||
|
narrowed to one.
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
|
||||||
|
- **D1.** One subject per studyset, from a fixed list of about Vaia's breadth, with
|
||||||
|
*Other / not sure* last.
|
||||||
|
- **D2.** Each subject has a Lucide icon and a default colour from the palette. Choosing
|
||||||
|
one gives the studyset that colour, which stays the studyset's to change.
|
||||||
|
- **D3.** Languages are one subject, *Languages*. The language itself comes from the
|
||||||
|
studyset's front language, which every course already gives.
|
||||||
|
- **D4.** Computing is five subjects: *Computer science*, *Software engineering*, *Data
|
||||||
|
science and AI*, *IT and operations*, *Security and privacy*. Most of the library is
|
||||||
|
computing, and a single subject would narrow nothing.
|
||||||
|
- **D5.** The subject sits on the top studyset alone, which its sub-studysets show as
|
||||||
|
theirs (`subjectOf`); their sheet offers none. It is kept in the studyset's sealed
|
||||||
|
payload as `subject`, a subject's ID (schema 8), and so goes into archives and is
|
||||||
|
published with the studyset, for Explore to filter by later.
|
||||||
|
- **D6.** The list is the agent's draft of 10 Nov 2026, taken as it is (below).
|
||||||
|
- **D7.** IDs are never renamed. An ID this app version doesn't know (a newer one's) is
|
||||||
|
kept as it is and reads as no subject until another is picked.
|
||||||
|
- **D8.** The library, as Vaia's: a dropdown of the subjects in use, cards naming the
|
||||||
|
subject under the title with a stripe in its colour, sections by recency (the last 7
|
||||||
|
days, the last month, older), the archive last.
|
||||||
|
- **D9.** Existing studysets: the agent writes a subject into each of the developer's 72
|
||||||
|
archives not yet imported; those already in the library get one by hand.
|
||||||
|
- **D10.** Explore filters by subject later; its releases don't keep the subject in a
|
||||||
|
column yet (it is in each release's repository).
|
||||||
|
|
||||||
|
## The list (D6)
|
||||||
|
|
||||||
|
ID, English and German name, Lucide icon, default colour (`Subject`, `subject.dart`).
|
||||||
|
|
||||||
|
| Group | ID | English | German | Icon | Colour |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| Sciences and health | `biology` | Biology | Biologie | dna | fern |
|
||||||
|
| | `chemistry` | Chemistry | Chemie | flask-conical | reef |
|
||||||
|
| | `physics` | Physics | Physik | atom | slateblue |
|
||||||
|
| | `astronomy` | Astronomy | Astronomie | telescope | enzian |
|
||||||
|
| | `earth-sciences` | Earth sciences | Geowissenschaften | mountain | sahara |
|
||||||
|
| | `environmental-science` | Environmental science | Umweltwissenschaften | leaf | forest |
|
||||||
|
| | `medicine` | Medicine | Medizin | stethoscope | cerise |
|
||||||
|
| | `nursing` | Nursing | Pflege | heart-pulse | strawberry |
|
||||||
|
| | `pharmacy` | Pharmacy | Pharmazie | pill | pink |
|
||||||
|
| | `nutrition` | Nutrition | Ernährung | apple | carrot |
|
||||||
|
| | `psychology` | Psychology | Psychologie | brain | plum |
|
||||||
|
| | `sports-science` | Sports science | Sportwissenschaft | dumbbell | copper |
|
||||||
|
| Mathematics and computing | `mathematics` | Mathematics | Mathematik | sigma | cobalt |
|
||||||
|
| | `statistics` | Statistics | Statistik | chart-column | ocean |
|
||||||
|
| | `computer-science` | Computer science | Informatik | cpu | slateblue |
|
||||||
|
| | `software-engineering` | Software engineering | Softwareentwicklung | code | enzian |
|
||||||
|
| | `data-science-ai` | Data science and AI | Data Science und KI | brain-circuit | purple |
|
||||||
|
| | `it-operations` | IT and operations | IT und Betrieb | server | pine |
|
||||||
|
| | `security-privacy` | Security and privacy | Sicherheit und Datenschutz | shield-check | forest |
|
||||||
|
| | `engineering` | Engineering | Ingenieurwesen | cog | soil |
|
||||||
|
| | `architecture` | Architecture | Architektur | building-2 | sahara |
|
||||||
|
| Society | `history` | History | Geschichte | landmark | soil |
|
||||||
|
| | `geography` | Geography | Geografie | globe | pacific |
|
||||||
|
| | `politics` | Politics | Politik | vote | cobalt |
|
||||||
|
| | `law` | Law | Recht | scale | pickle |
|
||||||
|
| | `economics` | Economics | Volkswirtschaft | trending-up | olive |
|
||||||
|
| | `business` | Business | Betriebswirtschaft | briefcase | copper |
|
||||||
|
| | `marketing` | Marketing | Marketing | megaphone | carrot |
|
||||||
|
| | `accounting-finance` | Accounting and finance | Rechnungswesen und Finanzen | calculator | pine |
|
||||||
|
| | `sociology` | Sociology | Soziologie | users | plum |
|
||||||
|
| | `anthropology` | Anthropology | Anthropologie | footprints | sahara |
|
||||||
|
| | `education` | Education | Pädagogik | school | ocean |
|
||||||
|
| | `media-studies` | Media studies | Medienwissenschaft | tv | pink |
|
||||||
|
| | `philosophy` | Philosophy | Philosophie | lightbulb | purple |
|
||||||
|
| | `religious-studies` | Religious studies | Religionswissenschaft | book-open | olive |
|
||||||
|
| Languages and arts | `languages` | Languages | Sprachen | languages | cerise |
|
||||||
|
| | `literature` | Literature | Literatur | book-marked | strawberry |
|
||||||
|
| | `linguistics` | Linguistics | Linguistik | speech | enzian |
|
||||||
|
| | `art-design` | Art and design | Kunst und Design | palette | pink |
|
||||||
|
| | `music` | Music | Musik | music | plum |
|
||||||
|
| | `theatre-film` | Theatre and film | Theater und Film | clapperboard | cerise |
|
||||||
|
| Everyday | `cooking-hospitality` | Cooking and hospitality | Kochen und Gastronomie | chef-hat | carrot |
|
||||||
|
| | `driving-licences` | Driving and licences | Führerschein und Lizenzen | car | soil |
|
||||||
|
| Other | `other` | Other / not sure | Sonstiges / unsicher | circle-question-mark | none |
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- A studyset saved by this version is in schema 8, which older versions refuse to read,
|
||||||
|
as every schema before (ADR 0002): an archive with subjects imports from this version
|
||||||
|
on.
|
||||||
|
- Adding a subject is cheap; renaming an ID isn't (D7), so names change, IDs don't.
|
||||||
Loading…
Reference in a new issue