v1
This commit is contained in:
140
lib/src/services/deck_repository.dart
Normal file
140
lib/src/services/deck_repository.dart
Normal file
@@ -0,0 +1,140 @@
|
||||
import 'dart:async';
|
||||
import 'package:path/path.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:sqflite/sqflite.dart';
|
||||
import '../models/kanji_item.dart';
|
||||
import '../api/wk_client.dart';
|
||||
|
||||
class DeckRepository {
|
||||
Database? _db;
|
||||
String? _apiKey;
|
||||
|
||||
Future<void> setApiKey(String apiKey) async {
|
||||
_apiKey = apiKey;
|
||||
await saveApiKey(apiKey);
|
||||
}
|
||||
|
||||
String? get apiKey => _apiKey;
|
||||
|
||||
Future<Database> _openDb() async {
|
||||
if (_db != null) return _db!;
|
||||
final dir = await getApplicationDocumentsDirectory();
|
||||
final path = join(dir.path, 'wanikani_srs.db');
|
||||
|
||||
_db = await openDatabase(
|
||||
path,
|
||||
version: 2,
|
||||
onCreate: (db, version) async {
|
||||
await db.execute(
|
||||
'''CREATE TABLE kanji (id INTEGER PRIMARY KEY, characters TEXT, meanings TEXT, onyomi TEXT, kunyomi TEXT)''');
|
||||
await db.execute(
|
||||
'''CREATE TABLE settings (key TEXT PRIMARY KEY, value TEXT)''');
|
||||
},
|
||||
onUpgrade: (db, oldVersion, newVersion) async {
|
||||
await db.execute(
|
||||
'''CREATE TABLE IF NOT EXISTS settings (key TEXT PRIMARY KEY, value TEXT)''');
|
||||
},
|
||||
);
|
||||
|
||||
return _db!;
|
||||
}
|
||||
|
||||
Future<void> saveApiKey(String apiKey) async {
|
||||
final db = await _openDb();
|
||||
await db.insert(
|
||||
'settings',
|
||||
{'key': 'apiKey', 'value': apiKey},
|
||||
conflictAlgorithm: ConflictAlgorithm.replace,
|
||||
);
|
||||
}
|
||||
|
||||
Future<String?> loadApiKey() async {
|
||||
final db = await _openDb();
|
||||
final rows =
|
||||
await db.query('settings', where: 'key = ?', whereArgs: ['apiKey']);
|
||||
if (rows.isNotEmpty) {
|
||||
_apiKey = rows.first['value'] as String;
|
||||
return _apiKey;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<void> saveKanji(List<KanjiItem> items) async {
|
||||
final db = await _openDb();
|
||||
final batch = db.batch();
|
||||
for (final it in items) {
|
||||
batch.insert(
|
||||
'kanji',
|
||||
{
|
||||
'id': it.id,
|
||||
'characters': it.characters,
|
||||
'meanings': it.meanings.join('|'),
|
||||
'onyomi': it.onyomi.join('|'),
|
||||
'kunyomi': it.kunyomi.join('|'),
|
||||
},
|
||||
conflictAlgorithm: ConflictAlgorithm.replace,
|
||||
);
|
||||
}
|
||||
await batch.commit(noResult: true);
|
||||
}
|
||||
|
||||
Future<List<KanjiItem>> loadKanji() async {
|
||||
final db = await _openDb();
|
||||
final rows = await db.query('kanji');
|
||||
return rows
|
||||
.map((r) => KanjiItem(
|
||||
id: r['id'] as int,
|
||||
characters: r['characters'] as String,
|
||||
meanings: (r['meanings'] as String)
|
||||
.split('|')
|
||||
.where((s) => s.isNotEmpty)
|
||||
.toList(),
|
||||
onyomi: (r['onyomi'] as String)
|
||||
.split('|')
|
||||
.where((s) => s.isNotEmpty)
|
||||
.toList(),
|
||||
kunyomi: (r['kunyomi'] as String)
|
||||
.split('|')
|
||||
.where((s) => s.isNotEmpty)
|
||||
.toList(),
|
||||
))
|
||||
.toList();
|
||||
}
|
||||
|
||||
Future<List<KanjiItem>> fetchAndCacheFromWk([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: ['kanji']);
|
||||
|
||||
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'] == 'kanji' ||
|
||||
(s['data'] != null &&
|
||||
(s['data'] as Map)['object_type'] == 'kanji'))
|
||||
.map((s) => KanjiItem.fromSubject(s))
|
||||
.where((k) => k.characters.isNotEmpty && k.meanings.isNotEmpty)
|
||||
.toList();
|
||||
|
||||
await saveKanji(items);
|
||||
return items;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user