Add kanji page

This commit is contained in:
Oystein Kristoffer Tveit 2022-01-26 01:58:47 +01:00
parent a8b0f3b737
commit c1ccbc00b3
16 changed files with 292 additions and 97 deletions

View File

@ -43,8 +43,8 @@ android {
defaultConfig { defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.tangocard_reader" applicationId "xyz.h7x4.tangocard_reader"
minSdkVersion 16 minSdkVersion 26
targetSdkVersion 30 targetSdkVersion 30
versionCode flutterVersionCode.toInteger() versionCode flutterVersionCode.toInteger()
versionName flutterVersionName versionName flutterVersionName

View File

@ -1,7 +1,7 @@
import 'dart:math'; import 'dart:math';
import 'package:tangocard_reader/models/yokutango_entry.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:tangocard_reader/models/data_entry.dart';
class Flashcard extends StatelessWidget { class Flashcard extends StatelessWidget {
final YokutangoEntry? card; final YokutangoEntry? card;

View File

@ -0,0 +1,6 @@
part 'yokutango_entry.dart';
part 'kanji_entry.dart';
abstract class DataEntry {
const DataEntry();
}

View File

@ -0,0 +1,53 @@
part of 'data_entry.dart';
class KanjiEntry extends DataEntry {
final String kanji;
final List<WordConstruct> kana;
KanjiEntry.fromJson(Map<String, dynamic> json)
: kanji = json['kanji'] as String,
kana = [
for (final j in json['kana'] as List) WordConstruct.fromJson(j)
];
@override
String toString() {
return '$kanji - ${kana.join('')}';
}
}
class WordConstruct {
final List<WordPiece> pieces;
WordConstruct({required this.pieces});
WordConstruct.fromJson(dynamic json)
: pieces = (json is String)
? [WordPiece(word: json, isActive: true)]
: [for (final j in json as List) WordPiece.fromJson(j)];
@override
String toString() {
return pieces.map((p) => p.isActive ? p.word : '(${p.word})').join('');
}
}
class WordPiece {
final String? kana;
final String? romaji;
final String word;
final bool isActive;
const WordPiece({
required this.word,
this.kana,
this.romaji,
this.isActive = false,
});
WordPiece.fromJson(Map<String, dynamic> json)
: kana = json['kana'] as String?,
romaji = json['romaji'] as String?,
word = json['text'] as String,
isActive = json['active'] as bool? ?? false;
}

View File

@ -1,7 +1,7 @@
import 'package:tangocard_reader/models/yokutango_entry.dart'; import 'data_entry.dart';
class BenkyouArgs { class BenkyouArgs {
final List<YokutangoEntry> cards; final List<DataEntry> cards;
final int? index; final int? index;
const BenkyouArgs({required this.cards, this.index}); const BenkyouArgs({required this.cards, this.index});

View File

@ -1,4 +1,6 @@
class YokutangoEntry { part of 'data_entry.dart';
class YokutangoEntry extends DataEntry {
final List<JapaneseWord> japanese; final List<JapaneseWord> japanese;
final List<NorwegianWord> norwegian; final List<NorwegianWord> norwegian;

View File

@ -1,11 +1,14 @@
import 'dart:io'; import 'dart:io';
import 'package:tangocard_reader/models/data_entry.dart';
import 'package:tangocard_reader/models/router_args.dart'; import 'package:tangocard_reader/models/router_args.dart';
import 'package:tangocard_reader/screens/flashcard.dart'; import 'package:tangocard_reader/screens/practise/flashcard.dart';
import 'package:tangocard_reader/screens/home.dart'; import 'package:tangocard_reader/screens/home.dart';
import 'package:tangocard_reader/screens/tango_list.dart'; import 'package:tangocard_reader/screens/pages/tango_list.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'screens/pages/kanji_list.dart';
class PageRouter { class PageRouter {
static Route<dynamic> generateRoute(RouteSettings settings) { static Route<dynamic> generateRoute(RouteSettings settings) {
final args = settings.arguments; final args = settings.arguments;
@ -14,22 +17,41 @@ class PageRouter {
case '/': case '/':
return MaterialPageRoute(builder: (_) => const Home()); return MaterialPageRoute(builder: (_) => const Home());
case '/tangolist': case '/list/tango':
final file = args as File; final file = args as File;
return MaterialPageRoute(builder: (_) => TangoList(file: file)); return MaterialPageRoute(builder: (_) => TangoList(file: file));
case '/benkyou': case '/list/kanji':
final file = args as File;
return MaterialPageRoute(builder: (_) => KanjiList(file: file));
case '/benkyou/tango':
final benkyouArgs = args as BenkyouArgs; final benkyouArgs = args as BenkyouArgs;
return MaterialPageRoute( return MaterialPageRoute(
builder: (_) => FlashcardView( builder: (_) => FlashcardView(
cards: benkyouArgs.cards, cards: benkyouArgs.cards as List<YokutangoEntry>,
index: benkyouArgs.index, index: benkyouArgs.index,
), ),
); );
case '/benkyou/kanji':
// final benkyouArgs = args as BenkyouArgs;
// return MaterialPageRoute(
// builder: (_) => FlashcardView(
// cards: benkyouArgs.cards as List<YokutangoEntry>,
// index: benkyouArgs.index,
// ),
// );
default: default:
return MaterialPageRoute( return MaterialPageRoute(
builder: (_) => const Text("ERROR: this route does not exist")); builder: (_) => Scaffold(
appBar: AppBar(title: const Text('Error')),
body: Center(
child: ErrorWidget('No such route...')
),
),
);
} }
} }
} }

View File

@ -1,81 +1,37 @@
import 'dart:convert';
import 'dart:io';
import 'package:tangocard_reader/models/yokutango_entry.dart';
import 'package:tangocard_reader/screens/error.dart';
import 'package:tangocard_reader/screens/loading.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:tangocard_reader/service/tangocard_files.dart';
class Home extends StatelessWidget { import 'pages/tango_set_list.dart';
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key); const Home({Key? key}) : super(key: key);
@override @override
Widget build(BuildContext context) { State<Home> createState() => _HomeState();
return Scaffold( }
class _HomeState extends State<Home> {
int page = 0;
final _pages = [
TangoSetList(files: tangocardFilePaths, route: '/list/tango',),
TangoSetList(files: kanjicardFilePaths, route: '/list/kanji',),
];
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text("よく単語"), title: const Text("よく単語"),
centerTitle: true, centerTitle: true,
), ),
body: const TangocardList()); body: _pages[page],
} bottomNavigationBar: BottomNavigationBar(
} onTap: (int index) => setState(() => page = index),
currentIndex: page,
class TangocardList extends StatelessWidget { items: const [
const TangocardList({Key? key}) : super(key: key); BottomNavigationBarItem(label: 'Tango', icon: Icon(Icons.style)),
BottomNavigationBarItem(label: 'Kanji', icon: Text('漢字')),
Future<Map<File, List<YokutangoEntry>>> get tangocardFilePaths => rootBundle ],
.loadString('AssetManifest.json') ),
.then( );
(json) => jsonDecode(json)
.keys
.where((String key) =>
key.contains('yokutango/json/') && key.contains('.json'))
.toList(),
)
.then(
(l) async => Map.fromIterables(
l.map<File>((f) => File(f)),
await Future.wait<List<YokutangoEntry>>(
l
.map<Future<List<YokutangoEntry>>>(
(String t) => rootBundle
.loadString(t)
.then<List<YokutangoEntry>>((s) => jsonDecode(s)
.map<YokutangoEntry>(
(e) => YokutangoEntry.fromJson(e))
.toList()),
)
),
),
);
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: tangocardFilePaths,
builder: (context, snapshot) {
if (snapshot.hasError) {
debugPrint(snapshot.error.toString());
return const ErrorScreen();
} else if (!snapshot.hasData) {
return const LoadingScreen();
}
return ListView(
children: (snapshot.data as Map<File, List<YokutangoEntry>>)
.entries
.map(
(e) => ListTile(
title: Text(
"${e.key.uri.pathSegments.last} - ${e.value.length} cards"),
onTap: () => Navigator.pushNamed(context, '/tangolist',
arguments: e.key),
),
)
.toList(),
);
},
);
}
} }

View File

@ -1,24 +1,23 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:tangocard_reader/models/router_args.dart';
import 'package:tangocard_reader/models/yokutango_entry.dart';
import 'package:tangocard_reader/screens/error.dart';
import 'package:tangocard_reader/screens/loading.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:tangocard_reader/models/data_entry.dart';
import 'package:tangocard_reader/models/router_args.dart';
import 'package:tangocard_reader/screens/misc/error.dart';
import 'package:tangocard_reader/screens/misc/loading.dart';
class TangoList extends StatelessWidget { class KanjiList extends StatelessWidget {
final File file; final File file;
const TangoList({ const KanjiList({
required this.file,
Key? key, Key? key,
required this.file,
}) : super(key: key); }) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) => Scaffold(
return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(file.uri.pathSegments.last), title: Text(file.uri.pathSegments.last),
), ),
@ -33,7 +32,7 @@ class TangoList extends StatelessWidget {
} }
final entries = (jsonSnapshot.data as List) final entries = (jsonSnapshot.data as List)
.map((e) => YokutangoEntry.fromJson(e)) .map((e) => KanjiEntry.fromJson(e))
.toList(); .toList();
return ListView( return ListView(
@ -44,7 +43,7 @@ class TangoList extends StatelessWidget {
i, i,
ListTile( ListTile(
title: Text(e.toString()), title: Text(e.toString()),
onTap: () => Navigator.pushNamed(context, '/benkyou', onTap: () => Navigator.pushNamed(context, '/benkyou/kanji',
arguments: BenkyouArgs(cards: entries, index: i)), arguments: BenkyouArgs(cards: entries, index: i)),
), ),
), ),
@ -53,7 +52,7 @@ class TangoList extends StatelessWidget {
.toList(), .toList(),
); );
}, },
), )
); );
}
} }

View File

@ -0,0 +1,61 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:tangocard_reader/models/data_entry.dart';
import 'package:tangocard_reader/models/router_args.dart';
import 'package:tangocard_reader/screens/misc/error.dart';
import 'package:tangocard_reader/screens/misc/loading.dart';
class TangoList extends StatelessWidget {
final File file;
const TangoList({
required this.file,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(file.uri.pathSegments.last),
),
body: FutureBuilder(
future:
rootBundle.loadString(file.path).then((data) => jsonDecode(data)),
builder: (context, jsonSnapshot) {
if (jsonSnapshot.hasError) {
return const ErrorScreen();
} else if (!jsonSnapshot.hasData) {
return const LoadingScreen();
}
final entries = (jsonSnapshot.data as List)
.map((e) => YokutangoEntry.fromJson(e))
.toList();
return ListView(
children: entries
.asMap()
.map(
(i, e) => MapEntry(
i,
ListTile(
title: Text(e.toString()),
onTap: () => Navigator.pushNamed(
context,
'/benkyou/tango',
arguments: BenkyouArgs(cards: entries, index: i),
),
),
),
)
.values
.toList(),
);
},
));
}
}

View File

@ -0,0 +1,46 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:tangocard_reader/screens/misc/error.dart';
import 'package:tangocard_reader/screens/misc/loading.dart';
class TangoSetList extends StatelessWidget {
final String route;
final Future<Map<File, List>> files;
const TangoSetList({
Key? key,
required this.route,
required this.files,
}) : super(key: key);
@override
Widget build(BuildContext context) => FutureBuilder(
future: files,
builder: (context, snapshot) {
if (snapshot.hasError) {
debugPrint(snapshot.error.toString());
return const ErrorScreen();
} else if (!snapshot.hasData) {
return const LoadingScreen();
}
return ListView(
children: (snapshot.data as Map<File, List>)
.entries
.map(
(e) => ListTile(
title: Text(
"${e.key.uri.pathSegments.last} - ${e.value.length} cards"),
onTap: () => Navigator.pushNamed(
context,
route,
arguments: e.key,
),
),
)
.toList(),
);
},
);
}

View File

@ -1,9 +1,9 @@
import 'dart:math'; import 'dart:math';
import 'package:tangocard_reader/components/flashcard.dart'; import 'package:tangocard_reader/components/flashcard.dart';
import 'package:tangocard_reader/models/yokutango_entry.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:tangocard_reader/models/data_entry.dart';
class FlashcardView extends StatefulWidget { class FlashcardView extends StatefulWidget {
final List<YokutangoEntry> cards; final List<YokutangoEntry> cards;

View File

@ -0,0 +1,50 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:tangocard_reader/models/data_entry.dart';
// TODO: merge
Future<Map<File, List<YokutangoEntry>>> get tangocardFilePaths => rootBundle
.loadString('AssetManifest.json')
.then(
(json) => jsonDecode(json)
.keys
.where((String key) =>
key.contains('yokutango/json/') && key.contains('.json'))
.toList(),
)
.then(
(l) async => Map.fromIterables(
l.map<File>((f) => File(f)),
await Future.wait<List<YokutangoEntry>>(
l.map<Future<List<YokutangoEntry>>>(
(String t) => rootBundle.loadString(t).then<List<YokutangoEntry>>(
(s) => jsonDecode(s)
.map<YokutangoEntry>((e) => YokutangoEntry.fromJson(e))
.toList()),
)),
),
);
Future<Map<File, List<KanjiEntry>>> get kanjicardFilePaths => rootBundle
.loadString('AssetManifest.json')
.then(
(json) => jsonDecode(json)
.keys
.where((String key) =>
key.contains(RegExp(r'yokutango/kanji/kanji_\d+.json')))
.toList(),
)
.then(
(l) async => Map.fromIterables(
l.map<File>((f) => File(f)),
await Future.wait<List<KanjiEntry>>(l.map<Future<List<KanjiEntry>>>(
(String t) => rootBundle.loadString(t).then<List<KanjiEntry>>((s) =>
jsonDecode(s)
.map<KanjiEntry>((e) => KanjiEntry.fromJson(e))
.toList()),
)),
),
);

View File

@ -176,7 +176,7 @@ packages:
name: vector_math name: vector_math
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.0" version: "2.1.1"
sdks: sdks:
dart: ">=2.12.0 <3.0.0" dart: ">=2.14.0 <3.0.0"
flutter: ">=1.16.0" flutter: ">=1.16.0"