Add database for search history

adapt-navigator
Oystein Kristoffer Tveit 2021-07-17 16:11:17 +02:00
parent 1aebc38954
commit 25e270ec1d
12 changed files with 557 additions and 30 deletions

View File

@ -0,0 +1,25 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import './database_event.dart';
import './database_state.dart';
export './database_event.dart';
export './database_state.dart';
export './database_not_connected_exception.dart';
class DatabaseBloc extends Bloc<DatabaseEvent, DatabaseState> {
DatabaseBloc() : super(DatabaseDisconnected());
@override
Stream<DatabaseState> mapEventToState(DatabaseEvent event)
async* {
if (event is ConnectedToDatabase) {
yield DatabaseConnected(event.database);
} else {
yield DatabaseDisconnected();
}
}
}

View File

@ -0,0 +1,14 @@
import 'package:objectbox/objectbox.dart';
abstract class DatabaseEvent {
const DatabaseEvent();
}
class ConnectedToDatabase extends DatabaseEvent {
final Store database;
const ConnectedToDatabase(this.database);
}
class DisconnectedFromDatabase extends DatabaseEvent {
const DisconnectedFromDatabase();
}

View File

@ -0,0 +1 @@
class DatabaseNotConnectedException implements Exception {}

View File

@ -0,0 +1,15 @@
import 'package:objectbox/objectbox.dart';
abstract class DatabaseState {
const DatabaseState();
}
class DatabaseConnected extends DatabaseState {
final Store database;
const DatabaseConnected(this.database);
}
class DatabaseDisconnected extends DatabaseState {
const DatabaseDisconnected();
}

View File

@ -1,5 +1,8 @@
import 'dart:async';
import 'package:jisho_study_tool/bloc/database/database_bloc.dart';
import 'package:jisho_study_tool/models/history/search.dart';
import './kanji_event.dart';
import './kanji_state.dart';
@ -12,7 +15,23 @@ export './kanji_state.dart';
class KanjiBloc extends Bloc<KanjiEvent, KanjiState> {
KanjiBloc() : super(KanjiSearch(KanjiSearchType.Initial));
DatabaseBloc _databaseBloc;
KanjiBloc(this._databaseBloc) : super(KanjiSearch(KanjiSearchType.Initial));
void addSearchToDB(searchString) {
if (_databaseBloc.state is DatabaseDisconnected)
throw DatabaseNotConnectedException;
(_databaseBloc.state as DatabaseConnected)
.database
.box<Search>()
.put(Search(
query: searchString,
timestamp: DateTime.now(),
type: "kanji"
));
}
@override
Stream<KanjiState> mapEventToState(KanjiEvent event)
@ -22,6 +41,7 @@ class KanjiBloc extends Bloc<KanjiEvent, KanjiState> {
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');

View File

@ -3,8 +3,10 @@ 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.dart';
import 'package:meta/meta.dart';
import 'package:jisho_study_tool/bloc/database/database_bloc.dart';
import 'package:jisho_study_tool/services/jisho_api/jisho_search.dart';
import 'package:unofficial_jisho_api/parser.dart';
@ -12,7 +14,24 @@ part 'search_event.dart';
part 'search_state.dart';
class SearchBloc extends Bloc<SearchEvent, SearchState> {
SearchBloc() : super(SearchInitial());
DatabaseBloc _databaseBloc;
SearchBloc(this._databaseBloc) : super(SearchInitial());
void addSearchToDB(searchString) {
if (_databaseBloc.state is DatabaseDisconnected)
throw DatabaseNotConnectedException;
(_databaseBloc.state as DatabaseConnected)
.database
.box<Search>()
.put(Search(
query: searchString,
timestamp: DateTime.now(),
type: "search"
));
}
@override
Stream<SearchState> mapEventToState(
@ -22,6 +41,7 @@ class SearchBloc extends Bloc<SearchEvent, SearchState> {
yield SearchLoading();
try {
addSearchToDB(event.searchString);
final searchResults = await fetchJishoResults(event.searchString);
if (searchResults.meta.status == 200) yield SearchFinished(searchResults.data);
} on Exception {

View File

@ -1,16 +1,23 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.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/search/search_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/view/screens/kanji/view.dart';
import 'package:jisho_study_tool/view/screens/history.dart';
import 'package:jisho_study_tool/view/screens/search/view.dart';
import 'bloc/search/search_bloc.dart';
void main() => runApp(MyApp());
DatabaseBloc _databaseBloc = DatabaseBloc();
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
@ -21,8 +28,9 @@ class MyApp extends StatelessWidget {
),
home: MultiBlocProvider(
providers: [
BlocProvider(create: (context) => SearchBloc()),
BlocProvider(create: (context) => KanjiBloc()),
BlocProvider(create: (context) => SearchBloc(_databaseBloc)),
BlocProvider(create: (context) => KanjiBloc(_databaseBloc)),
BlocProvider(create: (context) => _databaseBloc),
],
child: Home(),
),
@ -38,27 +46,62 @@ class Home extends StatefulWidget {
class _HomeState extends State<Home> {
int selectedPage = 0;
Store _store;
@override
void initState() {
super.initState();
getApplicationDocumentsDirectory()
.then((dir) {
_store = Store(
getObjectBoxModel(),
directory: join(dir.path, 'objectbox'),
);
_databaseBloc.add(ConnectedToDatabase(_store));
});
}
@override
void dispose() {
_store.close();
_databaseBloc.add(DisconnectedFromDatabase());
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: pages[selectedPage].titleBar,
centerTitle: true,
),
body: pages[selectedPage].content,
bottomNavigationBar: BottomNavigationBar(
currentIndex: selectedPage,
onTap: (int index) {
setState(() {
selectedPage = index;
});
},
items: navBar,
showSelectedLabels: false,
showUnselectedLabels: false,
unselectedItemColor: Colors.blue,
selectedItemColor: Colors.green,
),
return BlocBuilder<DatabaseBloc, DatabaseState>(
builder: (context, state) {
if (state is DatabaseDisconnected) {
return Center(
child: CircularProgressIndicator(),
);
}
return Scaffold(
appBar: AppBar(
title: pages[selectedPage].titleBar,
centerTitle: true,
),
body: pages[selectedPage].content,
bottomNavigationBar: BottomNavigationBar(
currentIndex: selectedPage,
onTap: (int index) {
setState(() {
selectedPage = index;
});
},
items: navBar,
showSelectedLabels: false,
showUnselectedLabels: false,
unselectedItemColor: Colors.blue,
selectedItemColor: Colors.green,
),
);
},
);
}
}

View File

@ -0,0 +1,26 @@
import 'package:objectbox/objectbox.dart';
@Entity()
class Search {
int id = 0;
@Property(type: PropertyType.date)
DateTime timestamp;
String query;
String type;
Search({
this.id,
this.timestamp,
this.query,
this.type,
});
@override
String toString() {
return "${timestamp.toIso8601String()} [${type.toUpperCase()}] - $query";
}
}

47
lib/objectbox-model.json Normal file
View File

@ -0,0 +1,47 @@
{
"_note1": "KEEP THIS FILE! Check it into a version control system (VCS) like git.",
"_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:7471000004971513655",
"lastPropertyId": "4:1530573958565295186",
"name": "Search",
"properties": [
{
"id": "1:182004738902401315",
"name": "id",
"type": 6,
"flags": 1
},
{
"id": "2:647032929519296287",
"name": "timestamp",
"type": 10
},
{
"id": "3:8448353731705407210",
"name": "query",
"type": 9
},
{
"id": "4:1530573958565295186",
"name": "type",
"type": 9
}
],
"relations": []
}
],
"lastEntityId": "1:7471000004971513655",
"lastIndexId": "0:0",
"lastRelationId": "0:0",
"lastSequenceId": "0:0",
"modelVersion": 5,
"modelVersionParserMinimum": 5,
"retiredEntityUids": [],
"retiredIndexUids": [],
"retiredPropertyUids": [],
"retiredRelationUids": [],
"version": 1
}

View File

@ -1,10 +1,27 @@
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/search.dart';
class HistoryView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListView.builder(
itemBuilder: (context, index) => ListTile(),
// return ListView.builder(
// itemBuilder: (context, index) => ListTile(),
// );
return BlocBuilder<DatabaseBloc, DatabaseState>(
builder: (context, state) {
if (state is DatabaseDisconnected)
throw DatabaseNotConnectedException();
return Text(
(state as DatabaseConnected)
.database
.box<Search>()
.getAll()
.map((e) => e.toString())
.toString()
);
},
);
}
}

View File

@ -1,13 +1,34 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
_fe_analyzer_shared:
dependency: transitive
description:
name: _fe_analyzer_shared
url: "https://pub.dartlang.org"
source: hosted
version: "22.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
url: "https://pub.dartlang.org"
source: hosted
version: "1.7.2"
args:
dependency: transitive
description:
name: args
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
async:
dependency: transitive
description:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.5.0"
version: "2.7.0"
bloc:
dependency: transitive
description:
@ -22,6 +43,62 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
build:
dependency: transitive
description:
name: build
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.3"
build_config:
dependency: transitive
description:
name: build_config
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.7"
build_daemon:
dependency: transitive
description:
name: build_daemon
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.10"
build_resolvers:
dependency: transitive
description:
name: build_resolvers
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.4"
build_runner:
dependency: "direct dev"
description:
name: build_runner
url: "https://pub.dartlang.org"
source: hosted
version: "1.12.2"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
url: "https://pub.dartlang.org"
source: hosted
version: "6.1.12"
built_collection:
dependency: transitive
description:
name: built_collection
url: "https://pub.dartlang.org"
source: hosted
version: "5.1.0"
built_value:
dependency: transitive
description:
name: built_value
url: "https://pub.dartlang.org"
source: hosted
version: "8.1.1"
characters:
dependency: transitive
description:
@ -36,6 +113,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
checked_yaml:
dependency: transitive
description:
name: checked_yaml
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
cli_util:
dependency: transitive
description:
name: cli_util
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.3"
clock:
dependency: transitive
description:
@ -43,6 +134,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
code_builder:
dependency: transitive
description:
name: code_builder
url: "https://pub.dartlang.org"
source: hosted
version: "3.7.0"
collection:
dependency: transitive
description:
@ -50,6 +148,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.15.0"
convert:
dependency: transitive
description:
name: convert
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
crypto:
dependency: transitive
description:
name: crypto
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
csslib:
dependency: transitive
description:
@ -57,6 +169,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.16.2"
dart_style:
dependency: transitive
description:
name: dart_style
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.2"
division:
dependency: "direct main"
description:
@ -85,6 +204,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "6.1.0"
fixnum:
dependency: transitive
description:
name: fixnum
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
flutter:
dependency: "direct main"
description: flutter
@ -107,6 +233,20 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
glob:
dependency: transitive
description:
name: glob
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
graphs:
dependency: transitive
description:
name: graphs
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
html:
dependency: transitive
description:
@ -128,6 +268,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.2"
http_multi_server:
dependency: transitive
description:
name: http_multi_server
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
http_parser:
dependency: transitive
description:
@ -135,6 +282,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.4"
io:
dependency: transitive
description:
name: io
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.3"
js:
dependency: transitive
description:
@ -142,6 +296,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.3"
json_annotation:
dependency: transitive
description:
name: json_annotation
url: "https://pub.dartlang.org"
source: hosted
version: "4.1.0"
logging:
dependency: transitive
description:
name: logging
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
matcher:
dependency: transitive
description:
@ -162,7 +330,14 @@ packages:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
version: "1.4.0"
mime:
dependency: transitive
description:
name: mime
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
nested:
dependency: transitive
description:
@ -170,6 +345,34 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
objectbox:
dependency: "direct main"
description:
name: objectbox
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.1"
objectbox_flutter_libs:
dependency: "direct main"
description:
name: objectbox_flutter_libs
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.1"
objectbox_generator:
dependency: "direct dev"
description:
name: objectbox_generator
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.1"
package_config:
dependency: transitive
description:
name: package_config
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
path:
dependency: transitive
description:
@ -177,6 +380,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0"
path_provider:
dependency: "direct main"
description:
name: path_provider
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.2"
path_provider_linux:
dependency: transitive
description:
@ -184,6 +394,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
path_provider_macos:
dependency: transitive
description:
name: path_provider_macos
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
path_provider_platform_interface:
dependency: transitive
description:
@ -219,6 +436,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.3"
pool:
dependency: transitive
description:
name: pool
url: "https://pub.dartlang.org"
source: hosted
version: "1.5.0"
process:
dependency: transitive
description:
@ -233,6 +457,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "5.0.0"
pub_semver:
dependency: transitive
description:
name: pub_semver
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
pubspec_parse:
dependency: transitive
description:
name: pubspec_parse
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
shared_preferences:
dependency: "direct main"
description:
@ -275,11 +513,32 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
shelf:
dependency: transitive
description:
name: shelf
url: "https://pub.dartlang.org"
source: hosted
version: "0.7.9"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.4+1"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
source_gen:
dependency: transitive
description:
name: source_gen
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.3"
source_span:
dependency: transitive
description:
@ -301,6 +560,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
stream_transform:
dependency: transitive
description:
name: stream_transform
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
string_scanner:
dependency: transitive
description:
@ -321,7 +587,14 @@ packages:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.19"
version: "0.4.0"
timing:
dependency: transitive
description:
name: timing
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
typed_data:
dependency: transitive
description:
@ -385,6 +658,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
watcher:
dependency: transitive
description:
name: watcher
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
win32:
dependency: transitive
description:
@ -399,6 +686,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.0"
yaml:
dependency: transitive
description:
name: yaml
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.0"
sdks:
dart: ">=2.12.0 <3.0.0"
flutter: ">=1.22.0"
flutter: ">=2.0.0"

View File

@ -10,6 +10,9 @@ dependencies:
sdk: flutter
shared_preferences: "^2.0.3"
objectbox: ^1.1.1
objectbox_flutter_libs: any
path_provider: ^2.0.2
# cupertino_icons: ^0.1.2
mdi: ^4.0.0
@ -21,6 +24,8 @@ dependencies:
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^1.0.0
objectbox_generator: any
flutter:
uses-material-design: true