mirror of
https://github.com/h7x4/Jisho-Study-Tool.git
synced 2024-12-21 21:47:29 +01:00
Add lots of history functionality
This commit is contained in:
parent
d82fcbe427
commit
e8f42860af
@ -1,7 +1,8 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:jisho_study_tool/bloc/database/database_bloc.dart';
|
import 'package:jisho_study_tool/bloc/database/database_bloc.dart';
|
||||||
import 'package:jisho_study_tool/models/history/kanji_result.dart';
|
import 'package:jisho_study_tool/models/history/kanji_query.dart';
|
||||||
|
import 'package:jisho_study_tool/models/history/search.dart';
|
||||||
|
|
||||||
import './kanji_event.dart';
|
import './kanji_event.dart';
|
||||||
import './kanji_state.dart';
|
import './kanji_state.dart';
|
||||||
@ -14,7 +15,6 @@ export './kanji_event.dart';
|
|||||||
export './kanji_state.dart';
|
export './kanji_state.dart';
|
||||||
|
|
||||||
class KanjiBloc extends Bloc<KanjiEvent, KanjiState> {
|
class KanjiBloc extends Bloc<KanjiEvent, KanjiState> {
|
||||||
|
|
||||||
DatabaseBloc _databaseBloc;
|
DatabaseBloc _databaseBloc;
|
||||||
|
|
||||||
KanjiBloc(this._databaseBloc) : super(KanjiSearch(KanjiSearchType.Initial));
|
KanjiBloc(this._databaseBloc) : super(KanjiSearch(KanjiSearchType.Initial));
|
||||||
@ -25,33 +25,31 @@ class KanjiBloc extends Bloc<KanjiEvent, KanjiState> {
|
|||||||
|
|
||||||
(_databaseBloc.state as DatabaseConnected)
|
(_databaseBloc.state as DatabaseConnected)
|
||||||
.database
|
.database
|
||||||
.box<KanjiResult>()
|
.box<Search>()
|
||||||
.put(KanjiResult(
|
.put(Search(timestamp: DateTime.now())
|
||||||
|
..kanjiQuery.target = KanjiQuery(
|
||||||
kanji: kanji,
|
kanji: kanji,
|
||||||
timestamp: DateTime.now(),
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Stream<KanjiState> mapEventToState(KanjiEvent event)
|
Stream<KanjiState> mapEventToState(KanjiEvent event) async* {
|
||||||
async* {
|
|
||||||
if (event is GetKanji) {
|
if (event is GetKanji) {
|
||||||
|
|
||||||
yield KanjiSearchLoading();
|
yield KanjiSearchLoading();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
addSearchToDB(event.kanjiSearchString);
|
addSearchToDB(event.kanjiSearchString);
|
||||||
final kanji = await fetchKanji(event.kanjiSearchString);
|
final kanji = await fetchKanji(event.kanjiSearchString);
|
||||||
if (kanji.found) yield KanjiSearchFinished(kanji: kanji);
|
if (kanji.found)
|
||||||
else yield KanjiSearchError('Something went wrong');
|
yield KanjiSearchFinished(kanji: kanji);
|
||||||
|
else
|
||||||
|
yield KanjiSearchError('Something went wrong');
|
||||||
} on Exception {
|
} on Exception {
|
||||||
yield KanjiSearchError('Something went wrong');
|
yield KanjiSearchError('Something went wrong');
|
||||||
}
|
}
|
||||||
} else if (event is GetKanjiSuggestions) {
|
} else if (event is GetKanjiSuggestions) {
|
||||||
|
|
||||||
final suggestions = kanjiSuggestions(event.searchString);
|
final suggestions = kanjiSuggestions(event.searchString);
|
||||||
yield KanjiSearchKeyboard(KanjiSearchType.Keyboard, suggestions);
|
yield KanjiSearchKeyboard(KanjiSearchType.Keyboard, suggestions);
|
||||||
|
|
||||||
} else if (event is ReturnToInitialState) {
|
} else if (event is ReturnToInitialState) {
|
||||||
yield KanjiSearch(KanjiSearchType.Initial);
|
yield KanjiSearch(KanjiSearchType.Initial);
|
||||||
}
|
}
|
||||||
|
18
lib/bloc/navigation/navigation_bloc.dart
Normal file
18
lib/bloc/navigation/navigation_bloc.dart
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
|
import './navigation_event.dart';
|
||||||
|
import './navigation_state.dart';
|
||||||
|
|
||||||
|
export './navigation_event.dart';
|
||||||
|
export './navigation_state.dart';
|
||||||
|
|
||||||
|
class NavigationBloc extends Bloc<NavigationEvent, NavigationState> {
|
||||||
|
|
||||||
|
NavigationBloc() : super(NavigationPage(0));
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<NavigationState> mapEventToState(NavigationEvent event) async* {
|
||||||
|
if (event is ChangePage)
|
||||||
|
yield NavigationPage(event.pageNum);
|
||||||
|
}
|
||||||
|
}
|
8
lib/bloc/navigation/navigation_event.dart
Normal file
8
lib/bloc/navigation/navigation_event.dart
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
abstract class NavigationEvent {
|
||||||
|
const NavigationEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
class ChangePage extends NavigationEvent {
|
||||||
|
final int pageNum;
|
||||||
|
const ChangePage(this.pageNum);
|
||||||
|
}
|
9
lib/bloc/navigation/navigation_state.dart
Normal file
9
lib/bloc/navigation/navigation_state.dart
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
abstract class NavigationState {
|
||||||
|
const NavigationState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class NavigationPage extends NavigationState {
|
||||||
|
final int pageNum;
|
||||||
|
const NavigationPage(this.pageNum);
|
||||||
|
|
||||||
|
}
|
@ -3,7 +3,8 @@ import 'dart:async';
|
|||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:jisho_study_tool/models/history/search_string.dart';
|
import 'package:jisho_study_tool/models/history/search.dart';
|
||||||
|
import 'package:jisho_study_tool/models/history/word_query.dart';
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
|
|
||||||
import 'package:jisho_study_tool/bloc/database/database_bloc.dart';
|
import 'package:jisho_study_tool/bloc/database/database_bloc.dart';
|
||||||
@ -14,7 +15,6 @@ part 'search_event.dart';
|
|||||||
part 'search_state.dart';
|
part 'search_state.dart';
|
||||||
|
|
||||||
class SearchBloc extends Bloc<SearchEvent, SearchState> {
|
class SearchBloc extends Bloc<SearchEvent, SearchState> {
|
||||||
|
|
||||||
DatabaseBloc _databaseBloc;
|
DatabaseBloc _databaseBloc;
|
||||||
|
|
||||||
SearchBloc(this._databaseBloc) : super(SearchInitial());
|
SearchBloc(this._databaseBloc) : super(SearchInitial());
|
||||||
@ -25,10 +25,10 @@ class SearchBloc extends Bloc<SearchEvent, SearchState> {
|
|||||||
|
|
||||||
(_databaseBloc.state as DatabaseConnected)
|
(_databaseBloc.state as DatabaseConnected)
|
||||||
.database
|
.database
|
||||||
.box<SearchString>()
|
.box<Search>()
|
||||||
.put(SearchString(
|
.put(Search(timestamp: DateTime.now())
|
||||||
|
..wordQuery.target = WordQuery(
|
||||||
query: searchString,
|
query: searchString,
|
||||||
timestamp: DateTime.now(),
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,7 +42,8 @@ class SearchBloc extends Bloc<SearchEvent, SearchState> {
|
|||||||
try {
|
try {
|
||||||
addSearchToDB(event.searchString);
|
addSearchToDB(event.searchString);
|
||||||
final searchResults = await fetchJishoResults(event.searchString);
|
final searchResults = await fetchJishoResults(event.searchString);
|
||||||
if (searchResults.meta.status == 200) yield SearchFinished(searchResults.data!);
|
if (searchResults.meta.status == 200)
|
||||||
|
yield SearchFinished(searchResults.data!);
|
||||||
} on Exception {
|
} on Exception {
|
||||||
yield SearchError('Something went wrong');
|
yield SearchError('Something went wrong');
|
||||||
}
|
}
|
||||||
|
133
lib/main.dart
133
lib/main.dart
@ -1,13 +1,16 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:jisho_study_tool/view/screens/loading.dart';
|
||||||
import 'package:mdi/mdi.dart';
|
import 'package:mdi/mdi.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:path/path.dart';
|
import 'package:path/path.dart';
|
||||||
|
|
||||||
import 'package:jisho_study_tool/objectbox.g.dart';
|
import 'package:jisho_study_tool/objectbox.g.dart';
|
||||||
|
|
||||||
import 'package:jisho_study_tool/bloc/database/database_bloc.dart';
|
import 'package:jisho_study_tool/bloc/database/database_bloc.dart';
|
||||||
import 'package:jisho_study_tool/bloc/kanji/kanji_bloc.dart';
|
import 'package:jisho_study_tool/bloc/kanji/kanji_bloc.dart';
|
||||||
import 'package:jisho_study_tool/bloc/search/search_bloc.dart';
|
import 'package:jisho_study_tool/bloc/search/search_bloc.dart';
|
||||||
|
import 'package:jisho_study_tool/bloc/navigation/navigation_bloc.dart';
|
||||||
|
|
||||||
import 'package:jisho_study_tool/view/screens/kanji/view.dart';
|
import 'package:jisho_study_tool/view/screens/kanji/view.dart';
|
||||||
import 'package:jisho_study_tool/view/screens/history.dart';
|
import 'package:jisho_study_tool/view/screens/history.dart';
|
||||||
@ -18,42 +21,19 @@ void main() => runApp(MyApp());
|
|||||||
|
|
||||||
DatabaseBloc _databaseBloc = DatabaseBloc();
|
DatabaseBloc _databaseBloc = DatabaseBloc();
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
_MyAppState createState() => _MyAppState();
|
||||||
return MaterialApp(
|
|
||||||
title: 'Jisho Study Tool',
|
|
||||||
theme: ThemeData(
|
|
||||||
primarySwatch: Colors.blue,
|
|
||||||
),
|
|
||||||
home: MultiBlocProvider(
|
|
||||||
providers: [
|
|
||||||
BlocProvider(create: (context) => SearchBloc(_databaseBloc)),
|
|
||||||
BlocProvider(create: (context) => KanjiBloc(_databaseBloc)),
|
|
||||||
BlocProvider(create: (context) => _databaseBloc),
|
|
||||||
],
|
|
||||||
child: Home(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class Home extends StatefulWidget {
|
class _MyAppState extends State<MyApp> {
|
||||||
@override
|
|
||||||
_HomeState createState() => _HomeState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _HomeState extends State<Home> {
|
|
||||||
int selectedPage = 0;
|
|
||||||
|
|
||||||
late final Store _store;
|
late final Store _store;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
getApplicationDocumentsDirectory()
|
getApplicationDocumentsDirectory().then((dir) {
|
||||||
.then((dir) {
|
|
||||||
_store = Store(
|
_store = Store(
|
||||||
getObjectBoxModel(),
|
getObjectBoxModel(),
|
||||||
directory: join(dir.path, 'objectbox'),
|
directory: join(dir.path, 'objectbox'),
|
||||||
@ -72,14 +52,41 @@ class _HomeState extends State<Home> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocBuilder<DatabaseBloc, DatabaseState>(
|
return MaterialApp(
|
||||||
builder: (context, state) {
|
title: 'Jisho Study Tool',
|
||||||
|
|
||||||
if (state is DatabaseDisconnected) {
|
// TODO: Add color theme
|
||||||
return Center(
|
theme: ThemeData(
|
||||||
child: CircularProgressIndicator(),
|
primarySwatch: Colors.blue,
|
||||||
|
),
|
||||||
|
home: MultiBlocProvider(
|
||||||
|
providers: [
|
||||||
|
BlocProvider(create: (context) => SearchBloc(_databaseBloc)),
|
||||||
|
BlocProvider(create: (context) => KanjiBloc(_databaseBloc)),
|
||||||
|
BlocProvider(create: (context) => _databaseBloc),
|
||||||
|
BlocProvider(create: (context) => NavigationBloc()),
|
||||||
|
],
|
||||||
|
child:
|
||||||
|
BlocBuilder<DatabaseBloc, DatabaseState>(builder: (context, state) {
|
||||||
|
if (state is DatabaseDisconnected)
|
||||||
|
return Container(
|
||||||
|
child: LoadingScreen(),
|
||||||
|
decoration: BoxDecoration(color: Colors.white),
|
||||||
|
);
|
||||||
|
|
||||||
|
return Home();
|
||||||
|
}),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Home extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocBuilder<NavigationBloc, NavigationState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
int selectedPage = (state as NavigationPage).pageNum;
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
@ -89,12 +96,9 @@ class _HomeState extends State<Home> {
|
|||||||
body: pages[selectedPage].content,
|
body: pages[selectedPage].content,
|
||||||
bottomNavigationBar: BottomNavigationBar(
|
bottomNavigationBar: BottomNavigationBar(
|
||||||
currentIndex: selectedPage,
|
currentIndex: selectedPage,
|
||||||
onTap: (int index) {
|
onTap: (int index) =>
|
||||||
setState(() {
|
BlocProvider.of<NavigationBloc>(context).add(ChangePage(index)),
|
||||||
selectedPage = index;
|
items: pages.map((p) => p.item).toList(),
|
||||||
});
|
|
||||||
},
|
|
||||||
items: navBar,
|
|
||||||
showSelectedLabels: false,
|
showSelectedLabels: false,
|
||||||
showUnselectedLabels: false,
|
showUnselectedLabels: false,
|
||||||
unselectedItemColor: Colors.blue,
|
unselectedItemColor: Colors.blue,
|
||||||
@ -106,38 +110,15 @@ class _HomeState extends State<Home> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<BottomNavigationBarItem> navBar = [
|
|
||||||
BottomNavigationBarItem(
|
|
||||||
label: 'Search',
|
|
||||||
icon: Icon(Icons.search),
|
|
||||||
),
|
|
||||||
BottomNavigationBarItem(
|
|
||||||
label: 'Kanji',
|
|
||||||
icon: Icon(
|
|
||||||
Mdi.ideogramCjk,
|
|
||||||
size: 30,
|
|
||||||
)),
|
|
||||||
BottomNavigationBarItem(
|
|
||||||
label: 'History',
|
|
||||||
icon: Icon(Icons.history),
|
|
||||||
),
|
|
||||||
BottomNavigationBarItem(
|
|
||||||
label: 'Memorize',
|
|
||||||
icon: Icon(Icons.bookmark),
|
|
||||||
),
|
|
||||||
BottomNavigationBarItem(
|
|
||||||
label: 'Settings',
|
|
||||||
icon: Icon(Icons.settings),
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
class _Page {
|
class _Page {
|
||||||
Widget content;
|
final Widget content;
|
||||||
Widget titleBar;
|
final Widget titleBar;
|
||||||
|
final BottomNavigationBarItem item;
|
||||||
|
|
||||||
_Page({
|
const _Page({
|
||||||
required this.content,
|
required this.content,
|
||||||
required this.titleBar,
|
required this.titleBar,
|
||||||
|
required this.item,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,21 +126,39 @@ final List<_Page> pages = [
|
|||||||
_Page(
|
_Page(
|
||||||
content: SearchView(),
|
content: SearchView(),
|
||||||
titleBar: Text('Search'),
|
titleBar: Text('Search'),
|
||||||
|
item: BottomNavigationBarItem(
|
||||||
|
label: 'Search',
|
||||||
|
icon: Icon(Icons.search),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
_Page(
|
_Page(
|
||||||
content: KanjiView(),
|
content: KanjiView(),
|
||||||
titleBar: KanjiViewBar(),
|
titleBar: KanjiViewBar(),
|
||||||
|
item: BottomNavigationBarItem(
|
||||||
|
label: 'Kanji', icon: Icon(Mdi.ideogramCjk, size: 30)),
|
||||||
),
|
),
|
||||||
_Page(
|
_Page(
|
||||||
content: HistoryView(),
|
content: HistoryView(),
|
||||||
titleBar: Text("History"),
|
titleBar: Text("History"),
|
||||||
|
item: BottomNavigationBarItem(
|
||||||
|
label: 'History',
|
||||||
|
icon: Icon(Icons.history),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
_Page(
|
_Page(
|
||||||
content: Container(),
|
content: Container(),
|
||||||
titleBar: Text("Memorization"),
|
titleBar: Text("Saved"),
|
||||||
|
item: BottomNavigationBarItem(
|
||||||
|
label: 'Saved',
|
||||||
|
icon: Icon(Icons.bookmark),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
_Page(
|
_Page(
|
||||||
content: Container(),
|
content: Container(),
|
||||||
titleBar: Text("Settings"),
|
titleBar: Text("Settings"),
|
||||||
|
item: BottomNavigationBarItem(
|
||||||
|
label: 'Settings',
|
||||||
|
icon: Icon(Icons.settings),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
13
lib/models/history/kanji_query.dart
Normal file
13
lib/models/history/kanji_query.dart
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import 'package:objectbox/objectbox.dart';
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
class KanjiQuery {
|
||||||
|
int id;
|
||||||
|
|
||||||
|
String kanji;
|
||||||
|
|
||||||
|
KanjiQuery({
|
||||||
|
this.id = 0,
|
||||||
|
required this.kanji,
|
||||||
|
});
|
||||||
|
}
|
@ -1,22 +0,0 @@
|
|||||||
|
|
||||||
import 'package:objectbox/objectbox.dart';
|
|
||||||
|
|
||||||
@Entity()
|
|
||||||
class KanjiResult {
|
|
||||||
int id = 0;
|
|
||||||
|
|
||||||
@Property(type: PropertyType.date)
|
|
||||||
DateTime timestamp;
|
|
||||||
|
|
||||||
String kanji;
|
|
||||||
|
|
||||||
KanjiResult({
|
|
||||||
required this.timestamp,
|
|
||||||
required this.kanji,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return "[${timestamp.toIso8601String()}] - $kanji";
|
|
||||||
}
|
|
||||||
}
|
|
30
lib/models/history/search.dart
Normal file
30
lib/models/history/search.dart
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import 'package:objectbox/objectbox.dart';
|
||||||
|
|
||||||
|
import './kanji_query.dart';
|
||||||
|
import './word_query.dart';
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
class Search {
|
||||||
|
int id;
|
||||||
|
|
||||||
|
@Property(type: PropertyType.date)
|
||||||
|
late final DateTime timestamp;
|
||||||
|
|
||||||
|
final wordQuery = ToOne<WordQuery>();
|
||||||
|
|
||||||
|
final kanjiQuery = ToOne<KanjiQuery>();
|
||||||
|
|
||||||
|
Search({
|
||||||
|
this.id = 0,
|
||||||
|
required this.timestamp
|
||||||
|
}); // {
|
||||||
|
|
||||||
|
bool isKanji() {
|
||||||
|
// // TODO: better error message
|
||||||
|
if (this.wordQuery.target == null && this.kanjiQuery.target == null)
|
||||||
|
throw Exception();
|
||||||
|
|
||||||
|
return this.wordQuery.target == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,26 +0,0 @@
|
|||||||
import 'package:objectbox/objectbox.dart';
|
|
||||||
|
|
||||||
import 'package:jisho_study_tool/models/history/word_result.dart';
|
|
||||||
|
|
||||||
@Entity()
|
|
||||||
class SearchString {
|
|
||||||
int id = 0;
|
|
||||||
|
|
||||||
@Property(type: PropertyType.date)
|
|
||||||
DateTime timestamp;
|
|
||||||
|
|
||||||
String query;
|
|
||||||
|
|
||||||
@Backlink()
|
|
||||||
final chosenResults = ToMany<WordResult>();
|
|
||||||
|
|
||||||
SearchString({
|
|
||||||
required this.timestamp,
|
|
||||||
required this.query,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return "[${timestamp.toIso8601String()}] \"$query\"";
|
|
||||||
}
|
|
||||||
}
|
|
19
lib/models/history/word_query.dart
Normal file
19
lib/models/history/word_query.dart
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import 'package:objectbox/objectbox.dart';
|
||||||
|
|
||||||
|
import './word_result.dart';
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
class WordQuery {
|
||||||
|
int id;
|
||||||
|
|
||||||
|
String query;
|
||||||
|
|
||||||
|
// TODO: Link query with results that the user clicks onto.
|
||||||
|
@Backlink()
|
||||||
|
final chosenResults = ToMany<WordResult>();
|
||||||
|
|
||||||
|
WordQuery({
|
||||||
|
this.id = 0,
|
||||||
|
required this.query,
|
||||||
|
});
|
||||||
|
}
|
@ -1,25 +1,21 @@
|
|||||||
import 'package:objectbox/objectbox.dart';
|
import 'package:objectbox/objectbox.dart';
|
||||||
|
|
||||||
import 'package:jisho_study_tool/models/history/search_string.dart';
|
import 'package:jisho_study_tool/models/history/word_query.dart';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
class WordResult {
|
class WordResult {
|
||||||
int id = 0;
|
int id;
|
||||||
|
|
||||||
@Property(type: PropertyType.date)
|
@Property(type: PropertyType.date)
|
||||||
DateTime timestamp;
|
DateTime timestamp;
|
||||||
|
|
||||||
String word;
|
String word;
|
||||||
|
|
||||||
final searchString = ToOne<SearchString>();
|
final searchString = ToOne<WordQuery>();
|
||||||
|
|
||||||
WordResult({
|
WordResult({
|
||||||
|
this.id = 0,
|
||||||
required this.timestamp,
|
required this.timestamp,
|
||||||
required this.word,
|
required this.word,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return "[${timestamp.toIso8601String()}] - $word";
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -3,54 +3,6 @@
|
|||||||
"_note2": "ObjectBox manages crucial IDs for your object model. See docs for details.",
|
"_note2": "ObjectBox manages crucial IDs for your object model. See docs for details.",
|
||||||
"_note3": "If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.",
|
"_note3": "If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.",
|
||||||
"entities": [
|
"entities": [
|
||||||
{
|
|
||||||
"id": "1:8135239166970424087",
|
|
||||||
"lastPropertyId": "3:1930470268740402049",
|
|
||||||
"name": "KanjiResult",
|
|
||||||
"properties": [
|
|
||||||
{
|
|
||||||
"id": "1:2681934095975267680",
|
|
||||||
"name": "id",
|
|
||||||
"type": 6,
|
|
||||||
"flags": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "2:4514526257378540330",
|
|
||||||
"name": "timestamp",
|
|
||||||
"type": 10
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "3:1930470268740402049",
|
|
||||||
"name": "kanji",
|
|
||||||
"type": 9
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"relations": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "2:461492167249325765",
|
|
||||||
"lastPropertyId": "3:7573103520245228403",
|
|
||||||
"name": "SearchString",
|
|
||||||
"properties": [
|
|
||||||
{
|
|
||||||
"id": "1:4297905889790758495",
|
|
||||||
"name": "id",
|
|
||||||
"type": 6,
|
|
||||||
"flags": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "2:4157902147911002923",
|
|
||||||
"name": "timestamp",
|
|
||||||
"type": 10
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "3:7573103520245228403",
|
|
||||||
"name": "query",
|
|
||||||
"type": 9
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"relations": []
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"id": "3:8314315977756262774",
|
"id": "3:8314315977756262774",
|
||||||
"lastPropertyId": "4:7972948456299367594",
|
"lastPropertyId": "4:7972948456299367594",
|
||||||
@ -78,21 +30,112 @@
|
|||||||
"type": 11,
|
"type": 11,
|
||||||
"flags": 520,
|
"flags": 520,
|
||||||
"indexId": "1:6146948198859733323",
|
"indexId": "1:6146948198859733323",
|
||||||
"relationTarget": "SearchString"
|
"relationTarget": "WordQuery"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"relations": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4:4256390943850643278",
|
||||||
|
"lastPropertyId": "3:1496429060084558178",
|
||||||
|
"name": "KanjiQuery",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "1:2966275213904862677",
|
||||||
|
"name": "id",
|
||||||
|
"type": 6,
|
||||||
|
"flags": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2:3733952844232949036",
|
||||||
|
"name": "kanji",
|
||||||
|
"type": 9
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"relations": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5:3499538826755540666",
|
||||||
|
"lastPropertyId": "3:1154921921492752045",
|
||||||
|
"name": "WordQuery",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "1:2582448470002735577",
|
||||||
|
"name": "id",
|
||||||
|
"type": 6,
|
||||||
|
"flags": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2:6622038022626247037",
|
||||||
|
"name": "query",
|
||||||
|
"type": 9
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"relations": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "6:8118874861016646859",
|
||||||
|
"lastPropertyId": "5:818915488505962903",
|
||||||
|
"name": "Search",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "1:3233720904924970047",
|
||||||
|
"name": "id",
|
||||||
|
"type": 6,
|
||||||
|
"flags": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2:7793044338609887616",
|
||||||
|
"name": "timestamp",
|
||||||
|
"type": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4:5737790291742758071",
|
||||||
|
"name": "wordQueryId",
|
||||||
|
"type": 11,
|
||||||
|
"flags": 520,
|
||||||
|
"indexId": "4:4174896839978600983",
|
||||||
|
"relationTarget": "WordQuery"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5:818915488505962903",
|
||||||
|
"name": "kanjiQueryId",
|
||||||
|
"type": 11,
|
||||||
|
"flags": 520,
|
||||||
|
"indexId": "5:5394995618034342416",
|
||||||
|
"relationTarget": "KanjiQuery"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"relations": []
|
"relations": []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"lastEntityId": "3:8314315977756262774",
|
"lastEntityId": "6:8118874861016646859",
|
||||||
"lastIndexId": "1:6146948198859733323",
|
"lastIndexId": "5:5394995618034342416",
|
||||||
"lastRelationId": "0:0",
|
"lastRelationId": "1:2624712325077938293",
|
||||||
"lastSequenceId": "0:0",
|
"lastSequenceId": "0:0",
|
||||||
"modelVersion": 5,
|
"modelVersion": 5,
|
||||||
"modelVersionParserMinimum": 5,
|
"modelVersionParserMinimum": 5,
|
||||||
"retiredEntityUids": [],
|
"retiredEntityUids": [
|
||||||
"retiredIndexUids": [],
|
8135239166970424087,
|
||||||
"retiredPropertyUids": [],
|
461492167249325765
|
||||||
"retiredRelationUids": [],
|
],
|
||||||
|
"retiredIndexUids": [
|
||||||
|
2344626140411525437,
|
||||||
|
1957456749938325194
|
||||||
|
],
|
||||||
|
"retiredPropertyUids": [
|
||||||
|
2681934095975267680,
|
||||||
|
4514526257378540330,
|
||||||
|
1930470268740402049,
|
||||||
|
4297905889790758495,
|
||||||
|
4157902147911002923,
|
||||||
|
7573103520245228403,
|
||||||
|
1496429060084558178,
|
||||||
|
1154921921492752045,
|
||||||
|
2254834401134912797
|
||||||
|
],
|
||||||
|
"retiredRelationUids": [
|
||||||
|
2624712325077938293
|
||||||
|
],
|
||||||
"version": 1
|
"version": 1
|
||||||
}
|
}
|
51
lib/view/components/history/date_divider.dart
Normal file
51
lib/view/components/history/date_divider.dart
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class DateDivider extends StatelessWidget {
|
||||||
|
final String? text;
|
||||||
|
final DateTime? date;
|
||||||
|
|
||||||
|
const DateDivider({this.text, this.date, Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
String getHumanReadableDate(DateTime date) {
|
||||||
|
const Map<int, String> monthTable = {
|
||||||
|
1: 'Jan',
|
||||||
|
2: 'Feb',
|
||||||
|
3: 'Mar',
|
||||||
|
4: 'Apr',
|
||||||
|
5: 'May',
|
||||||
|
6: 'Jun',
|
||||||
|
7: 'Jul',
|
||||||
|
8: 'Aug',
|
||||||
|
9: 'Sep',
|
||||||
|
10: 'Oct',
|
||||||
|
11: 'Nov',
|
||||||
|
12: 'Dec',
|
||||||
|
};
|
||||||
|
|
||||||
|
int day = date.day;
|
||||||
|
String month = monthTable[date.month]!;
|
||||||
|
int year = date.year;
|
||||||
|
return "$day. $month $year";
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
Widget header = (this.text != null)
|
||||||
|
? Text(this.text!)
|
||||||
|
: (this.date != null)
|
||||||
|
? Text(getHumanReadableDate(this.date!))
|
||||||
|
: SizedBox.shrink();
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
child: DefaultTextStyle.merge(
|
||||||
|
child: header,
|
||||||
|
style: TextStyle(color: Colors.white),
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(color: Colors.grey),
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
vertical: 5,
|
||||||
|
horizontal: 10,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,39 +1,78 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||||
import 'package:jisho_study_tool/models/history/kanji_result.dart';
|
import 'package:jisho_study_tool/bloc/kanji/kanji_bloc.dart';
|
||||||
|
import 'package:jisho_study_tool/bloc/navigation/navigation_bloc.dart';
|
||||||
|
import 'package:jisho_study_tool/models/history/kanji_query.dart';
|
||||||
|
|
||||||
class _KanjiSearchItemHeader extends StatelessWidget {
|
import './search_item.dart';
|
||||||
final KanjiResult result;
|
|
||||||
|
|
||||||
const _KanjiSearchItemHeader(this.result, {Key? key}) : super(key: key);
|
class _KanjiBox extends StatelessWidget {
|
||||||
|
final String kanji;
|
||||||
|
|
||||||
|
const _KanjiBox(this.kanji);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Text("[KANJI] ${result.kanji} - ${result.timestamp.toIso8601String()}");
|
return IntrinsicHeight(
|
||||||
|
child: AspectRatio(
|
||||||
|
aspectRatio: 1,
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.all(5),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey[300],
|
||||||
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: FittedBox(
|
||||||
|
child: Text(
|
||||||
|
kanji,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.black,
|
||||||
|
fontSize: 25,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class KanjiSearchItem extends StatelessWidget {
|
class KanjiSearchItem extends StatelessWidget {
|
||||||
final KanjiResult result;
|
final KanjiQuery result;
|
||||||
|
final DateTime timestamp;
|
||||||
|
|
||||||
const KanjiSearchItem(this.result,{Key? key}) : super(key: key);
|
const KanjiSearchItem({
|
||||||
|
required this.result,
|
||||||
|
required this.timestamp,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Slidable(
|
return Slidable(
|
||||||
child: ListTile(title: _KanjiSearchItemHeader(result)),
|
child: SearchItem(
|
||||||
|
onTap: () {
|
||||||
|
BlocProvider.of<NavigationBloc>(context).add(ChangePage(1));
|
||||||
|
BlocProvider.of<KanjiBloc>(context).add(GetKanji(this.result.kanji));
|
||||||
|
},
|
||||||
|
time: timestamp,
|
||||||
|
search: _KanjiBox(result.kanji),
|
||||||
|
),
|
||||||
actionPane: SlidableScrollActionPane(),
|
actionPane: SlidableScrollActionPane(),
|
||||||
secondaryActions: [
|
secondaryActions: [
|
||||||
IconSlideAction(
|
IconSlideAction(
|
||||||
caption: "Favourite",
|
caption: "Favourite",
|
||||||
color: Colors.yellow,
|
color: Colors.yellow,
|
||||||
icon: Icons.star
|
icon: Icons.star,
|
||||||
),
|
),
|
||||||
IconSlideAction(
|
IconSlideAction(
|
||||||
caption: "Delete",
|
caption: "Delete",
|
||||||
color: Colors.red,
|
color: Colors.red,
|
||||||
icon: Icons.delete
|
icon: Icons.delete,
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
38
lib/view/components/history/phrase_search_item.dart
Normal file
38
lib/view/components/history/phrase_search_item.dart
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||||
|
import 'package:jisho_study_tool/bloc/navigation/navigation_bloc.dart';
|
||||||
|
import 'package:jisho_study_tool/bloc/search/search_bloc.dart';
|
||||||
|
import 'package:jisho_study_tool/models/history/word_query.dart';
|
||||||
|
|
||||||
|
import './search_item.dart';
|
||||||
|
|
||||||
|
class PhraseSearchItem extends StatelessWidget {
|
||||||
|
final WordQuery search;
|
||||||
|
final DateTime timestamp;
|
||||||
|
|
||||||
|
const PhraseSearchItem({
|
||||||
|
required this.search,
|
||||||
|
required this.timestamp,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Slidable(
|
||||||
|
actionPane: SlidableScrollActionPane(),
|
||||||
|
secondaryActions: [
|
||||||
|
IconSlideAction(
|
||||||
|
caption: "Delete", color: Colors.red, icon: Icons.delete)
|
||||||
|
],
|
||||||
|
child: SearchItem(
|
||||||
|
onTap: () {
|
||||||
|
BlocProvider.of<NavigationBloc>(context).add(ChangePage(0));
|
||||||
|
BlocProvider.of<SearchBloc>(context).add(GetSearchResults(this.search.query));
|
||||||
|
},
|
||||||
|
time: timestamp,
|
||||||
|
search: Text(search.query),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,41 +1,39 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
|
||||||
import 'package:jisho_study_tool/models/history/search_string.dart';
|
|
||||||
|
|
||||||
class SearchItemHeader extends StatelessWidget {
|
class SearchItem extends StatelessWidget {
|
||||||
final SearchString _search;
|
final DateTime time;
|
||||||
|
final Widget search;
|
||||||
|
final void Function()? onTap;
|
||||||
|
|
||||||
const SearchItemHeader(this._search, {Key? key}) : super(key: key);
|
const SearchItem({
|
||||||
|
required this.time,
|
||||||
|
required this.search,
|
||||||
|
this.onTap,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
String getTime() {
|
||||||
|
final hours = this.time.hour.toString().padLeft(2, '0');
|
||||||
|
final mins = this.time.minute.toString().padLeft(2, '0');
|
||||||
|
return "$hours:$mins";
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
child: Text("[SEARCH] ${_search.query} - ${_search.timestamp.toString()}"),
|
child: ListTile(
|
||||||
);
|
onTap: onTap,
|
||||||
}
|
contentPadding: EdgeInsets.zero,
|
||||||
}
|
title: Row(
|
||||||
|
children: [
|
||||||
class SearchItem extends StatelessWidget {
|
Padding(
|
||||||
final SearchString _search;
|
padding: EdgeInsets.symmetric(horizontal: 20),
|
||||||
|
child: Text(getTime()),
|
||||||
const SearchItem(this._search, {Key? key}) : super(key: key);
|
),
|
||||||
|
search,
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Slidable(
|
|
||||||
actionPane: SlidableScrollActionPane(),
|
|
||||||
secondaryActions: [
|
|
||||||
IconSlideAction(
|
|
||||||
caption: "Delete",
|
|
||||||
color: Colors.red,
|
|
||||||
icon: Icons.delete
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
child: ExpansionTile(
|
),
|
||||||
title: SearchItemHeader(_search),
|
),
|
||||||
expandedAlignment: Alignment.topCenter,
|
|
||||||
children: [ListTile(title: Text(_search.timestamp.toIso8601String()),)],
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,14 +14,12 @@ class KanjiSearchBar extends StatefulWidget {
|
|||||||
enum TextFieldButton {clear, paste}
|
enum TextFieldButton {clear, paste}
|
||||||
|
|
||||||
class _KanjiSearchBarState extends State<KanjiSearchBar> {
|
class _KanjiSearchBarState extends State<KanjiSearchBar> {
|
||||||
FocusNode focus = new FocusNode();
|
final TextEditingController textController = new TextEditingController();
|
||||||
TextEditingController textController = new TextEditingController();
|
|
||||||
TextFieldButton button = TextFieldButton.paste;
|
TextFieldButton button = TextFieldButton.paste;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
// focus.addListener(_onFocusChange);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _getKanjiSuggestions(String text) =>
|
void _getKanjiSuggestions(String text) =>
|
||||||
@ -57,10 +55,8 @@ class _KanjiSearchBarState extends State<KanjiSearchBar> {
|
|||||||
return TextField(
|
return TextField(
|
||||||
controller: textController,
|
controller: textController,
|
||||||
onChanged: (text) => _getKanjiSuggestions(text),
|
onChanged: (text) => _getKanjiSuggestions(text),
|
||||||
onSubmitted: (text) => {},
|
onSubmitted: (_) => {},
|
||||||
// BlocProvider.of<KanjiBloc>(context).add(GetKanji(text)),
|
|
||||||
decoration: new InputDecoration(
|
decoration: new InputDecoration(
|
||||||
|
|
||||||
prefixIcon: Icon(Icons.search),
|
prefixIcon: Icon(Icons.search),
|
||||||
hintText: 'Search',
|
hintText: 'Search',
|
||||||
fillColor: Colors.white,
|
fillColor: Colors.white,
|
||||||
|
@ -1,35 +1,74 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:jisho_study_tool/bloc/database/database_bloc.dart';
|
import 'package:jisho_study_tool/bloc/database/database_bloc.dart';
|
||||||
import 'package:jisho_study_tool/models/history/kanji_result.dart';
|
import 'package:jisho_study_tool/models/history/search.dart';
|
||||||
import 'package:jisho_study_tool/models/history/search_string.dart';
|
|
||||||
import 'package:jisho_study_tool/view/components/history/kanji_search_item.dart';
|
import 'package:jisho_study_tool/view/components/history/kanji_search_item.dart';
|
||||||
import 'package:jisho_study_tool/view/components/history/search_item.dart';
|
import 'package:jisho_study_tool/view/components/history/phrase_search_item.dart';
|
||||||
|
import 'package:jisho_study_tool/view/components/history/date_divider.dart';
|
||||||
|
|
||||||
|
import 'package:jisho_study_tool/objectbox.g.dart';
|
||||||
|
|
||||||
class HistoryView extends StatelessWidget {
|
class HistoryView extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// return ListView.builder(
|
|
||||||
// itemBuilder: (context, index) => ListTile(),
|
|
||||||
// );
|
|
||||||
return BlocBuilder<DatabaseBloc, DatabaseState>(
|
return BlocBuilder<DatabaseBloc, DatabaseState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
if (state is DatabaseDisconnected)
|
if (state is DatabaseDisconnected)
|
||||||
throw DatabaseNotConnectedException();
|
throw DatabaseNotConnectedException();
|
||||||
return ListView(
|
|
||||||
children: (state as DatabaseConnected)
|
|
||||||
.database
|
|
||||||
.box<SearchString>()
|
|
||||||
.getAll()
|
|
||||||
.map((e) => SearchItem(e) as Widget)
|
|
||||||
.toList()
|
|
||||||
|
|
||||||
+ (state as DatabaseConnected)
|
return StreamBuilder(
|
||||||
.database
|
stream: ((state as DatabaseConnected).database.box<Search>().query()
|
||||||
.box<KanjiResult>()
|
..order(Search_.timestamp, flags: Order.descending))
|
||||||
.getAll()
|
.watch(triggerImmediately: true)
|
||||||
.map((e) => KanjiSearchItem(e) as Widget)
|
.map((query) => query.find()),
|
||||||
.toList(),
|
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||||||
|
if (!snapshot.hasData) return Container();
|
||||||
|
return ListView.separated(
|
||||||
|
itemCount: snapshot.data.length + 1,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
if (index == 0) return Container();
|
||||||
|
Search search = snapshot.data[index - 1];
|
||||||
|
if (search.isKanji()) {
|
||||||
|
return KanjiSearchItem(
|
||||||
|
result: search.kanjiQuery.target!,
|
||||||
|
timestamp: search.timestamp,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return PhraseSearchItem(
|
||||||
|
search: search.wordQuery.target!,
|
||||||
|
timestamp: search.timestamp,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
separatorBuilder: (context, index) {
|
||||||
|
Function roundToDay = (DateTime date) =>
|
||||||
|
DateTime(date.year, date.month, date.day);
|
||||||
|
|
||||||
|
Search search = snapshot.data[index];
|
||||||
|
DateTime searchDate = roundToDay(search.timestamp);
|
||||||
|
|
||||||
|
bool newDate = true;
|
||||||
|
|
||||||
|
if (index != 0) {
|
||||||
|
Search prevSearch = snapshot.data[index - 1];
|
||||||
|
|
||||||
|
DateTime prevSearchDate = roundToDay(prevSearch.timestamp);
|
||||||
|
newDate = prevSearchDate != searchDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newDate) {
|
||||||
|
if (searchDate == roundToDay(DateTime.now()))
|
||||||
|
return DateDivider(text: "Today");
|
||||||
|
else if (searchDate ==
|
||||||
|
roundToDay(
|
||||||
|
DateTime.now().subtract(const Duration(days: 1))))
|
||||||
|
return DateDivider(text: "Yesterday");
|
||||||
|
return DateDivider(date: searchDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Divider();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user