Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

187 feature request clicking on a tag brings up a list of all books with that tag #393

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions assets/translations/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -256,5 +256,9 @@
"title": "Minutes"
},
"set_custom_reading_time": "Set custom reading time",
"select_all": "Select all"
}
"select_all": "Select all",
"tags": {
"one": "Tag",
"other": "Tags"
}
}
42 changes: 42 additions & 0 deletions lib/database/database_controler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -178,4 +178,46 @@ class DatabaseController {
}
return await batch.commit();
}

Future<List<Book>> getBooksWithSameTag(String tag) async {
final db = await dbClient.db;

var result = await db.query(
"booksTable",
where: 'tags IS NOT NULL AND deleted = 0',
orderBy: 'publication_year ASC',
);

final booksWithTag = List<Book>.empty(growable: true);

if (result.isNotEmpty) {
final books = result.map((item) => Book.fromJSON(item)).toList();
for (final book in books) {
if (book.tags != null && book.tags!.isNotEmpty) {
for (final bookTag in book.tags!.split('|||||')) {
if (bookTag == tag) {
booksWithTag.add(book);
}
}
}
}
}

return booksWithTag;
}

Future<List<Book>> getBooksWithSameAuthor(String author) async {
final db = await dbClient.db;

var result = await db.query(
"booksTable",
where: 'author = ? AND deleted = 0',
whereArgs: [author],
orderBy: 'publication_year ASC',
);

return result.isNotEmpty
? result.map((item) => Book.fromJSON(item)).toList()
: [];
}
}
1 change: 1 addition & 0 deletions lib/generated/locale_keys.g.dart
Original file line number Diff line number Diff line change
Expand Up @@ -255,4 +255,5 @@ abstract class LocaleKeys {
static const set_custom_reading_time = 'set_custom_reading_time';
static const select_all = 'select_all';
static const only_editions_with_covers = 'only_editions_with_covers';
static const tags = 'tags';
}
23 changes: 23 additions & 0 deletions lib/logic/cubit/book_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ class BookCubit extends Cubit {
final BehaviorSubject<List<String>> _tagsFetcher =
BehaviorSubject<List<String>>();
final BehaviorSubject<Book?> _bookFetcher = BehaviorSubject<Book?>();
final BehaviorSubject<List<Book>?> _booksWithSameTagFetcher =
BehaviorSubject<List<Book>?>();
final BehaviorSubject<List<Book>?> _booksWithSameAuthorFetcher =
BehaviorSubject<List<Book>?>();

Stream<List<Book>> get allBooks => _booksFetcher.stream;
Stream<List<Book>> get finishedBooks => _finishedBooksFetcher.stream;
Expand All @@ -44,6 +48,9 @@ class BookCubit extends Cubit {
Stream<List<Book>> get searchBooks => _searchBooksFetcher.stream;
Stream<List<int>> get finishedYears => _finishedYearsFetcher.stream;
Stream<List<String>> get tags => _tagsFetcher.stream;
Stream<List<Book>?> get booksWithSameTag => _booksWithSameTagFetcher.stream;
Stream<List<Book>?> get booksWithSameAuthor =>
_booksWithSameAuthorFetcher.stream;

Stream<Book?> get book => _bookFetcher.stream;

Expand Down Expand Up @@ -244,4 +251,20 @@ class BookCubit extends Cubit {
final SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setBool('is_cover_migration_done', true);
}

getBooksWithSameTag(String tag) async {
_booksWithSameTagFetcher.sink.add(null);

List<Book> books = await repository.getBooksWithSameTag(tag);

_booksWithSameTagFetcher.sink.add(books);
}

getBooksWithSameAuthor(String author) async {
_booksWithSameAuthorFetcher.sink.add(null);

List<Book> books = await repository.getBooksWithSameAuthor(author);

_booksWithSameAuthorFetcher.sink.add(books);
}
}
6 changes: 6 additions & 0 deletions lib/resources/repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,10 @@ class Repository {
Future<List<Book>> getDeletedBooks() => dbController.getDeletedBooks();

Future<int> removeAllBooks() => dbController.removeAllBooks();

Future<List<Book>> getBooksWithSameTag(String tag) =>
dbController.getBooksWithSameTag(tag);

Future<List<Book>> getBooksWithSameAuthor(String author) =>
dbController.getBooksWithSameAuthor(author);
}
46 changes: 36 additions & 10 deletions lib/ui/book_screen/widgets/book_title_detail.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,18 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:openreads/core/constants/enums.dart';
import 'package:openreads/core/themes/app_theme.dart';
import 'package:openreads/generated/locale_keys.g.dart';
import 'package:openreads/ui/similiar_books_screen/similiar_books_screen.dart';

class BookTitleDetail extends StatelessWidget {
const BookTitleDetail({
Key? key,
super.key,
required this.title,
required this.subtitle,
required this.author,
required this.publicationYear,
required this.bookType,
this.tags,
}) : super(key: key);
});

final String title;
final String? subtitle;
Expand All @@ -41,7 +42,16 @@ class BookTitleDetail extends StatelessWidget {
color: Theme.of(context).colorScheme.onSecondary,
),
),
onSelected: (_) {},
onSelected: (_) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SimiliarBooksScreen(
tag: tag,
),
),
);
},
),
);
}
Expand All @@ -63,6 +73,17 @@ class BookTitleDetail extends StatelessWidget {
return chips;
}

void _navigateToSimiliarAuthorBooksScreen(BuildContext context) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SimiliarBooksScreen(
author: author,
),
),
);
}

@override
Widget build(BuildContext context) {
return Card(
Expand Down Expand Up @@ -99,13 +120,18 @@ class BookTitleDetail extends StatelessWidget {
: const SizedBox(),
const Divider(height: 5),
const SizedBox(height: 10),
Padding(
padding: const EdgeInsets.fromLTRB(5, 0, 5, 0),
child: SelectableText(
author,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
InkWell(
borderRadius: BorderRadius.circular(cornerRadius),
onTap: () => _navigateToSimiliarAuthorBooksScreen(context),
child: Padding(
padding: const EdgeInsets.fromLTRB(5, 5, 5, 5),
child: SelectableText(
author,
onTap: () => _navigateToSimiliarAuthorBooksScreen(context),
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
),
),
Expand Down
6 changes: 3 additions & 3 deletions lib/ui/books_screen/books_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -878,7 +878,7 @@ class _BooksScreenState extends State<BooksScreen>
),
listNumber: 2,
selectedBookIds: selectedBookIds,
onBookSelected: _onItemSelected,
onBookSelectedForMultiSelect: _onItemSelected,
allBooksCount: snapshot.data!.length,
);
} else {
Expand Down Expand Up @@ -945,7 +945,7 @@ class _BooksScreenState extends State<BooksScreen>
),
listNumber: 1,
selectedBookIds: selectedBookIds,
onBookSelected: _onItemSelected,
onBookSelectedForMultiSelect: _onItemSelected,
allBooksCount: snapshot.data!.length,
);
} else {
Expand Down Expand Up @@ -1012,7 +1012,7 @@ class _BooksScreenState extends State<BooksScreen>
),
listNumber: 0,
selectedBookIds: selectedBookIds,
onBookSelected: _onItemSelected,
onBookSelectedForMultiSelect: _onItemSelected,
allBooksCount: snapshot.data!.length,
);
} else {
Expand Down
12 changes: 6 additions & 6 deletions lib/ui/books_screen/widgets/books_grid.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ class BooksGrid extends StatefulWidget {
required this.books,
required this.listNumber,
this.selectedBookIds,
this.onBookSelected,
this.onBookSelectedForMultiSelect,
required this.allBooksCount,
});

final List<Book> books;
final int listNumber;
final Set<int>? selectedBookIds;
final Function(int id)? onBookSelected;
final Function(int id)? onBookSelectedForMultiSelect;
final int allBooksCount;

@override
Expand All @@ -29,8 +29,8 @@ class _BooksGridState extends State<BooksGrid>
with AutomaticKeepAliveClientMixin {
_onPressed(int index, bool multiSelectMode, String heroTag) {
if (widget.books[index].id == null) return;
if (multiSelectMode && widget.onBookSelected != null) {
widget.onBookSelected!(widget.books[index].id!);
if (multiSelectMode && widget.onBookSelectedForMultiSelect != null) {
widget.onBookSelectedForMultiSelect!(widget.books[index].id!);
return;
}

Expand All @@ -49,8 +49,8 @@ class _BooksGridState extends State<BooksGrid>

onLongPressed(int index) {
if (widget.books[index].id == null) return;
if (widget.onBookSelected != null) {
widget.onBookSelected!(widget.books[index].id!);
if (widget.onBookSelectedForMultiSelect != null) {
widget.onBookSelectedForMultiSelect!(widget.books[index].id!);
return;
}
}
Expand Down
2 changes: 1 addition & 1 deletion lib/ui/books_screen/widgets/books_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class BooksList extends StatefulWidget {
required this.listNumber,
this.selectedBookIds,
this.onBookSelected,
required this.allBooksCount,
this.allBooksCount,
});

final List<Book> books;
Expand Down
84 changes: 84 additions & 0 deletions lib/ui/similiar_books_screen/similiar_books_screen.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flex_color_picker/flex_color_picker.dart';
import 'package:flutter/material.dart';
import 'package:openreads/generated/locale_keys.g.dart';
import 'package:openreads/main.dart';
import 'package:openreads/model/book.dart';
import 'package:openreads/ui/books_screen/widgets/widgets.dart';

class SimiliarBooksScreen extends StatefulWidget {
const SimiliarBooksScreen({
super.key,
this.tag,
this.author,
});

final String? tag;
final String? author;

@override
State<SimiliarBooksScreen> createState() => _SimiliarBooksScreenState();
}

class _SimiliarBooksScreenState extends State<SimiliarBooksScreen> {
@override
void initState() {
if (widget.tag != null) {
bookCubit.getBooksWithSameTag(widget.tag!);
} else if (widget.author != null) {
bookCubit.getBooksWithSameAuthor(widget.author!);
}
super.initState();
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Row(
children: [
widget.tag != null
? Text(
'${LocaleKeys.tags.plural(1).capitalize.tr()}: ',
style: const TextStyle(fontSize: 18),
)
: widget.author != null
? Text(
'${LocaleKeys.author.tr()}: ',
style: const TextStyle(fontSize: 18),
)
: const SizedBox(),
Text(
widget.tag != null
? widget.tag!
: widget.author != null
? widget.author!
: '',
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
],
),
),
body: StreamBuilder(
stream: widget.tag != null
? bookCubit.booksWithSameTag
: bookCubit.booksWithSameAuthor,
builder: (context, AsyncSnapshot<List<Book>?> snapshot) {
if (snapshot.hasData) {
return BooksList(
books: snapshot.data!,
listNumber: 0,
);
} else if (snapshot.hasError) {
return Text(
snapshot.error.toString(),
);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
}),
);
}
}