172 lines
4.4 KiB
Dart
172 lines
4.4 KiB
Dart
enum QuizMode { kanjiToEnglish, englishToKanji, reading }
|
|
|
|
class SrsItem {
|
|
final int kanjiId;
|
|
final QuizMode quizMode;
|
|
final String? readingType; // 'onyomi' or 'kunyomi'
|
|
int srsStage;
|
|
DateTime lastAsked;
|
|
|
|
SrsItem({
|
|
required this.kanjiId,
|
|
required this.quizMode,
|
|
this.readingType,
|
|
this.srsStage = 0,
|
|
DateTime? lastAsked,
|
|
}) : lastAsked = lastAsked ?? DateTime.now();
|
|
}
|
|
|
|
class KanjiItem {
|
|
final int id;
|
|
final int level;
|
|
final String characters;
|
|
final List<String> meanings;
|
|
final List<String> onyomi;
|
|
final List<String> kunyomi;
|
|
final Map<String, SrsItem> srsItems = {};
|
|
|
|
KanjiItem({
|
|
required this.id,
|
|
required this.level,
|
|
required this.characters,
|
|
required this.meanings,
|
|
required this.onyomi,
|
|
required this.kunyomi,
|
|
});
|
|
|
|
factory KanjiItem.fromSubject(Map<String, dynamic> subj) {
|
|
final int id = subj['id'] as int;
|
|
final data = subj['data'] as Map<String, dynamic>;
|
|
final int level = data['level'] as int;
|
|
final String characters = (data['characters'] ?? '') as String;
|
|
final List<String> meanings = <String>[];
|
|
final List<String> onyomi = <String>[];
|
|
final List<String> kunyomi = <String>[];
|
|
|
|
if (data['meanings'] != null) {
|
|
for (final m in data['meanings'] as List) {
|
|
meanings.add((m['meaning'] as String).toLowerCase());
|
|
}
|
|
}
|
|
|
|
if (data['readings'] != null) {
|
|
for (final r in data['readings'] as List) {
|
|
final typ = r['type'] as String? ?? '';
|
|
final reading = r['reading'] as String? ?? '';
|
|
if (typ == 'onyomi') {
|
|
onyomi.add(_katakanaToHiragana(reading));
|
|
} else if (typ == 'kunyomi') {
|
|
kunyomi.add(reading);
|
|
}
|
|
}
|
|
}
|
|
|
|
return KanjiItem(
|
|
id: id,
|
|
level: level,
|
|
characters: characters,
|
|
meanings: meanings,
|
|
onyomi: onyomi,
|
|
kunyomi: kunyomi,
|
|
);
|
|
}
|
|
}
|
|
|
|
String _katakanaToHiragana(String input) {
|
|
final buf = StringBuffer();
|
|
for (final r in input.runes) {
|
|
if (r >= 0x30A1 && r <= 0x30FA) {
|
|
buf.writeCharCode(r - 0x60);
|
|
} else {
|
|
buf.writeCharCode(r);
|
|
}
|
|
}
|
|
return buf.toString();
|
|
}
|
|
|
|
enum VocabQuizMode { vocabToEnglish, englishToVocab, audioToEnglish }
|
|
|
|
class VocabSrsItem {
|
|
final int vocabId;
|
|
final VocabQuizMode quizMode;
|
|
int srsStage;
|
|
DateTime lastAsked;
|
|
|
|
VocabSrsItem({
|
|
required this.vocabId,
|
|
required this.quizMode,
|
|
this.srsStage = 0,
|
|
DateTime? lastAsked,
|
|
}) : lastAsked = lastAsked ?? DateTime.now();
|
|
}
|
|
|
|
class PronunciationAudio {
|
|
final String url;
|
|
final String gender;
|
|
|
|
PronunciationAudio({required this.url, required this.gender});
|
|
}
|
|
|
|
class VocabularyItem {
|
|
final int id;
|
|
final int level;
|
|
final String characters;
|
|
final List<String> meanings;
|
|
final List<String> readings;
|
|
final List<PronunciationAudio> pronunciationAudios;
|
|
final Map<String, VocabSrsItem> srsItems = {};
|
|
|
|
VocabularyItem(
|
|
{required this.id,
|
|
required this.level,
|
|
required this.characters,
|
|
required this.meanings,
|
|
required this.readings,
|
|
required this.pronunciationAudios});
|
|
|
|
factory VocabularyItem.fromSubject(Map<String, dynamic> subj) {
|
|
final int id = subj['id'] as int;
|
|
final data = subj['data'] as Map<String, dynamic>;
|
|
final int level = data['level'] as int;
|
|
final String characters = (data['characters'] ?? '') as String;
|
|
final List<String> meanings = <String>[];
|
|
final List<String> readings = <String>[];
|
|
final List<PronunciationAudio> pronunciationAudios = <PronunciationAudio>[];
|
|
|
|
if (data['meanings'] != null) {
|
|
for (final m in data['meanings'] as List) {
|
|
meanings.add((m['meaning'] as String).toLowerCase());
|
|
}
|
|
}
|
|
|
|
if (data['readings'] != null) {
|
|
for (final r in data['readings'] as List) {
|
|
readings.add(r['reading'] as String);
|
|
}
|
|
}
|
|
|
|
if (data['pronunciation_audios'] != null) {
|
|
for (final audio in data['pronunciation_audios'] as List) {
|
|
final url = audio['url'] as String?;
|
|
final metadata = audio['metadata'] as Map<String, dynamic>?;
|
|
final gender = metadata?['gender'] as String?;
|
|
|
|
if (url != null && gender != null) {
|
|
pronunciationAudios.add(PronunciationAudio(
|
|
url: url,
|
|
gender: gender,
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
return VocabularyItem(
|
|
id: id,
|
|
level: level,
|
|
characters: characters,
|
|
meanings: meanings,
|
|
readings: readings,
|
|
pronunciationAudios: pronunciationAudios);
|
|
}
|
|
}
|