themes and some refractoring
This commit is contained in:
@@ -1,14 +1,14 @@
|
||||
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 '../models/srs_item.dart';
|
||||
import '../api/wk_client.dart';
|
||||
import 'database_constants.dart';
|
||||
import 'database_helper.dart';
|
||||
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||
|
||||
class DeckRepository {
|
||||
Database? _db;
|
||||
String? _apiKey;
|
||||
|
||||
Future<void> setApiKey(String apiKey) async {
|
||||
@@ -18,146 +18,75 @@ class DeckRepository {
|
||||
|
||||
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: 7,
|
||||
onCreate: (db, version) async {
|
||||
await db.execute(
|
||||
'''CREATE TABLE kanji (id INTEGER PRIMARY KEY, level INTEGER, characters TEXT, meanings TEXT, onyomi TEXT, kunyomi TEXT)''',
|
||||
);
|
||||
await db.execute(
|
||||
'''CREATE TABLE settings (key TEXT PRIMARY KEY, value TEXT)''',
|
||||
);
|
||||
await db.execute(
|
||||
'''CREATE TABLE srs_items (kanjiId INTEGER, quizMode TEXT, readingType TEXT, srsStage INTEGER, lastAsked TEXT, PRIMARY KEY (kanjiId, quizMode, readingType))''',
|
||||
);
|
||||
await db.execute(
|
||||
'''CREATE TABLE vocabulary (id INTEGER PRIMARY KEY, level INTEGER, characters TEXT, meanings TEXT, readings TEXT, pronunciation_audios TEXT)''',
|
||||
);
|
||||
await db.execute(
|
||||
'''CREATE TABLE srs_vocab_items (vocabId INTEGER, quizMode TEXT, srsStage INTEGER, lastAsked TEXT, PRIMARY KEY (vocabId, quizMode))''',
|
||||
);
|
||||
},
|
||||
onUpgrade: (db, oldVersion, newVersion) async {
|
||||
if (oldVersion < 2) {
|
||||
await db.execute(
|
||||
'''CREATE TABLE IF NOT EXISTS settings (key TEXT PRIMARY KEY, value TEXT)''',
|
||||
);
|
||||
}
|
||||
if (oldVersion < 3) {
|
||||
// Migration from version 2 to 3 was flawed, so we just drop the columns if they exist
|
||||
}
|
||||
if (oldVersion < 4) {
|
||||
await db.execute(
|
||||
'''CREATE TABLE srs_items (kanjiId INTEGER, quizMode TEXT, readingType TEXT, srsStage INTEGER, lastAsked TEXT, PRIMARY KEY (kanjiId, quizMode, readingType))''',
|
||||
);
|
||||
// We are not migrating the old srs data, as it was not mode-specific.
|
||||
// Old columns will be dropped.
|
||||
}
|
||||
if (oldVersion < 5) {
|
||||
await db.execute(
|
||||
'''CREATE TABLE vocabulary (id INTEGER PRIMARY KEY, characters TEXT, meanings TEXT, readings TEXT)''',
|
||||
);
|
||||
await db.execute(
|
||||
'''CREATE TABLE srs_vocab_items (vocabId INTEGER, quizMode TEXT, srsStage INTEGER, lastAsked TEXT, PRIMARY KEY (vocabId, quizMode))''',
|
||||
);
|
||||
}
|
||||
if (oldVersion < 6) {
|
||||
try {
|
||||
await db.execute(
|
||||
'ALTER TABLE vocabulary ADD COLUMN pronunciation_audios TEXT',
|
||||
);
|
||||
} catch (_) {
|
||||
// Ignore error, column might already exist
|
||||
}
|
||||
}
|
||||
if (oldVersion < 7) {
|
||||
try {
|
||||
await db.execute('ALTER TABLE kanji ADD COLUMN level INTEGER');
|
||||
await db.execute('ALTER TABLE vocabulary ADD COLUMN level INTEGER');
|
||||
} catch (_) {
|
||||
// Ignore error, column might already exist
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return _db!;
|
||||
}
|
||||
|
||||
Future<void> saveApiKey(String apiKey) async {
|
||||
final db = await _openDb();
|
||||
await db.insert('settings', {
|
||||
'key': 'apiKey',
|
||||
'value': apiKey,
|
||||
final db = await DatabaseHelper().db;
|
||||
await db.insert(DbConstants.settingsTable, {
|
||||
DbConstants.keyColumn: 'apiKey',
|
||||
DbConstants.valueColumn: apiKey,
|
||||
}, conflictAlgorithm: ConflictAlgorithm.replace);
|
||||
}
|
||||
|
||||
Future<String?> loadApiKey() async {
|
||||
String? envApiKey;
|
||||
try {
|
||||
envApiKey = dotenv.env['WANIKANI_API_KEY'];
|
||||
} catch (e) {
|
||||
envApiKey = null;
|
||||
}
|
||||
|
||||
if (envApiKey != null && envApiKey.isNotEmpty) {
|
||||
_apiKey = envApiKey;
|
||||
return _apiKey;
|
||||
}
|
||||
|
||||
final db = await _openDb();
|
||||
final db = await DatabaseHelper().db;
|
||||
final rows = await db.query(
|
||||
'settings',
|
||||
where: 'key = ?',
|
||||
DbConstants.settingsTable,
|
||||
where: '${DbConstants.keyColumn} = ?',
|
||||
whereArgs: ['apiKey'],
|
||||
);
|
||||
|
||||
if (rows.isNotEmpty) {
|
||||
_apiKey = rows.first['value'] as String;
|
||||
_apiKey = rows.first[DbConstants.valueColumn] as String;
|
||||
return _apiKey;
|
||||
}
|
||||
|
||||
try {
|
||||
final envApiKey = dotenv.env['WANIKANI_API_KEY'];
|
||||
if (envApiKey != null && envApiKey.isNotEmpty) {
|
||||
await saveApiKey(envApiKey);
|
||||
_apiKey = envApiKey;
|
||||
return _apiKey;
|
||||
}
|
||||
} catch (e) {
|
||||
// dotenv is not initialized
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<void> saveKanji(List<KanjiItem> items) async {
|
||||
final db = await _openDb();
|
||||
final db = await DatabaseHelper().db;
|
||||
final batch = db.batch();
|
||||
for (final it in items) {
|
||||
batch.insert('kanji', {
|
||||
'id': it.id,
|
||||
'level': it.level,
|
||||
'characters': it.characters,
|
||||
'meanings': it.meanings.join('|'),
|
||||
'onyomi': it.onyomi.join('|'),
|
||||
'kunyomi': it.kunyomi.join('|'),
|
||||
batch.insert(DbConstants.kanjiTable, {
|
||||
DbConstants.idColumn: it.id,
|
||||
DbConstants.levelColumn: it.level,
|
||||
DbConstants.charactersColumn: it.characters,
|
||||
DbConstants.meaningsColumn: it.meanings.join('|'),
|
||||
DbConstants.onyomiColumn: it.onyomi.join('|'),
|
||||
DbConstants.kunyomiColumn: 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');
|
||||
final db = await DatabaseHelper().db;
|
||||
final rows = await db.query(DbConstants.kanjiTable);
|
||||
final kanjiItems = rows
|
||||
.map(
|
||||
(r) => KanjiItem(
|
||||
id: r['id'] as int,
|
||||
level: r['level'] as int? ?? 0,
|
||||
characters: r['characters'] as String,
|
||||
meanings: (r['meanings'] as String)
|
||||
id: r[DbConstants.idColumn] as int,
|
||||
level: r[DbConstants.levelColumn] as int? ?? 0,
|
||||
characters: r[DbConstants.charactersColumn] as String,
|
||||
meanings: (r[DbConstants.meaningsColumn] as String)
|
||||
.split('|')
|
||||
.where((s) => s.isNotEmpty)
|
||||
.toList(),
|
||||
onyomi: (r['onyomi'] as String)
|
||||
onyomi: (r[DbConstants.onyomiColumn] as String)
|
||||
.split('|')
|
||||
.where((s) => s.isNotEmpty)
|
||||
.toList(),
|
||||
kunyomi: (r['kunyomi'] as String)
|
||||
kunyomi: (r[DbConstants.kunyomiColumn] as String)
|
||||
.split('|')
|
||||
.where((s) => s.isNotEmpty)
|
||||
.toList(),
|
||||
@@ -165,8 +94,23 @@ class DeckRepository {
|
||||
)
|
||||
.toList();
|
||||
|
||||
final srsRows = await db.query(DbConstants.srsItemsTable);
|
||||
final srsItemsByKanjiId = <int, List<SrsItem>>{};
|
||||
for (final r in srsRows) {
|
||||
final srsItem = SrsItem(
|
||||
subjectId: r[DbConstants.kanjiIdColumn] as int,
|
||||
quizMode: QuizMode.values.firstWhere(
|
||||
(e) => e.toString() == r[DbConstants.quizModeColumn] as String,
|
||||
),
|
||||
readingType: r[DbConstants.readingTypeColumn] as String?,
|
||||
srsStage: r[DbConstants.srsStageColumn] as int,
|
||||
lastAsked: DateTime.parse(r[DbConstants.lastAskedColumn] as String),
|
||||
);
|
||||
srsItemsByKanjiId.putIfAbsent(srsItem.subjectId, () => []).add(srsItem);
|
||||
}
|
||||
|
||||
for (final item in kanjiItems) {
|
||||
final srsItems = await getSrsItems(item.id);
|
||||
final srsItems = srsItemsByKanjiId[item.id] ?? [];
|
||||
for (final srsItem in srsItems) {
|
||||
final key = srsItem.quizMode.toString() + (srsItem.readingType ?? '');
|
||||
item.srsItems[key] = srsItem;
|
||||
@@ -176,47 +120,27 @@ class DeckRepository {
|
||||
return kanjiItems;
|
||||
}
|
||||
|
||||
Future<List<SrsItem>> getSrsItems(int kanjiId) async {
|
||||
final db = await _openDb();
|
||||
final rows = await db.query(
|
||||
'srs_items',
|
||||
where: 'kanjiId = ?',
|
||||
whereArgs: [kanjiId],
|
||||
);
|
||||
return rows.map((r) {
|
||||
return SrsItem(
|
||||
kanjiId: r['kanjiId'] as int,
|
||||
quizMode: QuizMode.values.firstWhere(
|
||||
(e) => e.toString() == r['quizMode'] as String,
|
||||
),
|
||||
readingType: r['readingType'] as String?,
|
||||
srsStage: r['srsStage'] as int,
|
||||
lastAsked: DateTime.parse(r['lastAsked'] as String),
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
Future<void> updateSrsItem(SrsItem item) async {
|
||||
final db = await _openDb();
|
||||
final db = await DatabaseHelper().db;
|
||||
await db.update(
|
||||
'srs_items',
|
||||
DbConstants.srsItemsTable,
|
||||
{
|
||||
'srsStage': item.srsStage,
|
||||
'lastAsked': item.lastAsked.toIso8601String(),
|
||||
DbConstants.srsStageColumn: item.srsStage,
|
||||
DbConstants.lastAskedColumn: item.lastAsked.toIso8601String(),
|
||||
},
|
||||
where: 'kanjiId = ? AND quizMode = ? AND readingType = ?',
|
||||
whereArgs: [item.kanjiId, item.quizMode.toString(), item.readingType],
|
||||
where: '${DbConstants.kanjiIdColumn} = ? AND ${DbConstants.quizModeColumn} = ? AND ${DbConstants.readingTypeColumn} = ?',
|
||||
whereArgs: [item.subjectId, item.quizMode.toString(), item.readingType],
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> insertSrsItem(SrsItem item) async {
|
||||
final db = await _openDb();
|
||||
await db.insert('srs_items', {
|
||||
'kanjiId': item.kanjiId,
|
||||
'quizMode': item.quizMode.toString(),
|
||||
'readingType': item.readingType,
|
||||
'srsStage': item.srsStage,
|
||||
'lastAsked': item.lastAsked.toIso8601String(),
|
||||
final db = await DatabaseHelper().db;
|
||||
await db.insert(DbConstants.srsItemsTable, {
|
||||
DbConstants.kanjiIdColumn: item.subjectId,
|
||||
DbConstants.quizModeColumn: item.quizMode.toString(),
|
||||
DbConstants.readingTypeColumn: item.readingType,
|
||||
DbConstants.srsStageColumn: item.srsStage,
|
||||
DbConstants.lastAskedColumn: item.lastAsked.toIso8601String(),
|
||||
}, conflictAlgorithm: ConflictAlgorithm.replace);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user