Add lots of history functionality

adapt-navigator
Oystein Kristoffer Tveit 2021-08-03 22:02:42 +02:00
parent d82fcbe427
commit e8f42860af
19 changed files with 525 additions and 278 deletions

View File

@ -1,7 +1,8 @@
import 'dart:async';
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_state.dart';
@ -14,7 +15,6 @@ export './kanji_event.dart';
export './kanji_state.dart';
class KanjiBloc extends Bloc<KanjiEvent, KanjiState> {
DatabaseBloc _databaseBloc;
KanjiBloc(this._databaseBloc) : super(KanjiSearch(KanjiSearchType.Initial));
@ -24,34 +24,32 @@ class KanjiBloc extends Bloc<KanjiEvent, KanjiState> {
throw DatabaseNotConnectedException;
(_databaseBloc.state as DatabaseConnected)
.database
.box<KanjiResult>()
.put(KanjiResult(
kanji: kanji,
timestamp: DateTime.now(),
));
.database
.box<Search>()
.put(Search(timestamp: DateTime.now())
..kanjiQuery.target = KanjiQuery(
kanji: kanji,
));
}
@override
Stream<KanjiState> mapEventToState(KanjiEvent event)
async* {
Stream<KanjiState> mapEventToState(KanjiEvent event) async* {
if (event is GetKanji) {
yield KanjiSearchLoading();
try {
addSearchToDB(event.kanjiSearchString);
final kanji = await fetchKanji(event.kanjiSearchString);
if (kanji.found) yield KanjiSearchFinished(kanji: kanji);
else yield KanjiSearchError('Something went wrong');
if (kanji.found)
yield KanjiSearchFinished(kanji: kanji);
else
yield KanjiSearchError('Something went wrong');
} on Exception {
yield KanjiSearchError('Something went wrong');
}
} else if (event is GetKanjiSuggestions) {
final suggestions = kanjiSuggestions(event.searchString);
yield KanjiSearchKeyboard(KanjiSearchType.Keyboard, suggestions);
} else if (event is ReturnToInitialState) {
yield KanjiSearch(KanjiSearchType.Initial);
}

View 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);
}
}

View File

@ -0,0 +1,8 @@
abstract class NavigationEvent {
const NavigationEvent();
}
class ChangePage extends NavigationEvent {
final int pageNum;
const ChangePage(this.pageNum);
}

View File

@ -0,0 +1,9 @@
abstract class NavigationState {
const NavigationState();
}
class NavigationPage extends NavigationState {
final int pageNum;
const NavigationPage(this.pageNum);
}

View File

@ -3,7 +3,8 @@ import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:flutter/cupertino.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:jisho_study_tool/bloc/database/database_bloc.dart';
@ -14,7 +15,6 @@ part 'search_event.dart';
part 'search_state.dart';
class SearchBloc extends Bloc<SearchEvent, SearchState> {
DatabaseBloc _databaseBloc;
SearchBloc(this._databaseBloc) : super(SearchInitial());
@ -24,12 +24,12 @@ class SearchBloc extends Bloc<SearchEvent, SearchState> {
throw DatabaseNotConnectedException;
(_databaseBloc.state as DatabaseConnected)
.database
.box<SearchString>()
.put(SearchString(
query: searchString,
timestamp: DateTime.now(),
));
.database
.box<Search>()
.put(Search(timestamp: DateTime.now())
..wordQuery.target = WordQuery(
query: searchString,
));
}
@override
@ -42,7 +42,8 @@ class SearchBloc extends Bloc<SearchEvent, SearchState> {
try {
addSearchToDB(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 {
yield SearchError('Something went wrong');
}

View File

@ -1,13 +1,16 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:jisho_study_tool/view/screens/loading.dart';
import 'package:mdi/mdi.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.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/kanji/kanji_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/history.dart';
@ -18,49 +21,26 @@ void main() => runApp(MyApp());
DatabaseBloc _databaseBloc = DatabaseBloc();
class MyApp extends StatelessWidget {
class MyApp extends StatefulWidget {
@override
Widget build(BuildContext context) {
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(),
),
);
}
_MyAppState createState() => _MyAppState();
}
class Home extends StatefulWidget {
@override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
int selectedPage = 0;
class _MyAppState extends State<MyApp> {
late final Store _store;
@override
void initState() {
super.initState();
getApplicationDocumentsDirectory()
.then((dir) {
_store = Store(
getObjectBoxModel(),
directory: join(dir.path, 'objectbox'),
);
getApplicationDocumentsDirectory().then((dir) {
_store = Store(
getObjectBoxModel(),
directory: join(dir.path, 'objectbox'),
);
_databaseBloc.add(ConnectedToDatabase(_store));
});
_databaseBloc.add(ConnectedToDatabase(_store));
});
}
@override
@ -72,14 +52,41 @@ class _HomeState extends State<Home> {
@override
Widget build(BuildContext context) {
return BlocBuilder<DatabaseBloc, DatabaseState>(
builder: (context, state) {
return MaterialApp(
title: 'Jisho Study Tool',
if (state is DatabaseDisconnected) {
return Center(
child: CircularProgressIndicator(),
);
}
// TODO: Add color theme
theme: ThemeData(
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(
appBar: AppBar(
@ -89,12 +96,9 @@ class _HomeState extends State<Home> {
body: pages[selectedPage].content,
bottomNavigationBar: BottomNavigationBar(
currentIndex: selectedPage,
onTap: (int index) {
setState(() {
selectedPage = index;
});
},
items: navBar,
onTap: (int index) =>
BlocProvider.of<NavigationBloc>(context).add(ChangePage(index)),
items: pages.map((p) => p.item).toList(),
showSelectedLabels: false,
showUnselectedLabels: false,
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 {
Widget content;
Widget titleBar;
final Widget content;
final Widget titleBar;
final BottomNavigationBarItem item;
_Page({
const _Page({
required this.content,
required this.titleBar,
required this.item,
});
}
@ -145,21 +126,39 @@ final List<_Page> pages = [
_Page(
content: SearchView(),
titleBar: Text('Search'),
item: BottomNavigationBarItem(
label: 'Search',
icon: Icon(Icons.search),
),
),
_Page(
content: KanjiView(),
titleBar: KanjiViewBar(),
item: BottomNavigationBarItem(
label: 'Kanji', icon: Icon(Mdi.ideogramCjk, size: 30)),
),
_Page(
content: HistoryView(),
titleBar: Text("History"),
item: BottomNavigationBarItem(
label: 'History',
icon: Icon(Icons.history),
),
),
_Page(
content: Container(),
titleBar: Text("Memorization"),
titleBar: Text("Saved"),
item: BottomNavigationBarItem(
label: 'Saved',
icon: Icon(Icons.bookmark),
),
),
_Page(
content: Container(),
titleBar: Text("Settings"),
item: BottomNavigationBarItem(
label: 'Settings',
icon: Icon(Icons.settings),
),
),
];

View File

@ -0,0 +1,13 @@
import 'package:objectbox/objectbox.dart';
@Entity()
class KanjiQuery {
int id;
String kanji;
KanjiQuery({
this.id = 0,
required this.kanji,
});
}

View File

@ -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";
}
}

View 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;
}
}

View File

@ -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\"";
}
}

View 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,
});
}

View File

@ -1,25 +1,21 @@
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()
class WordResult {
int id = 0;
int id;
@Property(type: PropertyType.date)
DateTime timestamp;
String word;
final searchString = ToOne<SearchString>();
final searchString = ToOne<WordQuery>();
WordResult({
this.id = 0,
required this.timestamp,
required this.word,
});
@override
String toString() {
return "[${timestamp.toIso8601String()}] - $word";
}
}

View File

@ -3,54 +3,6 @@
"_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.",
"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",
"lastPropertyId": "4:7972948456299367594",
@ -78,21 +30,112 @@
"type": 11,
"flags": 520,
"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": []
}
],
"lastEntityId": "3:8314315977756262774",
"lastIndexId": "1:6146948198859733323",
"lastRelationId": "0:0",
"lastEntityId": "6:8118874861016646859",
"lastIndexId": "5:5394995618034342416",
"lastRelationId": "1:2624712325077938293",
"lastSequenceId": "0:0",
"modelVersion": 5,
"modelVersionParserMinimum": 5,
"retiredEntityUids": [],
"retiredIndexUids": [],
"retiredPropertyUids": [],
"retiredRelationUids": [],
"retiredEntityUids": [
8135239166970424087,
461492167249325765
],
"retiredIndexUids": [
2344626140411525437,
1957456749938325194
],
"retiredPropertyUids": [
2681934095975267680,
4514526257378540330,
1930470268740402049,
4297905889790758495,
4157902147911002923,
7573103520245228403,
1496429060084558178,
1154921921492752045,
2254834401134912797
],
"retiredRelationUids": [
2624712325077938293
],
"version": 1
}

View 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,
),
);
}
}

View File

@ -1,39 +1,78 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.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 {
final KanjiResult result;
import './search_item.dart';
const _KanjiSearchItemHeader(this.result, {Key? key}) : super(key: key);
class _KanjiBox extends StatelessWidget {
final String kanji;
const _KanjiBox(this.kanji);
@override
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 {
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
Widget build(BuildContext context) {
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(),
secondaryActions: [
IconSlideAction(
caption: "Favourite",
color: Colors.yellow,
icon: Icons.star
icon: Icons.star,
),
IconSlideAction(
caption: "Delete",
color: Colors.red,
icon: Icons.delete
)
icon: Icons.delete,
),
],
);
}

View 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),
),
);
}
}

View File

@ -1,41 +1,39 @@
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 {
final SearchString _search;
class SearchItem extends StatelessWidget {
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
Widget build(BuildContext context) {
return Container(
child: Text("[SEARCH] ${_search.query} - ${_search.timestamp.toString()}"),
);
}
}
class SearchItem extends StatelessWidget {
final SearchString _search;
const SearchItem(this._search, {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: ExpansionTile(
title: SearchItemHeader(_search),
expandedAlignment: Alignment.topCenter,
children: [ListTile(title: Text(_search.timestamp.toIso8601String()),)],
)
child: ListTile(
onTap: onTap,
contentPadding: EdgeInsets.zero,
title: Row(
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 20),
child: Text(getTime()),
),
search,
],
),
),
);
}
}

View File

@ -14,14 +14,12 @@ class KanjiSearchBar extends StatefulWidget {
enum TextFieldButton {clear, paste}
class _KanjiSearchBarState extends State<KanjiSearchBar> {
FocusNode focus = new FocusNode();
TextEditingController textController = new TextEditingController();
final TextEditingController textController = new TextEditingController();
TextFieldButton button = TextFieldButton.paste;
@override
void initState() {
super.initState();
// focus.addListener(_onFocusChange);
}
void _getKanjiSuggestions(String text) =>
@ -57,10 +55,8 @@ class _KanjiSearchBarState extends State<KanjiSearchBar> {
return TextField(
controller: textController,
onChanged: (text) => _getKanjiSuggestions(text),
onSubmitted: (text) => {},
// BlocProvider.of<KanjiBloc>(context).add(GetKanji(text)),
onSubmitted: (_) => {},
decoration: new InputDecoration(
prefixIcon: Icon(Icons.search),
hintText: 'Search',
fillColor: Colors.white,

View File

@ -1,35 +1,74 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_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_string.dart';
import 'package:jisho_study_tool/models/history/search.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 {
@override
Widget build(BuildContext context) {
// return ListView.builder(
// itemBuilder: (context, index) => ListTile(),
// );
return BlocBuilder<DatabaseBloc, DatabaseState>(
builder: (context, state) {
if (state is DatabaseDisconnected)
throw DatabaseNotConnectedException();
return ListView(
children: (state as DatabaseConnected)
.database
.box<SearchString>()
.getAll()
.map((e) => SearchItem(e) as Widget)
.toList()
+ (state as DatabaseConnected)
.database
.box<KanjiResult>()
.getAll()
.map((e) => KanjiSearchItem(e) as Widget)
.toList(),
return StreamBuilder(
stream: ((state as DatabaseConnected).database.box<Search>().query()
..order(Search_.timestamp, flags: Order.descending))
.watch(triggerImmediately: true)
.map((query) => query.find()),
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();
},
);
},
);
},
);