Skip to content

Commit

Permalink
feat: Search foor covers online
Browse files Browse the repository at this point in the history
  • Loading branch information
mateusz-bak committed Apr 2, 2024
1 parent e3db154 commit dc22bdc
Show file tree
Hide file tree
Showing 10 changed files with 350 additions and 50 deletions.
4 changes: 3 additions & 1 deletion assets/translations/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -286,5 +286,7 @@
"default_books_format": "Default books format",
"cover_still_downloaded": "Cover is still being downloaded",
"wait_for_downloading_to_finish": "Wait for downloading to finish",
"save_without_cover": "Save without the cover"
"save_without_cover": "Save without the cover",
"search_online_for_cover": "Search online for the cover",
"book_cover": "book cover"
}
3 changes: 3 additions & 0 deletions lib/core/constants/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,7 @@ class Constants {
static const blurHashY = 2;

static const maxTagLength = 100;

static const duckDuckGoURL = 'https://duckduckgo.com/';
static const duckDuckGoImagesURL = 'https://duckduckgo.com/i.js';
}
47 changes: 47 additions & 0 deletions lib/core/helpers/helpers.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import 'dart:io';

import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:blurhash/blurhash.dart' as blurhash;
import 'package:image_cropper/image_cropper.dart';
import 'package:openreads/core/constants/constants.dart';
import 'package:openreads/generated/locale_keys.g.dart';

import 'package:openreads/logic/cubit/edit_book_cubit.dart';
import 'package:openreads/main.dart';
import 'package:openreads/model/book.dart';

Future generateBlurHash(Uint8List bytes, BuildContext context) async {
Expand Down Expand Up @@ -43,3 +49,44 @@ DateTime? getLatestStartDate(Book book) {
.map((e) => e.startDate)
.reduce((value, element) => value!.isAfter(element!) ? value : element);
}

Future<CroppedFile?> cropImage(BuildContext context, Uint8List cover) async {
final colorScheme = Theme.of(context).colorScheme;

//write temporary file as ImageCropper requires a file
final tmpCoverTimestamp = DateTime.now().millisecondsSinceEpoch;
final tmpCoverFile = File(
'${appTempDirectory.path}/$tmpCoverTimestamp.jpg',
);
await tmpCoverFile.writeAsBytes(cover);

return await ImageCropper().cropImage(
maxWidth: 1024,
maxHeight: 1024,
sourcePath: tmpCoverFile.path,
compressQuality: 90,
uiSettings: [
AndroidUiSettings(
toolbarTitle: LocaleKeys.edit_cover.tr(),
toolbarColor: Colors.black,
statusBarColor: Colors.black,
toolbarWidgetColor: Colors.white,
backgroundColor: colorScheme.surface,
cropGridColor: Colors.black87,
activeControlsWidgetColor: colorScheme.primary,
cropFrameColor: Colors.black87,
lockAspectRatio: false,
hideBottomControls: false,
),
IOSUiSettings(
title: LocaleKeys.edit_cover.tr(),
cancelButtonTitle: LocaleKeys.cancel.tr(),
doneButtonTitle: LocaleKeys.save.tr(),
rotateButtonsHidden: false,
rotateClockwiseButtonHidden: true,
aspectRatioPickerButtonHidden: false,
aspectRatioLockDimensionSwapEnabled: false,
),
],
);
}
2 changes: 2 additions & 0 deletions lib/generated/locale_keys.g.dart
Original file line number Diff line number Diff line change
Expand Up @@ -286,4 +286,6 @@ abstract class LocaleKeys {
static const coverStillDownloaded = 'cover_still_downloaded';
static const waitForDownloadingToFinish = 'wait_for_downloading_to_finish';
static const saveWithoutCover = 'save_without_cover';
static const searchOnlineForCover = 'search_online_for_cover';
static const bookCover = 'book_cover';
}
84 changes: 36 additions & 48 deletions lib/ui/add_book_screen/widgets/cover_view_edit.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import 'dart:io';
// ignore_for_file: use_build_context_synchronously

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

import 'package:image/image.dart' as img;
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:image_picker/image_picker.dart';
import 'package:image_cropper/image_cropper.dart';
import 'package:blurhash_dart/blurhash_dart.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
Expand All @@ -19,6 +19,7 @@ import 'package:openreads/generated/locale_keys.g.dart';
import 'package:openreads/logic/cubit/edit_book_cubit.dart';
import 'package:openreads/main.dart';
import 'package:openreads/ui/add_book_screen/widgets/widgets.dart';
import 'package:openreads/ui/search_covers_screen/search_covers_screen.dart';

class CoverViewEdit extends StatefulWidget {
const CoverViewEdit({super.key});
Expand Down Expand Up @@ -54,7 +55,10 @@ class _CoverViewEditState extends State<CoverViewEdit> {
return;
}

final croppedPhoto = await _cropImage(await photoXFile.readAsBytes());
final croppedPhoto = await cropImage(
context,
await photoXFile.readAsBytes(),
);

if (croppedPhoto == null) {
_setCoverLoading(false);
Expand All @@ -80,9 +84,15 @@ class _CoverViewEditState extends State<CoverViewEdit> {
_setCoverLoading(false);
}

void _editCurrentCover(BuildContext context) async {
void _editCurrentCover({
required BuildContext context,
bool pop = true,
}) async {
_setCoverLoading(true);
Navigator.of(context).pop();

if (pop) {
Navigator.of(context).pop();
}

final cover = context.read<EditBookCoverCubit>().state;

Expand All @@ -91,7 +101,7 @@ class _CoverViewEditState extends State<CoverViewEdit> {
return;
}

final croppedPhoto = await _cropImage(cover);
final croppedPhoto = await cropImage(context, cover);

if (croppedPhoto == null) {
_setCoverLoading(false);
Expand All @@ -118,47 +128,6 @@ class _CoverViewEditState extends State<CoverViewEdit> {
_setCoverLoading(false);
}

Future<CroppedFile?> _cropImage(Uint8List cover) async {
final colorScheme = Theme.of(context).colorScheme;

//write temporary file as ImageCropper requires a file
final tmpCoverTimestamp = DateTime.now().millisecondsSinceEpoch;
final tmpCoverFile = File(
'${appTempDirectory.path}/$tmpCoverTimestamp.jpg',
);
await tmpCoverFile.writeAsBytes(cover);

return await ImageCropper().cropImage(
maxWidth: 1024,
maxHeight: 1024,
sourcePath: tmpCoverFile.path,
compressQuality: 90,
uiSettings: [
AndroidUiSettings(
toolbarTitle: LocaleKeys.edit_cover.tr(),
toolbarColor: Colors.black,
statusBarColor: Colors.black,
toolbarWidgetColor: Colors.white,
backgroundColor: colorScheme.surface,
cropGridColor: Colors.black87,
activeControlsWidgetColor: colorScheme.primary,
cropFrameColor: Colors.black87,
lockAspectRatio: false,
hideBottomControls: false,
),
IOSUiSettings(
title: LocaleKeys.edit_cover.tr(),
cancelButtonTitle: LocaleKeys.cancel.tr(),
doneButtonTitle: LocaleKeys.save.tr(),
rotateButtonsHidden: false,
rotateClockwiseButtonHidden: true,
aspectRatioPickerButtonHidden: false,
aspectRatioLockDimensionSwapEnabled: false,
),
],
);
}

_deleteCover(BuildContext context) async {
_setCoverLoading(true);

Expand Down Expand Up @@ -205,6 +174,19 @@ class _CoverViewEditState extends State<CoverViewEdit> {
_setCoverLoading(false);
}

_searchForCoverOnline(BuildContext context) async {
Navigator.of(context).pop();

Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SearchCoversScreen(
book: context.read<EditBookCubit>().state,
),
),
);
}

showCoverLoadBottomSheet(BuildContext context) {
FocusManager.instance.primaryFocus?.unfocus();

Expand Down Expand Up @@ -238,6 +220,12 @@ class _CoverViewEditState extends State<CoverViewEdit> {
onPressed: () => _loadCoverFromStorage(context),
),
const SizedBox(height: 15),
CoverOptionButton(
text: LocaleKeys.searchOnlineForCover.tr(),
icon: FontAwesomeIcons.magnifyingGlass,
onPressed: () => _searchForCoverOnline(context),
),
const SizedBox(height: 15),
CoverOptionButton(
text: LocaleKeys.get_cover_from_open_library.tr(),
icon: FontAwesomeIcons.globe,
Expand All @@ -253,7 +241,7 @@ class _CoverViewEditState extends State<CoverViewEdit> {
text: LocaleKeys.edit_current_cover.tr(),
icon: FontAwesomeIcons.image,
onPressed: () => _editCurrentCover(
context,
context: context,
),
),
],
Expand Down
79 changes: 79 additions & 0 deletions lib/ui/search_covers_screen/search_covers_screen.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
import 'package:openreads/generated/locale_keys.g.dart';
import 'package:openreads/model/book.dart';
import 'package:openreads/ui/add_book_screen/widgets/widgets.dart';
import 'package:openreads/ui/search_covers_screen/widgets/widgets.dart';

class SearchCoversScreen extends StatefulWidget {
const SearchCoversScreen({
super.key,
required this.book,
});

final Book book;

@override
State<SearchCoversScreen> createState() => _SearchCoversScreenState();
}

class _SearchCoversScreenState extends State<SearchCoversScreen> {
final PagingController<int, String> _pagingController =
PagingController(firstPageKey: 0);

late TextEditingController controller;
String searchQuery = '';

@override
initState() {
super.initState();

searchQuery =
'${widget.book.title} ${widget.book.author} ${LocaleKeys.bookCover.tr()}';

controller = TextEditingController(text: searchQuery);

controller.addListener(() {
setState(() {
if (searchQuery == controller.text) return;

searchQuery = controller.text;
});
});
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
LocaleKeys.searchOnlineForCover.tr(),
style: const TextStyle(fontSize: 18),
),
),
body: Column(
children: [
Padding(
padding: const EdgeInsets.only(bottom: 10),
child: BookTextField(
controller: controller,
keyboardType: TextInputType.text,
maxLength: 256,
onSubmitted: (_) {
setState(() {
searchQuery = controller.text;
_pagingController.refresh();
});
},
),
),
SearchCoversGrid(
pagingController: _pagingController,
query: searchQuery,
),
],
),
);
}
}
Loading

0 comments on commit dc22bdc

Please sign in to comment.