change a bunch of stuff, seperate tracking for progress, updated custom srs layout
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'package:path/path.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:sqflite/sqflite.dart';
|
||||
@@ -263,154 +262,5 @@ class DeckRepository {
|
||||
return items;
|
||||
}
|
||||
|
||||
Future<List<VocabSrsItem>> getVocabSrsItems(int vocabId) async {
|
||||
final db = await _openDb();
|
||||
final rows = await db.query(
|
||||
'srs_vocab_items',
|
||||
where: 'vocabId = ?',
|
||||
whereArgs: [vocabId],
|
||||
);
|
||||
return rows.map((r) {
|
||||
return VocabSrsItem(
|
||||
vocabId: r['vocabId'] as int,
|
||||
quizMode: VocabQuizMode.values.firstWhere(
|
||||
(e) => e.toString() == r['quizMode'] as String,
|
||||
),
|
||||
srsStage: r['srsStage'] as int,
|
||||
lastAsked: DateTime.parse(r['lastAsked'] as String),
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
Future<void> updateVocabSrsItem(VocabSrsItem item) async {
|
||||
final db = await _openDb();
|
||||
await db.update(
|
||||
'srs_vocab_items',
|
||||
{
|
||||
'srsStage': item.srsStage,
|
||||
'lastAsked': item.lastAsked.toIso8601String(),
|
||||
},
|
||||
where: 'vocabId = ? AND quizMode = ?',
|
||||
whereArgs: [item.vocabId, item.quizMode.toString()],
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> insertVocabSrsItem(VocabSrsItem item) async {
|
||||
final db = await _openDb();
|
||||
await db.insert('srs_vocab_items', {
|
||||
'vocabId': item.vocabId,
|
||||
'quizMode': item.quizMode.toString(),
|
||||
'srsStage': item.srsStage,
|
||||
'lastAsked': item.lastAsked.toIso8601String(),
|
||||
}, conflictAlgorithm: ConflictAlgorithm.replace);
|
||||
}
|
||||
|
||||
Future<void> saveVocabulary(List<VocabularyItem> items) async {
|
||||
final db = await _openDb();
|
||||
final batch = db.batch();
|
||||
for (final it in items) {
|
||||
final audios = it.pronunciationAudios
|
||||
.map((a) => {'url': a.url, 'gender': a.gender})
|
||||
.toList();
|
||||
batch.insert('vocabulary', {
|
||||
'id': it.id,
|
||||
'level': it.level,
|
||||
'characters': it.characters,
|
||||
'meanings': it.meanings.join('|'),
|
||||
'readings': it.readings.join('|'),
|
||||
'pronunciation_audios': jsonEncode(audios),
|
||||
}, conflictAlgorithm: ConflictAlgorithm.replace);
|
||||
}
|
||||
await batch.commit(noResult: true);
|
||||
}
|
||||
|
||||
Future<List<VocabularyItem>> loadVocabulary() async {
|
||||
final db = await _openDb();
|
||||
final rows = await db.query('vocabulary');
|
||||
final vocabItems = rows.map((r) {
|
||||
final audiosRaw = r['pronunciation_audios'] as String?;
|
||||
final List<PronunciationAudio> audios = [];
|
||||
if (audiosRaw != null && audiosRaw.isNotEmpty) {
|
||||
try {
|
||||
final decoded = jsonDecode(audiosRaw) as List;
|
||||
for (final audioData in decoded) {
|
||||
audios.add(
|
||||
PronunciationAudio(
|
||||
url: audioData['url'] as String,
|
||||
gender: audioData['gender'] as String,
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
// Error decoding, so we'll just have no audio for this item
|
||||
}
|
||||
}
|
||||
return VocabularyItem(
|
||||
id: r['id'] as int,
|
||||
level: r['level'] as int? ?? 0,
|
||||
characters: r['characters'] as String,
|
||||
meanings: (r['meanings'] as String)
|
||||
.split('|')
|
||||
.where((s) => s.isNotEmpty)
|
||||
.toList(),
|
||||
readings: (r['readings'] as String)
|
||||
.split('|')
|
||||
.where((s) => s.isNotEmpty)
|
||||
.toList(),
|
||||
pronunciationAudios: audios,
|
||||
);
|
||||
}).toList();
|
||||
|
||||
for (final item in vocabItems) {
|
||||
final srsItems = await getVocabSrsItems(item.id);
|
||||
for (final srsItem in srsItems) {
|
||||
final key = srsItem.quizMode.toString();
|
||||
item.srsItems[key] = srsItem;
|
||||
}
|
||||
}
|
||||
|
||||
return vocabItems;
|
||||
}
|
||||
|
||||
Future<List<VocabularyItem>> fetchAndCacheVocabularyFromWk([
|
||||
String? apiKey,
|
||||
]) async {
|
||||
final key = apiKey ?? _apiKey;
|
||||
if (key == null) throw Exception('API key not set');
|
||||
|
||||
final client = WkClient(key);
|
||||
final assignments = await client.fetchAllAssignments(
|
||||
subjectTypes: ['vocabulary'],
|
||||
);
|
||||
|
||||
final unlocked = <int>{};
|
||||
for (final a in assignments) {
|
||||
final data = a['data'] as Map<String, dynamic>;
|
||||
final sidRaw = data['subject_id'];
|
||||
if (sidRaw == null) continue;
|
||||
final sid = sidRaw is int ? sidRaw : int.tryParse(sidRaw.toString());
|
||||
if (sid == null) continue;
|
||||
final started = data['started_at'];
|
||||
final srs = data['srs_stage'];
|
||||
final isUnlocked = (started != null) || (srs != null && (srs as int) > 0);
|
||||
if (isUnlocked) unlocked.add(sid);
|
||||
}
|
||||
|
||||
if (unlocked.isEmpty) return [];
|
||||
|
||||
final subjects = await client.fetchSubjectsByIds(unlocked.toList());
|
||||
final items = subjects
|
||||
.where(
|
||||
(s) =>
|
||||
s['object'] == 'vocabulary' ||
|
||||
(s['data'] != null &&
|
||||
(s['data'] as Map)['object_type'] == 'vocabulary'),
|
||||
)
|
||||
.map((s) => VocabularyItem.fromSubject(s))
|
||||
.where((k) => k.characters.isNotEmpty && k.meanings.isNotEmpty)
|
||||
.toList();
|
||||
|
||||
await saveVocabulary(items);
|
||||
return items;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user