mirror of
https://github.com/dstark5/Openlib.git
synced 2025-09-19 22:31:18 +08:00
Compare commits
4 Commits
v1.0.3-bet
...
v1.0.4-bet
Author | SHA1 | Date | |
---|---|---|---|
74793e5d65 | |||
88c1612184 | |||
2924020eb7 | |||
3bda1a7284 |
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#### An Open source app to download and read books from shadow library ([Anna’s Archive](https://annas-archive.org/)).
|
#### An Open source app to download and read books from shadow library ([Anna’s Archive](https://annas-archive.org/)).
|
||||||
|
|
||||||
[](https://flutter.dev/) [](https://opensource.org/licenses/) 
|
[](https://flutter.dev/) [](https://opensource.org/licenses/) [](https://github.com/dstark5/Openlib/releases)
|
||||||
|
|
||||||
[<img src="github_releases.png"
|
[<img src="github_releases.png"
|
||||||
alt="Download from GitHub"
|
alt="Download from GitHub"
|
||||||
|
@ -24,7 +24,6 @@
|
|||||||
android:label="Openlib"
|
android:label="Openlib"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:requestLegacyExternalStorage="true"
|
android:requestLegacyExternalStorage="true"
|
||||||
android:usesCleartextTraffic="true"
|
|
||||||
android:networkSecurityConfig="@xml/network_security_config"
|
android:networkSecurityConfig="@xml/network_security_config"
|
||||||
android:icon="@mipmap/launcher_icon">
|
android:icon="@mipmap/launcher_icon">
|
||||||
|
|
||||||
|
32
android/app/src/main/res/raw/annas_archive
Normal file
32
android/app/src/main/res/raw/annas_archive
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIFfTCCBGWgAwIBAgIRAI0YQRVveHeTEYLPgc2CDXowDQYJKoZIhvcNAQELBQAw
|
||||||
|
RjELMAkGA1UEBhMCVVMxIjAgBgNVBAoTGUdvb2dsZSBUcnVzdCBTZXJ2aWNlcyBM
|
||||||
|
TEMxEzARBgNVBAMTCkdUUyBDQSAxUDUwHhcNMjMxMDMwMjMwOTExWhcNMjQwMTI4
|
||||||
|
MjMwOTEwWjAcMRowGAYDVQQDExFhbm5hcy1hcmNoaXZlLm9yZzCCASIwDQYJKoZI
|
||||||
|
hvcNAQEBBQADggEPADCCAQoCggEBAKl79Oj7GW10c1ikBC/sSkyr2tmmIl1/iszu
|
||||||
|
9DN5w3lbX3q5hLeSB2xPaW6FWPB0u+YR8tgXyk6jG8jkzSe2cdkW0pjKaxSNiQgW
|
||||||
|
rXipoonUbaceSDd3aFQFGhqe0urkM+84Sgspy39REdxXuQeL2hXH8fouRPA65/pn
|
||||||
|
2nipwkpZHOezEQfE7BbUYwt4/YQaWXD3ScBLNx0PJuZdL4sfVC41IP8Ml/i5zzU7
|
||||||
|
mupl6EGgw5IuXwyHN1AC1NHQBU5/8X062/NdVhW/letbsR52Z6DJ727+nWLpdwq4
|
||||||
|
WgowHui6PthI6h+F4LCu+g3V5akAibsNffqVyWssxaMWFXjMuDECAwEAAaOCAo4w
|
||||||
|
ggKKMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMB
|
||||||
|
Af8EAjAAMB0GA1UdDgQWBBSIWJPHr1GutuUbnUApJ/vU3TQP9DAfBgNVHSMEGDAW
|
||||||
|
gBTV/J4N3x7K3QiXl24rxV/FK/XsuDB4BggrBgEFBQcBAQRsMGowNQYIKwYBBQUH
|
||||||
|
MAGGKWh0dHA6Ly9vY3NwLnBraS5nb29nL3MvZ3RzMXA1L1VfN25sRDJPX1JRMDEG
|
||||||
|
CCsGAQUFBzAChiVodHRwOi8vcGtpLmdvb2cvcmVwby9jZXJ0cy9ndHMxcDUuZGVy
|
||||||
|
MDEGA1UdEQQqMCiCEWFubmFzLWFyY2hpdmUub3JnghMqLmFubmFzLWFyY2hpdmUu
|
||||||
|
b3JnMCEGA1UdIAQaMBgwCAYGZ4EMAQIBMAwGCisGAQQB1nkCBQMwPAYDVR0fBDUw
|
||||||
|
MzAxoC+gLYYraHR0cDovL2NybHMucGtpLmdvb2cvZ3RzMXA1L19ZUS1xNlF1bEJB
|
||||||
|
LmNybDCCAQUGCisGAQQB1nkCBAIEgfYEgfMA8QB2AHb/iD8KtvuVUcJhzPWHujS0
|
||||||
|
pM27KdxoQgqf5mdMWjp0AAABi4MQGPAAAAQDAEcwRQIhALVAeepoK0WtUPs01yzZ
|
||||||
|
XjAkdE7WYGw2QNzSkvywFRoIAiAnp1Lec4PqwGPbg/ppC8PgGxb8+yvSlVT0ChUt
|
||||||
|
NMinWgB3ANq2v2s/tbYin5vCu1xr6HCRcWy7UYSFNL2kPTBI1/urAAABi4MQGP0A
|
||||||
|
AAQDAEgwRgIhAKbAEYr9qDmiafcnkXIG7xObbI4fz3IsLSht8etA/jSlAiEAkQ7G
|
||||||
|
t6x81Onpl4RcTtrXLptI0fDkrAZ66hAPWbIH8SAwDQYJKoZIhvcNAQELBQADggEB
|
||||||
|
AGfvZYtIOPKRvVyfI4tJpetCJmU/DEMbCIyX05M+2P1n2uY1D4tweAMmYf4trh5Z
|
||||||
|
cuTK3QDeoICus3WK08L3Ni/699QbQ0uonp0IIIOi2NlP2rLhvtETWpmLoX6jM55W
|
||||||
|
cYGiQldhCgWYEXoANrJshUbjkyM81QMNzSrn33JPkzWUdgVoS/KfABaeymLekXO4
|
||||||
|
ndLp4ktLlYQZr3JJU39FvwgN8IcmeLWUnpSWsekH+nHSW9e8vOsNQoZyHw0minqz
|
||||||
|
ZzFbS10reX1kG56+AxDf5fOOM+C+MAozSUnXUjrkpXXakwUooMTklKtYbBiwR2R0
|
||||||
|
wrcYmVHymn07AUliDOalu2I=
|
||||||
|
-----END CERTIFICATE-----
|
17
android/app/src/main/res/xml/network_security_config.xml
Normal file
17
android/app/src/main/res/xml/network_security_config.xml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<network-security-config>
|
||||||
|
<base-config cleartextTrafficPermitted="false">
|
||||||
|
<trust-anchors>
|
||||||
|
<certificates src="@raw/annas_archive"/>
|
||||||
|
<certificates src="system"/>
|
||||||
|
</trust-anchors>
|
||||||
|
</base-config>
|
||||||
|
|
||||||
|
<domain-config cleartextTrafficPermitted="true">
|
||||||
|
<domain includeSubdomains="true">secure.example.com</domain>
|
||||||
|
<domain includeSubdomains="true">127.0.0.1</domain>
|
||||||
|
</domain-config>
|
||||||
|
<domain-config cleartextTrafficPermitted="false">
|
||||||
|
<domain includeSubdomains="true">annas-archive.org</domain>
|
||||||
|
</domain-config>
|
||||||
|
</network-security-config>
|
@ -66,8 +66,7 @@ class AnnasArchieve {
|
|||||||
List<BookData> _parser(resData, String fileType) {
|
List<BookData> _parser(resData, String fileType) {
|
||||||
var document =
|
var document =
|
||||||
parse(resData.toString().replaceAll(RegExp(r"<!--|-->"), ''));
|
parse(resData.toString().replaceAll(RegExp(r"<!--|-->"), ''));
|
||||||
var books = document.querySelectorAll(
|
var books = document.querySelectorAll('a');
|
||||||
'a[class="js-vim-focus custom-a flex items-center relative left-[-10px] w-[calc(100%+20px)] px-[10px] outline-offset-[-2px] outline-2 rounded-[3px] hover:bg-[#00000011] focus:outline "]');
|
|
||||||
|
|
||||||
List<BookData> bookList = [];
|
List<BookData> bookList = [];
|
||||||
|
|
||||||
@ -246,18 +245,39 @@ class AnnasArchieve {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String urlEncoder(
|
||||||
|
{required String searchQuery,
|
||||||
|
required String content,
|
||||||
|
required String sort,
|
||||||
|
required String fileType,
|
||||||
|
required bool enableFilters}) {
|
||||||
|
searchQuery = searchQuery.replaceAll(" ", "+");
|
||||||
|
if (enableFilters == false) return '$baseUrl/search?q=$searchQuery';
|
||||||
|
if (content == "" && sort == "" && fileType == "") {
|
||||||
|
return '$baseUrl/search?q=$searchQuery';
|
||||||
|
}
|
||||||
|
return '$baseUrl/search?index=&q=$searchQuery&content=$content&ext=$fileType&sort=$sort';
|
||||||
|
}
|
||||||
|
|
||||||
Future<List<BookData>> searchBooks(
|
Future<List<BookData>> searchBooks(
|
||||||
{required String searchQuery,
|
{required String searchQuery,
|
||||||
String content = "",
|
String content = "",
|
||||||
String sort = "",
|
String sort = "",
|
||||||
String fileType = ""}) async {
|
String fileType = "",
|
||||||
|
bool enableFilters = true}) async {
|
||||||
try {
|
try {
|
||||||
final String encodedURL = content == ""
|
final String encodedURL = urlEncoder(
|
||||||
? '$baseUrl/search?index=&q=$searchQuery&ext=$fileType&sort=$sort'
|
searchQuery: searchQuery,
|
||||||
: '$baseUrl/search?index=&q=$searchQuery&content=$content&ext=$fileType&sort=$sort';
|
content: content,
|
||||||
|
sort: sort,
|
||||||
|
fileType: fileType,
|
||||||
|
enableFilters: enableFilters);
|
||||||
|
|
||||||
final response = await dio.get(encodedURL,
|
final response = await dio.get(encodedURL,
|
||||||
options: Options(headers: defaultDioHeaders));
|
options: Options(headers: defaultDioHeaders));
|
||||||
|
if (!enableFilters) {
|
||||||
|
return _parser(response.data, "");
|
||||||
|
}
|
||||||
return _parser(response.data, fileType);
|
return _parser(response.data, fileType);
|
||||||
} on DioException catch (e) {
|
} on DioException catch (e) {
|
||||||
if (e.type == DioExceptionType.unknown) {
|
if (e.type == DioExceptionType.unknown) {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:chunked_downloader/chunked_downloader.dart';
|
||||||
import 'files.dart';
|
import 'files.dart';
|
||||||
|
|
||||||
Future<String> _getFilePath(String fileName) async {
|
Future<String> _getFilePath(String fileName) async {
|
||||||
@ -60,31 +61,21 @@ Future<void> downloadFile(
|
|||||||
|
|
||||||
if (workingMirror != null) {
|
if (workingMirror != null) {
|
||||||
try {
|
try {
|
||||||
CancelToken cancelToken = CancelToken();
|
var chunkedDownloader = await ChunkedDownloader(
|
||||||
|
url: workingMirror,
|
||||||
dio.download(
|
saveFilePath: path,
|
||||||
workingMirror,
|
chunkSize: 32 * 1024,
|
||||||
path,
|
onError: (error) {
|
||||||
options: Options(headers: {
|
onDownlaodFailed();
|
||||||
'Connection': 'Keep-Alive',
|
},
|
||||||
'User-Agent':
|
onProgress: (received, total, speed) {
|
||||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'
|
onProgress(received, total);
|
||||||
}),
|
},
|
||||||
onReceiveProgress: (rcv, total) {
|
onDone: (file) {})
|
||||||
onProgress(rcv, total);
|
.start();
|
||||||
},
|
|
||||||
deleteOnError: true,
|
|
||||||
cancelToken: cancelToken,
|
|
||||||
).catchError((err) {
|
|
||||||
if (err.type != DioExceptionType.cancel) {
|
|
||||||
onDownlaodFailed();
|
|
||||||
}
|
|
||||||
throw err;
|
|
||||||
});
|
|
||||||
|
|
||||||
mirrorStatus(true);
|
mirrorStatus(true);
|
||||||
|
cancelDownlaod(chunkedDownloader);
|
||||||
cancelDownlaod(cancelToken);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
onDownlaodFailed();
|
onDownlaodFailed();
|
||||||
}
|
}
|
||||||
|
@ -52,3 +52,97 @@ class OpenLibrary {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class GoodReads {
|
||||||
|
String url = "https://www.goodreads.com/shelf/show/trending";
|
||||||
|
|
||||||
|
List<TrendingBookData> _parser(data) {
|
||||||
|
var document = parse(data.toString());
|
||||||
|
var bookList = document.querySelectorAll('div[class="elementList"]');
|
||||||
|
List<TrendingBookData> trendingBooks = [];
|
||||||
|
for (var element in bookList) {
|
||||||
|
if (element
|
||||||
|
.querySelector('a[class="leftAlignedImage"]')
|
||||||
|
?.attributes['title'] !=
|
||||||
|
null &&
|
||||||
|
element.querySelector('img')?.attributes['src'] != null) {
|
||||||
|
String? thumbnail = element.querySelector('img')?.attributes['src'];
|
||||||
|
trendingBooks.add(
|
||||||
|
TrendingBookData(
|
||||||
|
title: element
|
||||||
|
.querySelector('a[class="leftAlignedImage"]')
|
||||||
|
?.attributes['title']
|
||||||
|
.toString()
|
||||||
|
.trim(),
|
||||||
|
thumbnail:
|
||||||
|
thumbnail.toString().replaceAll("._SY75_.", "._SY225_.")),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return trendingBooks;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<TrendingBookData>> trendingBooks() async {
|
||||||
|
try {
|
||||||
|
final dio = Dio();
|
||||||
|
final response = await dio.get(url,
|
||||||
|
options: Options(
|
||||||
|
sendTimeout: const Duration(seconds: 20),
|
||||||
|
receiveTimeout: const Duration(seconds: 20)));
|
||||||
|
return _parser(response.data.toString());
|
||||||
|
} on DioException catch (e) {
|
||||||
|
if (e.type == DioExceptionType.unknown) {
|
||||||
|
throw "socketException";
|
||||||
|
}
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PenguinRandomHouse {
|
||||||
|
String url =
|
||||||
|
"https://www.penguinrandomhouse.com/ajaxc/categories/books/?from=0&to=50&contentId=&elClass=book&dataType=html&catFilter=best-sellers";
|
||||||
|
|
||||||
|
List<TrendingBookData> _parser(data) {
|
||||||
|
var document = parse(data.toString());
|
||||||
|
var bookList = document.querySelectorAll('div[class="book"]');
|
||||||
|
List<TrendingBookData> trendingBooks = [];
|
||||||
|
for (var element in bookList) {
|
||||||
|
if (element.querySelector('div[class="title"]')?.text != null &&
|
||||||
|
element
|
||||||
|
.querySelector('img[class="responsive_img"]')
|
||||||
|
?.attributes['src'] !=
|
||||||
|
null) {
|
||||||
|
String? thumbnail = element
|
||||||
|
.querySelector('img[class="responsive_img"]')
|
||||||
|
?.attributes['src'];
|
||||||
|
trendingBooks.add(
|
||||||
|
TrendingBookData(
|
||||||
|
title: element
|
||||||
|
.querySelector('div[class="title"]')
|
||||||
|
?.text
|
||||||
|
.toString()
|
||||||
|
.trim(),
|
||||||
|
thumbnail: thumbnail.toString()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return trendingBooks;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<TrendingBookData>> trendingBooks() async {
|
||||||
|
try {
|
||||||
|
final dio = Dio();
|
||||||
|
final response = await dio.get(url,
|
||||||
|
options: Options(
|
||||||
|
sendTimeout: const Duration(seconds: 20),
|
||||||
|
receiveTimeout: const Duration(seconds: 20)));
|
||||||
|
return _parser(response.data.toString());
|
||||||
|
} on DioException catch (e) {
|
||||||
|
if (e.type == DioExceptionType.unknown) {
|
||||||
|
throw "socketException";
|
||||||
|
}
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,7 +2,7 @@ import 'dart:math';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:openlib/services/database.dart';
|
import 'package:openlib/services/database.dart';
|
||||||
import 'package:dio/dio.dart';
|
import 'package:chunked_downloader/chunked_downloader.dart';
|
||||||
|
|
||||||
import 'package:openlib/services/open_library.dart';
|
import 'package:openlib/services/open_library.dart';
|
||||||
import 'package:openlib/services/annas_archieve.dart';
|
import 'package:openlib/services/annas_archieve.dart';
|
||||||
@ -65,9 +65,22 @@ final searchQueryProvider = StateProvider<String>((ref) => "");
|
|||||||
|
|
||||||
final getTrendingBooks = FutureProvider<List<TrendingBookData>>((ref) async {
|
final getTrendingBooks = FutureProvider<List<TrendingBookData>>((ref) async {
|
||||||
OpenLibrary openLibrary = OpenLibrary();
|
OpenLibrary openLibrary = OpenLibrary();
|
||||||
return await openLibrary.trendingBooks();
|
GoodReads goodReads = GoodReads();
|
||||||
|
PenguinRandomHouse penguinTrending = PenguinRandomHouse();
|
||||||
|
List<TrendingBookData> trendingBooks =
|
||||||
|
await Future.wait<List<TrendingBookData>>([
|
||||||
|
openLibrary.trendingBooks(),
|
||||||
|
goodReads.trendingBooks(),
|
||||||
|
penguinTrending.trendingBooks()
|
||||||
|
]).then((List<List<TrendingBookData>> listOfData) =>
|
||||||
|
listOfData.expand((element) => element).toList());
|
||||||
|
|
||||||
|
trendingBooks.shuffle();
|
||||||
|
return trendingBooks;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final enableFiltersState = StateProvider<bool>((ref) => true);
|
||||||
|
|
||||||
//Provider for Trending Books
|
//Provider for Trending Books
|
||||||
final searchProvider = FutureProvider.family
|
final searchProvider = FutureProvider.family
|
||||||
.autoDispose<List<BookData>, String>((ref, searchQuery) async {
|
.autoDispose<List<BookData>, String>((ref, searchQuery) async {
|
||||||
@ -76,7 +89,8 @@ final searchProvider = FutureProvider.family
|
|||||||
searchQuery: searchQuery,
|
searchQuery: searchQuery,
|
||||||
content: ref.watch(getTypeValue),
|
content: ref.watch(getTypeValue),
|
||||||
sort: ref.watch(getSortValue),
|
sort: ref.watch(getSortValue),
|
||||||
fileType: ref.watch(getFileTypeValue));
|
fileType: ref.watch(getFileTypeValue),
|
||||||
|
enableFilters: ref.watch(enableFiltersState));
|
||||||
return data;
|
return data;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -118,8 +132,8 @@ final getDownloadedFileSize = StateProvider.autoDispose<String>((ref) {
|
|||||||
return bytesToFileSize(ref.watch(downloadedFileSizeInBytes));
|
return bytesToFileSize(ref.watch(downloadedFileSizeInBytes));
|
||||||
});
|
});
|
||||||
|
|
||||||
final cancelCurrentDownload = StateProvider<CancelToken>((ref) {
|
final cancelCurrentDownload = StateProvider<ChunkedDownloader>((ref) {
|
||||||
return CancelToken();
|
return ChunkedDownloader(saveFilePath: "", url: "");
|
||||||
});
|
});
|
||||||
|
|
||||||
final dbProvider = Provider<MyLibraryDb>((ref) => throw UnimplementedError());
|
final dbProvider = Provider<MyLibraryDb>((ref) => throw UnimplementedError());
|
||||||
|
@ -43,7 +43,7 @@ class AboutPage extends StatelessWidget {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(left: 7, right: 7, top: 5),
|
padding: EdgeInsets.only(left: 7, right: 7, top: 5),
|
||||||
child: Text(
|
child: Text(
|
||||||
"1.0.3",
|
"1.0.4",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import 'package:dio/dio.dart';
|
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/scheduler.dart';
|
import 'package:flutter/scheduler.dart';
|
||||||
@ -21,6 +20,9 @@ import 'package:openlib/state/state.dart'
|
|||||||
dbProvider,
|
dbProvider,
|
||||||
checkIdExists,
|
checkIdExists,
|
||||||
myLibraryProvider;
|
myLibraryProvider;
|
||||||
|
|
||||||
|
import 'package:chunked_downloader/chunked_downloader.dart';
|
||||||
|
|
||||||
import 'package:openlib/ui/components/book_info_widget.dart';
|
import 'package:openlib/ui/components/book_info_widget.dart';
|
||||||
import 'package:openlib/ui/components/file_buttons_widget.dart';
|
import 'package:openlib/ui/components/file_buttons_widget.dart';
|
||||||
import 'package:openlib/ui/components/snack_bar_widget.dart';
|
import 'package:openlib/ui/components/snack_bar_widget.dart';
|
||||||
@ -141,6 +143,7 @@ class BookInfoPage extends ConsumerWidget {
|
|||||||
height: 25,
|
height: 25,
|
||||||
child: CircularProgressIndicator(
|
child: CircularProgressIndicator(
|
||||||
color: Theme.of(context).colorScheme.secondary,
|
color: Theme.of(context).colorScheme.secondary,
|
||||||
|
strokeCap: StrokeCap.round,
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
@ -203,6 +206,7 @@ class _ActionButtonWidgetState extends ConsumerState<ActionButtonWidget> {
|
|||||||
loading: () {
|
loading: () {
|
||||||
return CircularProgressIndicator(
|
return CircularProgressIndicator(
|
||||||
color: Theme.of(context).colorScheme.secondary,
|
color: Theme.of(context).colorScheme.secondary,
|
||||||
|
strokeCap: StrokeCap.round,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -241,7 +245,7 @@ Future<void> downloadFileWidget(
|
|||||||
showSnackBar(context: context, message: 'Book has been downloaded!');
|
showSnackBar(context: context, message: 'Book has been downloaded!');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
cancelDownlaod: (CancelToken downloadToken) {
|
cancelDownlaod: (ChunkedDownloader downloadToken) {
|
||||||
ref.read(cancelCurrentDownload.notifier).state = downloadToken;
|
ref.read(cancelCurrentDownload.notifier).state = downloadToken;
|
||||||
},
|
},
|
||||||
mirrorStatus: (val) {
|
mirrorStatus: (val) {
|
||||||
@ -341,6 +345,7 @@ class _ShowDialog extends ConsumerWidget {
|
|||||||
color:
|
color:
|
||||||
Theme.of(context).colorScheme.secondary,
|
Theme.of(context).colorScheme.secondary,
|
||||||
strokeWidth: 2.5,
|
strokeWidth: 2.5,
|
||||||
|
strokeCap: StrokeCap.round,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
@ -412,7 +417,7 @@ class _ShowDialog extends ConsumerWidget {
|
|||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
)),
|
)),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
ref.read(cancelCurrentDownload).cancel();
|
ref.read(cancelCurrentDownload).stop();
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
child: const Padding(
|
child: const Padding(
|
||||||
|
@ -10,10 +10,7 @@ import 'package:openlib/ui/components/book_card_widget.dart';
|
|||||||
import 'package:openlib/state/state.dart' show searchProvider;
|
import 'package:openlib/state/state.dart' show searchProvider;
|
||||||
|
|
||||||
class ResultPage extends ConsumerWidget {
|
class ResultPage extends ConsumerWidget {
|
||||||
const ResultPage({
|
const ResultPage({super.key, required this.searchQuery});
|
||||||
super.key,
|
|
||||||
required this.searchQuery,
|
|
||||||
});
|
|
||||||
|
|
||||||
final String searchQuery;
|
final String searchQuery;
|
||||||
|
|
||||||
@ -119,6 +116,7 @@ class ResultPage extends ConsumerWidget {
|
|||||||
height: 25,
|
height: 25,
|
||||||
child: CircularProgressIndicator(
|
child: CircularProgressIndicator(
|
||||||
color: Theme.of(context).colorScheme.secondary,
|
color: Theme.of(context).colorScheme.secondary,
|
||||||
|
strokeCap: StrokeCap.round,
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
],
|
],
|
||||||
|
@ -10,7 +10,8 @@ import 'package:openlib/state/state.dart'
|
|||||||
selectedFileTypeState,
|
selectedFileTypeState,
|
||||||
typeValues,
|
typeValues,
|
||||||
fileType,
|
fileType,
|
||||||
sortValues;
|
sortValues,
|
||||||
|
enableFiltersState;
|
||||||
import 'components/snack_bar_widget.dart';
|
import 'components/snack_bar_widget.dart';
|
||||||
|
|
||||||
class SearchPage extends ConsumerWidget {
|
class SearchPage extends ConsumerWidget {
|
||||||
@ -18,11 +19,10 @@ class SearchPage extends ConsumerWidget {
|
|||||||
|
|
||||||
void onSubmit(BuildContext context, WidgetRef ref) {
|
void onSubmit(BuildContext context, WidgetRef ref) {
|
||||||
if (ref.read(searchQueryProvider).isNotEmpty) {
|
if (ref.read(searchQueryProvider).isNotEmpty) {
|
||||||
|
ref.read(enableFiltersState.notifier).state = true;
|
||||||
Navigator.push(context,
|
Navigator.push(context,
|
||||||
MaterialPageRoute(builder: (BuildContext context) {
|
MaterialPageRoute(builder: (BuildContext context) {
|
||||||
return ResultPage(
|
return ResultPage(searchQuery: ref.read(searchQueryProvider));
|
||||||
searchQuery: ref.read(searchQueryProvider),
|
|
||||||
);
|
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
showSnackBar(context: context, message: 'Search field is empty');
|
showSnackBar(context: context, message: 'Search field is empty');
|
||||||
|
@ -6,7 +6,8 @@ import 'extensions.dart';
|
|||||||
import 'package:openlib/ui/components/page_title_widget.dart';
|
import 'package:openlib/ui/components/page_title_widget.dart';
|
||||||
import 'package:openlib/ui/components/error_widget.dart';
|
import 'package:openlib/ui/components/error_widget.dart';
|
||||||
import 'package:openlib/ui/results_page.dart';
|
import 'package:openlib/ui/results_page.dart';
|
||||||
import 'package:openlib/state/state.dart' show getTrendingBooks;
|
import 'package:openlib/state/state.dart'
|
||||||
|
show getTrendingBooks, enableFiltersState;
|
||||||
|
|
||||||
class TrendingPage extends ConsumerWidget {
|
class TrendingPage extends ConsumerWidget {
|
||||||
const TrendingPage({super.key});
|
const TrendingPage({super.key});
|
||||||
@ -42,11 +43,11 @@ class TrendingPage extends ConsumerWidget {
|
|||||||
(BuildContext context, int index) {
|
(BuildContext context, int index) {
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
ref.read(enableFiltersState.notifier).state = false;
|
||||||
Navigator.push(context, MaterialPageRoute(
|
Navigator.push(context, MaterialPageRoute(
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return ResultPage(
|
return ResultPage(
|
||||||
searchQuery: data[index].title!,
|
searchQuery: data[index].title!);
|
||||||
);
|
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
@ -142,6 +143,7 @@ class TrendingPage extends ConsumerWidget {
|
|||||||
height: 25,
|
height: 25,
|
||||||
child: CircularProgressIndicator(
|
child: CircularProgressIndicator(
|
||||||
color: Theme.of(context).colorScheme.secondary,
|
color: Theme.of(context).colorScheme.secondary,
|
||||||
|
strokeCap: StrokeCap.round,
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
|
20
pubspec.lock
20
pubspec.lock
@ -73,6 +73,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.3"
|
version: "2.0.3"
|
||||||
|
chunked_downloader:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: chunked_downloader
|
||||||
|
sha256: "7fe3a37a0c7fb1d41f3f922c30937fdccae2647b6f6f71778bffcf4823048754"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.0.2"
|
||||||
cli_util:
|
cli_util:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -141,10 +149,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: dio
|
name: dio
|
||||||
sha256: ce75a1b40947fea0a0e16ce73337122a86762e38b982e1ccb909daa3b9bc4197
|
sha256: "797e1e341c3dd2f69f2dad42564a6feff3bfb87187d05abb93b9609e6f1645c3"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.3.2"
|
version: "5.4.0"
|
||||||
epub_view:
|
epub_view:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -284,10 +292,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: google_fonts
|
name: google_fonts
|
||||||
sha256: e20ff62b158b96f392bfc8afe29dee1503c94fbea2cbe8186fd59b756b8ae982
|
sha256: "6b6f10f0ce3c42f6552d1c70d2c28d764cf22bb487f50f66cca31dcd5194f4d6"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.1.0"
|
version: "4.0.4"
|
||||||
google_nav_bar:
|
google_nav_bar:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -308,10 +316,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: http
|
name: http
|
||||||
sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525"
|
sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "0.13.6"
|
||||||
http_parser:
|
http_parser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
|||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 1.0.3+5
|
version: 1.0.4+7
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=3.0.5 <4.0.0'
|
sdk: '>=3.0.5 <4.0.0'
|
||||||
@ -34,13 +34,13 @@ dependencies:
|
|||||||
|
|
||||||
google_nav_bar: ^5.0.6
|
google_nav_bar: ^5.0.6
|
||||||
|
|
||||||
epub_view: ^3.2.0
|
|
||||||
flutter_pdfview: ^1.2.7
|
flutter_pdfview: ^1.2.7
|
||||||
vocsy_epub_viewer: ^2.0.0
|
vocsy_epub_viewer: ^2.0.0
|
||||||
|
epub_view: ^3.2.0
|
||||||
# syncfusion_flutter_pdfviewer: ^22.2.5
|
# syncfusion_flutter_pdfviewer: ^22.2.5
|
||||||
# pdfx: ^2.4.0
|
# pdfx: ^2.4.0
|
||||||
|
|
||||||
dio: ^5.3.0
|
dio: ^5.4.0
|
||||||
html: ^0.15.4
|
html: ^0.15.4
|
||||||
|
|
||||||
sqflite: ^2.3.0
|
sqflite: ^2.3.0
|
||||||
@ -51,9 +51,10 @@ dependencies:
|
|||||||
webview_cookie_manager: ^2.0.6
|
webview_cookie_manager: ^2.0.6
|
||||||
|
|
||||||
flutter_svg: ^2.0.7
|
flutter_svg: ^2.0.7
|
||||||
google_fonts: ^5.1.0
|
google_fonts:
|
||||||
|
|
||||||
cached_network_image: ^3.2.3
|
cached_network_image: ^3.2.3
|
||||||
|
chunked_downloader: ^0.0.2
|
||||||
|
|
||||||
sqflite_common_ffi: ^2.3.0+2
|
sqflite_common_ffi: ^2.3.0+2
|
||||||
url_launcher: ^6.1.12
|
url_launcher: ^6.1.12
|
||||||
|
Reference in New Issue
Block a user