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

487 feature request suggest author when adding book #527

Merged
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
2 changes: 1 addition & 1 deletion ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ SPEC CHECKSUMS:
SwiftProtobuf: bcfd2bc231cf9ae552cdc7c4e877bd3b41fe57b1
SwiftyGif: 93a1cc87bf3a51916001cf8f3d63835fb64c819f
TOCropViewController: edfd4f25713d56905ad1e0b9f5be3fbe0f59c863
url_launcher_ios: bbd758c6e7f9fd7b5b1d4cde34d2b95fcce5e812
url_launcher_ios: 6116280ddcfe98ab8820085d8d76ae7449447586

PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796

Expand Down
2 changes: 1 addition & 1 deletion lib/core/helpers/backup/backup_export.dart
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ class BackupExport {

static Future<String?> prepareTemporaryBackup(BuildContext context) async {
try {
await bookCubit.getAllBooks(tags: true);
await bookCubit.getAllBooks(getTags: false, getAuthors: false);

final books = await bookCubit.allBooks.first;
final listOfBookJSONs = List<String>.empty(growable: true);
Expand Down
2 changes: 1 addition & 1 deletion lib/core/helpers/backup/csv_export.dart
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ class CSVExport {

static Future<String?> _prepareCSVExport() async {
try {
await bookCubit.getAllBooks(tags: true);
await bookCubit.getAllBooks(getAuthors: false, getTags: false);

final books = await bookCubit.allBooks.first;
final rows = List<List<String>>.empty(growable: true);
Expand Down
32 changes: 29 additions & 3 deletions lib/logic/cubit/book_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ class BookCubit extends Cubit {
BehaviorSubject<List<int>>();
final BehaviorSubject<List<String>> _tagsFetcher =
BehaviorSubject<List<String>>();
final BehaviorSubject<List<String>> _authorsFetcher =
BehaviorSubject<List<String>>();
final BehaviorSubject<Book?> _bookFetcher = BehaviorSubject<Book?>();
final BehaviorSubject<List<Book>?> _booksWithSameTagFetcher =
BehaviorSubject<List<Book>?>();
Expand All @@ -53,6 +55,7 @@ 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<String>> get authors => _authorsFetcher.stream;
Stream<List<Book>?> get booksWithSameTag => _booksWithSameTagFetcher.stream;
Stream<List<Book>?> get booksWithSameAuthor =>
_booksWithSameAuthorFetcher.stream;
Expand All @@ -74,11 +77,20 @@ class BookCubit extends Cubit {
getAllBooks();
}

getAllBooks({bool tags = false}) async {
getAllBooks({
bool getTags = true,
bool getAuthors = true,
}) async {
List<Book> books = await repository.getAllNotDeletedBooks();
_booksFetcher.sink.add(books);
if (tags) return;
_tagsFetcher.sink.add(_getTags(books));

if (getTags) {
_tagsFetcher.sink.add(_getTags(books));
}

if (getAuthors) {
_authorsFetcher.sink.add(_getAuthors(books));
}
}

removeAllBooks() async {
Expand Down Expand Up @@ -237,6 +249,20 @@ class BookCubit extends Cubit {
return tags;
}

List<String> _getAuthors(List<Book> books) {
final authors = List<String>.empty(growable: true);

for (var book in books) {
if (!authors.contains(book.author)) {
authors.add(book.author);
}
}

authors.sort((a, b) => a.compareTo(b));

return authors;
}

Future<bool> _checkIfCoverMigrationDone() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
final bool? check = prefs.getBool('is_cover_migration_done');
Expand Down
23 changes: 14 additions & 9 deletions lib/ui/add_book_screen/add_book_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -446,15 +446,20 @@ class _AddBookScreenState extends State<AddBookScreen> {
textCapitalization: TextCapitalization.sentences,
),
const SizedBox(height: 10),
BookTextField(
controller: _authorCtrl,
hint: LocaleKeys.enter_author.tr(),
icon: Icons.person,
keyboardType: TextInputType.name,
maxLines: 5,
maxLength: 255,
textCapitalization: TextCapitalization.words,
),
StreamBuilder<List<String>>(
stream: bookCubit.authors,
builder: (context, AsyncSnapshot<List<String>?> snapshot) {
return BookTextField(
controller: _authorCtrl,
hint: LocaleKeys.enter_author.tr(),
icon: Icons.person,
keyboardType: TextInputType.name,
maxLines: 5,
maxLength: 255,
textCapitalization: TextCapitalization.words,
suggestions: snapshot.data,
);
}),
const Padding(
padding: EdgeInsets.all(10),
child: Divider(),
Expand Down
125 changes: 86 additions & 39 deletions lib/ui/add_book_screen/widgets/book_text_field.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_typeahead/flutter_typeahead.dart';

import 'package:openreads/core/themes/app_theme.dart';

Expand All @@ -19,6 +20,7 @@ class BookTextField extends StatefulWidget {
this.textCapitalization = TextCapitalization.none,
this.padding = const EdgeInsets.symmetric(horizontal: 10),
this.onSubmitted,
this.suggestions,
});

final TextEditingController controller;
Expand All @@ -34,13 +36,13 @@ class BookTextField extends StatefulWidget {
final TextCapitalization textCapitalization;
final Function(String)? onSubmitted;
final EdgeInsets padding;
final List<String>? suggestions;

@override
State<BookTextField> createState() => _BookTextFieldState();
}

class _BookTextFieldState extends State<BookTextField> {
final FocusNode focusNode = FocusNode();
bool showClearButton = false;

@override
Expand Down Expand Up @@ -70,46 +72,91 @@ class _BookTextFieldState extends State<BookTextField> {
border: Border.all(color: dividerColor),
),
child: Scrollbar(
child: TextField(
autofocus: widget.autofocus,
keyboardType: widget.keyboardType,
inputFormatters: widget.inputFormatters,
textCapitalization: widget.textCapitalization,
controller: widget.controller,
focusNode: focusNode,
minLines: 1,
maxLines: widget.maxLines,
maxLength: widget.maxLength,
textInputAction: widget.textInputAction,
style: const TextStyle(fontSize: 14),
onSubmitted: widget.onSubmitted ?? (_) {},
decoration: InputDecoration(
labelText: widget.hint,
labelStyle: const TextStyle(fontSize: 14),
icon: (widget.icon != null)
? Icon(
widget.icon,
color: Theme.of(context).colorScheme.primary,
)
: null,
border: InputBorder.none,
counterText: widget.hideCounter ? "" : null,
suffixIcon: showClearButton
? IconButton(
onPressed: () {
widget.controller.clear();
setState(() {
showClearButton = false;
});
focusNode.requestFocus();
},
icon: const Icon(Icons.clear),
)
: null,
),
),
child: widget.suggestions != null && widget.suggestions!.isNotEmpty
? _buildTypeAheadField()
: _buildTextField(context),
),
),
);
}

TypeAheadField<String> _buildTypeAheadField() {
return TypeAheadField(
controller: widget.controller,
hideOnLoading: true,
hideOnEmpty: true,
itemBuilder: (context, suggestion) {
return Container(
color: Theme.of(context).colorScheme.surfaceVariant,
child: ListTile(
title: Text(suggestion),
),
);
},
decorationBuilder: (context, child) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 5),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(cornerRadius),
border: Border.all(color: dividerColor),
),
child: child,
);
},
suggestionsCallback: (pattern) {
return widget.suggestions!.where((String option) {
return option.toLowerCase().startsWith(pattern.toLowerCase());
}).toList();
},
onSelected: (suggestion) {
widget.controller.text = suggestion;
},
builder: (_, __, focusNode) {
return _buildTextField(context, focusNode: focusNode);
});
}

TextField _buildTextField(
BuildContext context, {
FocusNode? focusNode,
}) {
return TextField(
autofocus: widget.autofocus,
keyboardType: widget.keyboardType,
inputFormatters: widget.inputFormatters,
textCapitalization: widget.textCapitalization,
controller: widget.controller,
focusNode: focusNode,
minLines: 1,
maxLines: widget.maxLines,
maxLength: widget.maxLength,
textInputAction: widget.textInputAction,
style: const TextStyle(fontSize: 14),
onSubmitted: widget.onSubmitted ?? (_) {},
decoration: InputDecoration(
labelText: widget.hint,
labelStyle: const TextStyle(fontSize: 14),
icon: (widget.icon != null)
? Icon(
widget.icon,
color: Theme.of(context).colorScheme.primary,
)
: null,
border: InputBorder.none,
counterText: widget.hideCounter ? "" : null,
suffixIcon: showClearButton
? IconButton(
onPressed: () {
widget.controller.clear();
setState(() {
showClearButton = false;
});
focusNode?.requestFocus();
},
icon: const Icon(Icons.clear),
)
: null,
),
);
}
}
Loading
Loading