change a bunch of stuff, seperate tracking for progress, updated custom srs layout
This commit is contained in:
@@ -1,11 +1,15 @@
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import '../models/kanji_item.dart';
|
||||
import '../services/deck_repository.dart';
|
||||
import 'package:hirameki_srs/src/services/vocab_deck_repository.dart';
|
||||
import '../services/custom_deck_repository.dart';
|
||||
import '../models/custom_kanji_item.dart';
|
||||
import 'settings_screen.dart';
|
||||
import 'custom_card_details_screen.dart';
|
||||
import 'add_card_screen.dart';
|
||||
|
||||
class BrowseScreen extends StatefulWidget {
|
||||
const BrowseScreen({super.key});
|
||||
@@ -222,8 +226,7 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
final item = items[index];
|
||||
return GestureDetector(
|
||||
onTap: () => _showReadingsDialog(item),
|
||||
child:
|
||||
_buildSrsItemCard(item.characters, item.srsItems.values.toList()),
|
||||
child: _buildSrsItemCard(item),
|
||||
);
|
||||
},
|
||||
padding: const EdgeInsets.all(8),
|
||||
@@ -241,53 +244,87 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
}
|
||||
|
||||
Widget _buildVocabListTile(VocabularyItem item) {
|
||||
final avgSrsStage = item.srsItems.isNotEmpty
|
||||
? item.srsItems.values
|
||||
.map((s) => s.srsStage)
|
||||
.reduce((a, b) => a + b) /
|
||||
item.srsItems.length
|
||||
: 0.0;
|
||||
final requiredModes = <String>[
|
||||
VocabQuizMode.vocabToEnglish.toString(),
|
||||
VocabQuizMode.englishToVocab.toString(),
|
||||
VocabQuizMode.audioToEnglish.toString(),
|
||||
];
|
||||
|
||||
return Card(
|
||||
color: const Color(0xFF1E1E1E),
|
||||
margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
item.characters,
|
||||
style: const TextStyle(fontSize: 24, color: Colors.white),
|
||||
int minSrsStage = 9;
|
||||
|
||||
for (final mode in requiredModes) {
|
||||
final srsItem = item.srsItems[mode];
|
||||
if (srsItem == null) {
|
||||
minSrsStage = 0;
|
||||
break;
|
||||
}
|
||||
if (srsItem.srsStage < minSrsStage) {
|
||||
minSrsStage = srsItem.srsStage;
|
||||
}
|
||||
}
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () => _showVocabDetailsDialog(context, item),
|
||||
child: Card(
|
||||
color: const Color(0xFF1E1E1E),
|
||||
margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
item.characters,
|
||||
style: const TextStyle(fontSize: 24, color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
item.meanings.join(', '),
|
||||
style: const TextStyle(color: Colors.grey),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_buildSrsIndicator(avgSrsStage.round()),
|
||||
],
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
item.meanings.join(', '),
|
||||
style: const TextStyle(color: Colors.grey),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_buildSrsIndicator(minSrsStage),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSrsItemCard(String characters, List<dynamic> srsItems) {
|
||||
final avgSrsStage = srsItems.isNotEmpty
|
||||
? srsItems.map((s) => s.srsStage).reduce((a, b) => a + b) /
|
||||
srsItems.length
|
||||
: 0.0;
|
||||
Widget _buildSrsItemCard(KanjiItem item) {
|
||||
final requiredModes = <String>[
|
||||
QuizMode.kanjiToEnglish.toString(),
|
||||
QuizMode.englishToKanji.toString(),
|
||||
];
|
||||
if (item.onyomi.isNotEmpty) {
|
||||
requiredModes.add('${QuizMode.reading}onyomi');
|
||||
}
|
||||
if (item.kunyomi.isNotEmpty) {
|
||||
requiredModes.add('${QuizMode.reading}kunyomi');
|
||||
}
|
||||
|
||||
int minSrsStage = 9;
|
||||
|
||||
for (final mode in requiredModes) {
|
||||
final srsItem = item.srsItems[mode];
|
||||
if (srsItem == null) {
|
||||
minSrsStage = 0;
|
||||
break;
|
||||
}
|
||||
if (srsItem.srsStage < minSrsStage) {
|
||||
minSrsStage = srsItem.srsStage;
|
||||
}
|
||||
}
|
||||
|
||||
return Card(
|
||||
color: const Color(0xFF1E1E1E),
|
||||
@@ -297,12 +334,12 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
characters,
|
||||
item.characters,
|
||||
style: const TextStyle(fontSize: 32, color: Colors.white),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_buildSrsIndicator(avgSrsStage.round()),
|
||||
_buildSrsIndicator(minSrsStage),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -339,6 +376,32 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
}
|
||||
|
||||
void _showReadingsDialog(KanjiItem kanji) {
|
||||
final srsScores = <String, int>{
|
||||
'JP -> EN': 0,
|
||||
'EN -> JP': 0,
|
||||
'Reading (onyomi)': 0,
|
||||
'Reading (kunyomi)': 0,
|
||||
};
|
||||
|
||||
for (final entry in kanji.srsItems.entries) {
|
||||
final srsItem = entry.value;
|
||||
switch (srsItem.quizMode) {
|
||||
case QuizMode.kanjiToEnglish:
|
||||
srsScores['JP -> EN'] = srsItem.srsStage;
|
||||
break;
|
||||
case QuizMode.englishToKanji:
|
||||
srsScores['EN -> JP'] = srsItem.srsStage;
|
||||
break;
|
||||
case QuizMode.reading:
|
||||
if (srsItem.readingType == 'onyomi') {
|
||||
srsScores['Reading (onyomi)'] = srsItem.srsStage;
|
||||
} else if (srsItem.readingType == 'kunyomi') {
|
||||
srsScores['Reading (kunyomi)'] = srsItem.srsStage;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
@@ -348,37 +411,51 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
'Details for ${kanji.characters}',
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Level: ${kanji.level}',
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (kanji.meanings.isNotEmpty)
|
||||
content: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Meanings: ${kanji.meanings.join(', ')}',
|
||||
'Level: ${kanji.level}',
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (kanji.onyomi.isNotEmpty)
|
||||
Text(
|
||||
'On\'yomi: ${kanji.onyomi.join(', ')}',
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
if (kanji.kunyomi.isNotEmpty)
|
||||
Text(
|
||||
'Kun\'yomi: ${kanji.kunyomi.join(', ')}',
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
if (kanji.onyomi.isEmpty && kanji.kunyomi.isEmpty)
|
||||
const SizedBox(height: 16),
|
||||
if (kanji.meanings.isNotEmpty)
|
||||
Text(
|
||||
'Meanings: ${kanji.meanings.join(', ')}',
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (kanji.onyomi.isNotEmpty)
|
||||
Text(
|
||||
'On\'yomi: ${kanji.onyomi.join(', ')}',
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
if (kanji.kunyomi.isNotEmpty)
|
||||
Text(
|
||||
'Kun\'yomi: ${kanji.kunyomi.join(', ')}',
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
if (kanji.onyomi.isEmpty && kanji.kunyomi.isEmpty)
|
||||
const Text(
|
||||
'No readings available.',
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const Divider(color: Colors.grey),
|
||||
const SizedBox(height: 16),
|
||||
const Text(
|
||||
'No readings available.',
|
||||
style: const TextStyle(color: Colors.white),
|
||||
'SRS Scores:',
|
||||
style: TextStyle(
|
||||
color: Colors.white, fontWeight: FontWeight.bold),
|
||||
),
|
||||
],
|
||||
...srsScores.entries.map((entry) => Text(
|
||||
' ${entry.key}: ${entry.value}',
|
||||
style: const TextStyle(color: Colors.white),
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
@@ -395,9 +472,11 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
Future<void> _loadDecks() async {
|
||||
setState(() => _loading = true);
|
||||
try {
|
||||
final repo = Provider.of<DeckRepository>(context, listen: false);
|
||||
await repo.loadApiKey();
|
||||
final apiKey = repo.apiKey;
|
||||
final kanjiRepo = Provider.of<DeckRepository>(context, listen: false);
|
||||
final vocabRepo =
|
||||
Provider.of<VocabDeckRepository>(context, listen: false);
|
||||
await kanjiRepo.loadApiKey();
|
||||
final apiKey = kanjiRepo.apiKey;
|
||||
|
||||
if (apiKey == null || apiKey.isEmpty) {
|
||||
setState(() {
|
||||
@@ -407,16 +486,16 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
return;
|
||||
}
|
||||
|
||||
var kanji = await repo.loadKanji();
|
||||
var kanji = await kanjiRepo.loadKanji();
|
||||
if (kanji.isEmpty || kanji.every((k) => k.level == 0)) {
|
||||
setState(() => _status = 'Fetching kanji from WaniKani...');
|
||||
kanji = await repo.fetchAndCacheFromWk(apiKey);
|
||||
kanji = await kanjiRepo.fetchAndCacheFromWk(apiKey);
|
||||
}
|
||||
|
||||
var vocab = await repo.loadVocabulary();
|
||||
var vocab = await vocabRepo.loadVocabulary();
|
||||
if (vocab.isEmpty || vocab.every((v) => v.level == 0)) {
|
||||
setState(() => _status = 'Fetching vocabulary from WaniKani...');
|
||||
vocab = await repo.fetchAndCacheVocabularyFromWk(apiKey);
|
||||
vocab = await vocabRepo.fetchAndCacheVocabularyFromWk(apiKey);
|
||||
}
|
||||
|
||||
_kanjiDeck = kanji;
|
||||
@@ -458,7 +537,8 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: _isSelectionMode ? _buildSelectionAppBar() : _buildDefaultAppBar(),
|
||||
appBar:
|
||||
_isSelectionMode ? _buildSelectionAppBar() : _buildDefaultAppBar(),
|
||||
backgroundColor: const Color(0xFF121212),
|
||||
body: Column(
|
||||
children: [
|
||||
@@ -478,7 +558,8 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
_vocabByLevel,
|
||||
_vocabSortedLevels,
|
||||
_vocabPageController,
|
||||
(items) => _buildListView(items.cast<VocabularyItem>())),
|
||||
(items) =>
|
||||
_buildListView(items.cast<VocabularyItem>())),
|
||||
),
|
||||
_buildCustomSrsTab(),
|
||||
],
|
||||
@@ -487,10 +568,23 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
if (!_isSelectionMode)
|
||||
SafeArea(
|
||||
top: false,
|
||||
child: _tabController.index < 2 ? _buildLevelSelector() : const SizedBox.shrink(),
|
||||
child: _tabController.index < 2
|
||||
? _buildLevelSelector()
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
],
|
||||
),
|
||||
floatingActionButton: _tabController.index == 2
|
||||
? FloatingActionButton(
|
||||
onPressed: () async {
|
||||
await Navigator.of(context).push(
|
||||
MaterialPageRoute(builder: (_) => AddCardScreen()),
|
||||
);
|
||||
_loadCustomDeck();
|
||||
},
|
||||
child: const Icon(Icons.add),
|
||||
)
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -560,7 +654,8 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Delete Selected'),
|
||||
content: Text('Are you sure you want to delete ${_selectedItems.length} cards?'),
|
||||
content:
|
||||
Text('Are you sure you want to delete ${_selectedItems.length} cards?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
@@ -568,6 +663,7 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
final navigator = Navigator.of(context);
|
||||
for (final item in _selectedItems) {
|
||||
await _customDeckRepository.deleteCard(item);
|
||||
}
|
||||
@@ -575,8 +671,9 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
_isSelectionMode = false;
|
||||
_selectedItems.clear();
|
||||
});
|
||||
_loadCustomDeck();
|
||||
Navigator.of(context).pop();
|
||||
await _loadCustomDeck();
|
||||
if (!mounted) return;
|
||||
navigator.pop();
|
||||
},
|
||||
child: const Text('Delete'),
|
||||
),
|
||||
@@ -591,7 +688,8 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
}
|
||||
final bool targetState = _selectedItems.any((item) => !item.useInterval);
|
||||
|
||||
final selectedCharacters = _selectedItems.map((item) => item.characters).toSet();
|
||||
final selectedCharacters =
|
||||
_selectedItems.map((item) => item.characters).toSet();
|
||||
|
||||
final List<CustomKanjiItem> updatedItems = [];
|
||||
for (final item in _selectedItems) {
|
||||
@@ -600,8 +698,7 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
meaning: item.meaning,
|
||||
kanji: item.kanji,
|
||||
useInterval: targetState,
|
||||
srsLevel: item.srsLevel,
|
||||
nextReview: item.nextReview,
|
||||
srsData: item.srsData,
|
||||
);
|
||||
updatedItems.add(updatedItem);
|
||||
}
|
||||
@@ -653,14 +750,16 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (_) => CustomCardDetailsScreen(
|
||||
item: item,
|
||||
repository: _customDeckRepository,
|
||||
),
|
||||
),
|
||||
).then((_) => _loadCustomDeck());
|
||||
Navigator.of(context)
|
||||
.push(
|
||||
MaterialPageRoute(
|
||||
builder: (_) => CustomCardDetailsScreen(
|
||||
item: item,
|
||||
repository: _customDeckRepository,
|
||||
),
|
||||
),
|
||||
)
|
||||
.then((_) => _loadCustomDeck());
|
||||
}
|
||||
},
|
||||
child: Card(
|
||||
@@ -671,7 +770,7 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
),
|
||||
color: isSelected
|
||||
? Colors.blue.withOpacity(0.5)
|
||||
? Colors.blue.withAlpha((255 * 0.5).round())
|
||||
: const Color(0xFF1E1E1E),
|
||||
child: Stack(
|
||||
children: [
|
||||
@@ -683,20 +782,30 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
child: Text(
|
||||
item.kanji ?? item.characters,
|
||||
style: const TextStyle(fontSize: 32, color: Colors.white),
|
||||
item.kanji?.isNotEmpty == true
|
||||
? item.kanji!
|
||||
: item.characters,
|
||||
style:
|
||||
const TextStyle(fontSize: 32, color: Colors.white),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
item.meaning,
|
||||
style: const TextStyle(color: Colors.grey, fontSize: 16),
|
||||
style:
|
||||
const TextStyle(color: Colors.grey, fontSize: 16),
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_buildSrsIndicator(item.srsLevel),
|
||||
Builder(builder: (context) {
|
||||
final avgSrs = (item.srsData.japaneseToEnglish +
|
||||
item.srsData.englishToJapanese +
|
||||
item.srsData.listeningComprehension) /
|
||||
3;
|
||||
return _buildSrsIndicator(avgSrs.round());
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -719,3 +828,171 @@ class _BrowseScreenState extends State<BrowseScreen> with SingleTickerProviderSt
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _VocabDetailsDialog extends StatefulWidget {
|
||||
final VocabularyItem vocab;
|
||||
|
||||
const _VocabDetailsDialog({required this.vocab});
|
||||
|
||||
@override
|
||||
State<_VocabDetailsDialog> createState() => _VocabDetailsDialogState();
|
||||
}
|
||||
|
||||
class _VocabDetailsDialogState extends State<_VocabDetailsDialog> {
|
||||
List<Widget> _exampleSentences = [const CircularProgressIndicator()];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_fetchExampleSentences();
|
||||
}
|
||||
|
||||
Future<void> _fetchExampleSentences() async {
|
||||
try {
|
||||
final uri = Uri.parse(
|
||||
'https://jisho.org/api/v1/search/words?keyword=${Uri.encodeComponent(widget.vocab.characters)}');
|
||||
final response = await http.get(uri);
|
||||
if (response.statusCode == 200) {
|
||||
final data = jsonDecode(utf8.decode(response.bodyBytes));
|
||||
final sentences = <Widget>[];
|
||||
if (data['data'] != null && (data['data'] as List).isNotEmpty) {
|
||||
for (final result in data['data']) {
|
||||
if (result['japanese'] != null &&
|
||||
(result['japanese'] as List).isNotEmpty &&
|
||||
result['senses'] != null &&
|
||||
(result['senses'] as List).isNotEmpty) {
|
||||
final japaneseWord = result['japanese'][0]['word'] ?? result['japanese'][0]['reading'];
|
||||
final englishDefinition = result['senses'][0]['english_definitions'].join(', ');
|
||||
if (japaneseWord != null && englishDefinition != null) {
|
||||
sentences.add(
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(japaneseWord, style: const TextStyle(color: Colors.white)),
|
||||
Text(englishDefinition, style: const TextStyle(color: Colors.grey)),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sentences.isEmpty) {
|
||||
sentences.add(const Text('No example sentences found.', style: TextStyle(color: Colors.white)));
|
||||
}
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_exampleSentences = sentences;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_exampleSentences = [
|
||||
const Text('Failed to load example sentences.', style: TextStyle(color: Colors.red))
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_exampleSentences = [
|
||||
const Text('Error loading example sentences.', style: TextStyle(color: Colors.red))
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final srsScores = <String, int>{
|
||||
'JP -> EN': 0,
|
||||
'EN -> JP': 0,
|
||||
'Audio': 0,
|
||||
};
|
||||
|
||||
for (final entry in widget.vocab.srsItems.entries) {
|
||||
final srsItem = entry.value;
|
||||
switch (srsItem.quizMode) {
|
||||
case VocabQuizMode.vocabToEnglish:
|
||||
srsScores['JP -> EN'] = srsItem.srsStage;
|
||||
break;
|
||||
case VocabQuizMode.englishToVocab:
|
||||
srsScores['EN -> JP'] = srsItem.srsStage;
|
||||
break;
|
||||
case VocabQuizMode.audioToEnglish:
|
||||
srsScores['Audio'] = srsItem.srsStage;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Level: ${widget.vocab.level}',
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (widget.vocab.meanings.isNotEmpty)
|
||||
Text(
|
||||
'Meanings: ${widget.vocab.meanings.join(', ')}',
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (widget.vocab.readings.isNotEmpty)
|
||||
Text(
|
||||
'Readings: ${widget.vocab.readings.join(', ')}',
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const Divider(color: Colors.grey),
|
||||
const SizedBox(height: 16),
|
||||
const Text(
|
||||
'SRS Scores:',
|
||||
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
|
||||
),
|
||||
...srsScores.entries.map((entry) => Text(
|
||||
' ${entry.key}: ${entry.value}',
|
||||
style: const TextStyle(color: Colors.white),
|
||||
)),
|
||||
const SizedBox(height: 16),
|
||||
const Divider(color: Colors.grey),
|
||||
const SizedBox(height: 16),
|
||||
const Text(
|
||||
'Example Sentences:',
|
||||
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
|
||||
),
|
||||
..._exampleSentences,
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _showVocabDetailsDialog(BuildContext context, VocabularyItem vocab) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
backgroundColor: const Color(0xFF1E1E1E),
|
||||
title: Text(
|
||||
'Details for ${vocab.characters}',
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
content: _VocabDetailsDialog(vocab: vocab),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: const Text('Close', style: TextStyle(color: Colors.blueAccent)),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user