mirror of
https://github.com/dstark5/Openlib.git
synced 2025-09-19 13:33:35 +08:00
Compare commits
13 Commits
v1.0.3-bet
...
material-y
Author | SHA1 | Date | |
---|---|---|---|
fbe1ee70c9 | |||
c7b1fec73e | |||
72abde09e0 | |||
3d3097152d | |||
c5b1fc54d9 | |||
d253e52971 | |||
0b502629fb | |||
8ec03ba5d7 | |||
068a2c68e0 | |||
59d467c692 | |||
9fffb3829f | |||
e25a12b684 | |||
86684c5436 |
@ -1,30 +1,24 @@
|
||||
## Openlib Contribution guidelines
|
||||
|
||||
### Getting started
|
||||
# Code contribution
|
||||
|
||||
- This project is powered by Flutter. Make sure you have the latest version of Flutter.
|
||||
### Fork the repo
|
||||
|
||||
- If you want to fix bug or implement a new feature, that already mention in the [issues](https://github.com/dstark5/Openlib/issues), please, assign this issue to you and/or comment about it.
|
||||
- Clone the Openlib repository with git clone https://github.com/dstark5/Openlib.git (or use the link from your own fork, if you want to open a PR)
|
||||
|
||||
- Whether you have to implement new feature, please, open an issue or discussion regarding it to ensure it will be accepted.
|
||||
|
||||
### Make your update:
|
||||
Make your changes to the file(s) you'd like to update.
|
||||
|
||||
### Open a pull request
|
||||
|
||||
When you're done making changes and you'd like to propose them for review, open your PR (pull request). You can use the GitHub user interface for some small changes, like fixing a typo or updating a readme. You can also fork the repo and then clone it locally, to view changes and run your tests on your machine.
|
||||
|
||||
### Before PR Self Review
|
||||
|
||||
Always review your own PR first
|
||||
|
||||
- Confirm the changes doesn't break anything else.
|
||||
- Compare your pull request's source changes to staging to confirm that the output matches the source and that everything is rendering as expected. This helps spot issues like typos, content that doesn't follow the style guide, or content that isn't rendering due to versioning problems.
|
||||
|
||||
### Submit your PR & get it reviewed
|
||||
- Once you submit your PR, others from the Docs community will review it with you. The first thing you're going to want to do is a [self review](#self-review).
|
||||
- After that, we may have questions, check back on your PR to keep up with the conversation.
|
||||
- We may ask for changes to be made before a PR can be merged. You can make any other changes in your fork, then commit them to your branch.
|
||||
|
||||
### Self review
|
||||
You should always review your own PR first.
|
||||
|
||||
For content changes, make sure that you:
|
||||
- [ ] Confirm the changes doesn't break anything else.
|
||||
- [ ] Compare your pull request's source changes to staging to confirm that the output matches the source and that everything is rendering as expected. This helps spot issues like typos, content that doesn't follow the style guide, or content that isn't rendering due to versioning problems.
|
||||
- [ ] Review the content for technical accuracy.
|
||||
- [ ] Copy-edit the changes for grammar or spelling mistakes.
|
||||
|
||||
### Notes
|
||||
- Please, do not modify readme and other information files (except for typos).
|
||||
- Avoid adding new dependencies unless required. APK size is important.
|
||||
Once you Submit Your PR We Review The Code And Merge It.
|
28
README.md
28
README.md
@ -1,15 +1,15 @@
|
||||
<p align="center"><img src="assets/icons/appIcon.png" width="150"></p>
|
||||
<p align="center"><img src="assets/icons/appIcon.png" width="150"></p>
|
||||
<h1 align="center"><b>Openlib</b></h1>
|
||||
|
||||
#### 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/) 
|
||||
|
||||
[<img src="github_releases.png"
|
||||
alt="Download from GitHub"
|
||||
height="60">](https://github.com/dstark5/Openlib/releases) [<img src="https://gitlab.com/IzzyOnDroid/repo/-/raw/master/assets/IzzyOnDroid.png"
|
||||
height="80">](https://github.com/dstark5/Openlib/releases) [<img src="https://gitlab.com/IzzyOnDroid/repo/-/raw/master/assets/IzzyOnDroid.png"
|
||||
alt="Get it on IzzyDroid"
|
||||
height="60">](https://android.izzysoft.de/repo/apk/com.app.openlib)
|
||||
height="80">](https://android.izzysoft.de/repo/apk/com.app.openlib)
|
||||
|
||||
|
||||
## Note
|
||||
@ -18,10 +18,6 @@
|
||||
|
||||
#### Publishing Openlib, Or Any Fork Of It In The Google Play Store Violates Their Terms And Conditions.
|
||||
|
||||
## Download
|
||||
- Download and install APK from [GitHub Releases](https://github.com/dstark5/Openlib/releases).
|
||||
- Download and install APK from [ IzzyOnDroid ](https://android.izzysoft.de/repo/apk/com.app.openlib).
|
||||
|
||||
|
||||
## Screenshots
|
||||
|
||||
@ -37,23 +33,25 @@
|
||||
## Description
|
||||
##### Openlib Is An Open Source App To Download And Read Books From Shadow Library ([Anna’s Archive](https://annas-archive.org/)) . The App Has Built In Reader to Read Books.
|
||||
|
||||
##### As [Anna’s Archive](https://annas-archive.org/) Doesn't Have An API.The App Works By Sending Request To Anna’s Archive And Parses The Response To objects.The App Extracts The Mirrors From Response And Downloads The Book.
|
||||
|
||||
##### As [Anna’s Archive](https://annas-archive.org/) Doesn't Have An API.The App Works By Sending Request To Anna’s Archive And Parses The Response To objects.The App Extracts The Mirrors From Response And Downloads The Book And Store It In The Application Document Directory.
|
||||
## Features
|
||||
- Trending Books
|
||||
- Trending Books
|
||||
- Download And Read Books With In-Built Viewer
|
||||
- Supports Epub And Pdf Formats
|
||||
- Open Books In Your Favourite Ebook Reader
|
||||
- Filter Books
|
||||
- Sort Books
|
||||
|
||||
|
||||
## Roadmap
|
||||
|
||||
- Adding More Book Format supports (cbz,cbr,azw3,etc...)
|
||||
- Additing More Book Format supports (cbz,cbr,azw3,etc...)
|
||||
- Adding Support For Background Downloads
|
||||
- Adding Support For Multiple Downloads
|
||||
|
||||
## Installation and updates
|
||||
- Download the APK from [ IzzyOnDroid ](https://android.izzysoft.de/repo/apk/com.app.openlib) and install it.
|
||||
- Download the APK from [GitHub Releases](https://github.com/dstark5/Openlib/releases) and install it.
|
||||
|
||||
## Building from Source
|
||||
|
||||
- If you don't have Flutter SDK installed, please visit official [Flutter](https://flutter.dev) site.
|
||||
@ -78,9 +76,7 @@ flutter build
|
||||
- The Build Will Be In './build/app/outputs/flutter-apk/app-release.apk'
|
||||
|
||||
## Contribution
|
||||
Whether you have ideas, design changes or even major code changes, help is always welcome. The app gets better and better with each contribution, no matter how big or small!
|
||||
|
||||
If you'd like to get involved See [CONTRIBUTING.md](./CONTRIBUTING.md) for the guidelines.
|
||||
Whether you have ideas, design changes or even major code changes, help is always welcome. The app gets better and better with each contribution, no matter how big or small! If you'd like to get involved ,Please read our [CONTRIBUTING.md](https://github.com/dstark5/Openlib/blob/main/CONTRIBUTING.md)
|
||||
|
||||
## Issues
|
||||
|
||||
|
@ -79,8 +79,6 @@ android {
|
||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||
// replace with "debug" when running on debug version
|
||||
signingConfig signingConfigs.release
|
||||
minifyEnabled false
|
||||
shrinkResources false
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -91,4 +89,4 @@ flutter {
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
@ -1,34 +1,11 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_VIDEOS" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
||||
|
||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29"/>
|
||||
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="https" />
|
||||
</intent>
|
||||
</queries>
|
||||
|
||||
<application
|
||||
android:label="Openlib"
|
||||
android:name="${applicationName}"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:usesCleartextTraffic="true"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:icon="@mipmap/launcher_icon">
|
||||
|
||||
<activity
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTop"
|
||||
@ -40,7 +17,6 @@
|
||||
the Android process has started. This theme is visible to the user
|
||||
while the Flutter UI initializes. After that, this theme continues
|
||||
to determine the Window background behind the Flutter UI. -->
|
||||
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme"
|
||||
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 11 KiB |
@ -1,15 +1 @@
|
||||
<p>
|
||||
<i>Openlib</i> is an open source app to download and read books from shadow library (<a href='https://annas-archive.org/' target='_blank' rel='nofollow noopener'>Anna’s Archive</a>). The App Has Built In Reader to Read Books.
|
||||
</p>
|
||||
<p>
|
||||
As <i>Anna’s Archive</i> doesn't have an API, the app works by sending requests to <i>Anna’s Archive</i> and parses the response to objects. The app extracts the mirrors from the responses, downloads the book and stores it in the application's document directory.
|
||||
</p>
|
||||
<p>Main Features:</p>
|
||||
<ul>
|
||||
<li>Trending Books</li>
|
||||
<li>Download And Read Books With In-Built Viewer</li>
|
||||
<li>Supports Epub And Pdf Formats</li>
|
||||
<li>Open Books With Your Favourite Ebooks Reader</li>
|
||||
<li>Filter Books</li>
|
||||
<li>Sort Books</li>
|
||||
</ul>
|
||||
<p><i>Openlib</i> is an open source app to download and read books from shadow library (<a href='https://annas-archive.org/' target='_blank' rel='nofollow noopener'>Anna’s Archive</a>). The App Has Built In Reader to Read Books.</p><p>As <i>Anna’s Archive</i> doesn't have an API, the app works by sending requests to <i>Anna’s Archive</i> and parses the response to objects. The app extracts the mirrors from the responses, downloads the book and stores it in the application's document directory.</p><p>Features include a.o.:</p><ul><li>Trending Books</li><li>Download And Read Books With In-Built Viewer</li><li>Supports Epub And Pdf Formats</li><li>Filter Books</li><li>Sort Books</li></ul>
|
190
lib/main.dart
190
lib/main.dart
@ -1,27 +1,19 @@
|
||||
import 'dart:io' show Platform;
|
||||
import 'package:dynamic_color/dynamic_color.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'dart:io' show Platform;
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:google_nav_bar/google_nav_bar.dart';
|
||||
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
|
||||
import 'package:openlib/ui/themes.dart';
|
||||
import 'package:openlib/ui/extensions.dart';
|
||||
import 'package:openlib/ui/trending_page.dart';
|
||||
import 'package:openlib/ui/search_page.dart';
|
||||
import 'package:openlib/ui/mylibrary_page.dart';
|
||||
import 'package:openlib/ui/settings_page.dart';
|
||||
import 'package:openlib/services/database.dart' show Sqlite, MyLibraryDb;
|
||||
import 'package:openlib/services/files.dart'
|
||||
show moveFilesToAndroidInternalStorage;
|
||||
import 'package:openlib/state/state.dart'
|
||||
show
|
||||
selectedIndexProvider,
|
||||
themeModeProvider,
|
||||
openPdfWithExternalAppProvider,
|
||||
openEpubWithExternalAppProvider,
|
||||
userAgentProvider,
|
||||
cookieProvider,
|
||||
dbProvider;
|
||||
show selectedIndexProvider, dbProvider;
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
@ -31,63 +23,43 @@ void main() async {
|
||||
databaseFactory = databaseFactoryFfi;
|
||||
}
|
||||
|
||||
Database initDb = await Sqlite.initDb();
|
||||
MyLibraryDb dataBase = MyLibraryDb(dbInstance: initDb);
|
||||
bool isDarkMode = await dataBase.getPreference('darkMode');
|
||||
bool openPdfwithExternalapp =
|
||||
await dataBase.getPreference('openPdfwithExternalApp');
|
||||
bool openEpubwithExternalapp =
|
||||
await dataBase.getPreference('openEpubwithExternalApp');
|
||||
|
||||
String browserUserAgent = await dataBase.getBrowserOptions('userAgent');
|
||||
String browserCookie = await dataBase.getBrowserOptions('cookie');
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
//[SystemChrome] Also change colors in settings page Theme colors if any change
|
||||
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
|
||||
systemNavigationBarColor:
|
||||
isDarkMode ? Colors.black : Colors.grey.shade200));
|
||||
await moveFilesToAndroidInternalStorage();
|
||||
}
|
||||
|
||||
Database db = await Sqlite.initDb();
|
||||
runApp(
|
||||
ProviderScope(
|
||||
overrides: [
|
||||
dbProvider.overrideWithValue(dataBase),
|
||||
themeModeProvider.overrideWith(
|
||||
(ref) => isDarkMode ? ThemeMode.dark : ThemeMode.light),
|
||||
openPdfWithExternalAppProvider
|
||||
.overrideWith((ref) => openPdfwithExternalapp),
|
||||
openEpubWithExternalAppProvider
|
||||
.overrideWith((ref) => openEpubwithExternalapp),
|
||||
userAgentProvider.overrideWith((ref) => browserUserAgent),
|
||||
cookieProvider.overrideWith((ref) => browserCookie),
|
||||
],
|
||||
overrides: [dbProvider.overrideWithValue(MyLibraryDb(dbInstance: db))],
|
||||
child: const MyApp(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
class MyApp extends ConsumerWidget {
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({super.key});
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return MaterialApp(
|
||||
builder: (BuildContext context, Widget? child) {
|
||||
return MediaQuery(
|
||||
data: MediaQuery.of(context).copyWith(
|
||||
textScaleFactor: 1.0,
|
||||
),
|
||||
child: child!,
|
||||
);
|
||||
},
|
||||
debugShowCheckedModeBanner: false,
|
||||
title: 'Openlib',
|
||||
theme: lightTheme,
|
||||
darkTheme: darkTheme,
|
||||
themeMode: ref.watch(themeModeProvider),
|
||||
home: const HomePage(),
|
||||
);
|
||||
Widget build(BuildContext context) {
|
||||
return DynamicColorBuilder(
|
||||
builder: (ColorScheme? lightDynamic, ColorScheme? darkDynamic) {
|
||||
return MaterialApp(
|
||||
// builder: (BuildContext context, Widget? child) {
|
||||
// return MediaQuery(
|
||||
// data: MediaQuery.of(context).copyWith(
|
||||
// textScaleFactor: 1.0,
|
||||
// ),
|
||||
// child: child!,
|
||||
// );
|
||||
// },
|
||||
debugShowCheckedModeBanner: false,
|
||||
title: 'Openlib',
|
||||
theme: ThemeData(
|
||||
useMaterial3: true,
|
||||
colorScheme: lightDynamic,
|
||||
),
|
||||
darkTheme: ThemeData(
|
||||
useMaterial3: true,
|
||||
colorScheme: darkDynamic,
|
||||
),
|
||||
home: const HomePage(),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,86 +74,44 @@ class _HomePageState extends ConsumerState<HomePage> {
|
||||
static const List<Widget> _widgetOptions = <Widget>[
|
||||
TrendingPage(),
|
||||
SearchPage(),
|
||||
MyLibraryPage(),
|
||||
SettingsPage()
|
||||
MyLibraryPage()
|
||||
];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
|
||||
|
||||
final selectedIndex = ref.watch(selectedIndexProvider);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
title: const Text("Openlib"),
|
||||
titleTextStyle: Theme.of(context).textTheme.displayLarge,
|
||||
),
|
||||
body: _widgetOptions.elementAt(selectedIndex),
|
||||
bottomNavigationBar: SafeArea(
|
||||
child: GNav(
|
||||
backgroundColor: isDarkMode ? Colors.black : Colors.grey.shade200,
|
||||
haptic: true,
|
||||
tabBorderRadius: 50,
|
||||
tabActiveBorder: Border.all(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
systemNavigationBarColor: ElevationOverlay.applySurfaceTint(
|
||||
Theme.of(context).colorScheme.surface,
|
||||
Theme.of(context).colorScheme.surfaceTint,
|
||||
3)),
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).colorScheme.background,
|
||||
appBar: AppBar(
|
||||
leading:
|
||||
Icon(Icons.book, color: Theme.of(context).colorScheme.primary),
|
||||
title: const Text(
|
||||
"Openlib",
|
||||
style: TextStyle(fontWeight: FontWeight.w600),
|
||||
),
|
||||
tabMargin: const EdgeInsets.fromLTRB(13, 6, 13, 2.5),
|
||||
curve: Curves.fastLinearToSlowEaseIn,
|
||||
duration: const Duration(milliseconds: 25),
|
||||
gap: 5,
|
||||
color: const Color.fromARGB(255, 255, 255, 255),
|
||||
activeColor: const Color.fromARGB(255, 255, 255, 255),
|
||||
iconSize: 19, // tab button icon size
|
||||
tabBackgroundColor: Theme.of(context).colorScheme.secondary,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 13, vertical: 6.5),
|
||||
tabs: [
|
||||
GButton(
|
||||
icon: Icons.trending_up,
|
||||
text: 'Trending',
|
||||
iconColor: isDarkMode ? Colors.white : Colors.black,
|
||||
textStyle: const TextStyle(
|
||||
fontWeight: FontWeight.w900,
|
||||
color: Colors.white,
|
||||
fontSize: 11,
|
||||
),
|
||||
),
|
||||
GButton(
|
||||
icon: Icons.search,
|
||||
text: 'Search',
|
||||
iconColor: isDarkMode ? Colors.white : Colors.black,
|
||||
textStyle: const TextStyle(
|
||||
fontWeight: FontWeight.w900,
|
||||
color: Colors.white,
|
||||
fontSize: 11,
|
||||
),
|
||||
),
|
||||
GButton(
|
||||
icon: Icons.collections_bookmark,
|
||||
text: 'My Library',
|
||||
iconColor: isDarkMode ? Colors.white : Colors.black,
|
||||
textStyle: const TextStyle(
|
||||
fontWeight: FontWeight.w900,
|
||||
color: Colors.white,
|
||||
fontSize: 11,
|
||||
),
|
||||
),
|
||||
GButton(
|
||||
icon: Icons.build,
|
||||
text: 'Settings',
|
||||
iconColor: isDarkMode ? Colors.white : Colors.black,
|
||||
textStyle: const TextStyle(
|
||||
fontWeight: FontWeight.w900,
|
||||
color: Colors.white,
|
||||
fontSize: 11,
|
||||
),
|
||||
),
|
||||
titleSpacing: 1,
|
||||
),
|
||||
body: _widgetOptions.elementAt(selectedIndex),
|
||||
bottomNavigationBar: NavigationBar(
|
||||
destinations: const [
|
||||
NavigationDestination(
|
||||
icon: Icon(Icons.trending_up), label: "Trending"),
|
||||
NavigationDestination(icon: Icon(Icons.search), label: "Search"),
|
||||
NavigationDestination(
|
||||
icon: Icon(Icons.collections_bookmark), label: "My Library"),
|
||||
],
|
||||
selectedIndex: selectedIndex,
|
||||
onTabChange: (index) async {
|
||||
onDestinationSelected: (index) async {
|
||||
ref.read(selectedIndexProvider.notifier).state = index;
|
||||
},
|
||||
labelBehavior: NavigationDestinationLabelBehavior.onlyShowSelected,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -1,5 +1,4 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:html/parser.dart' show parse;
|
||||
|
||||
@ -49,25 +48,23 @@ class BookInfoData extends BookData {
|
||||
}
|
||||
|
||||
class AnnasArchieve {
|
||||
String baseUrl = "https://annas-archive.org";
|
||||
String baseUrl = "https://annas-archive.gs";
|
||||
|
||||
final Dio dio = Dio();
|
||||
|
||||
Map<String, dynamic> defaultDioHeaders = {
|
||||
"user-agent":
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36",
|
||||
};
|
||||
final Dio dio = Dio(BaseOptions(headers: {
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (iPhone; CPU iPhone OS 12_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/7.0.4 Mobile/16B91 Safari/605.1.15'
|
||||
}));
|
||||
|
||||
String getMd5(String url) {
|
||||
String md5 = url.toString().split('/').last;
|
||||
return md5;
|
||||
}
|
||||
|
||||
List<BookData> _parser(resData, String fileType) {
|
||||
List<BookData> _parser(resData) {
|
||||
var document =
|
||||
parse(resData.toString().replaceAll(RegExp(r"<!--|-->"), ''));
|
||||
var books = document.querySelectorAll(
|
||||
'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-black/6.7 focus:outline "]');
|
||||
'a[class="js-vim-focus custom-a flex items-center relative left-[-10px] w-[calc(100%+20px)] px-[10px] py-2 outline-offset-[-2px] outline-2 rounded-[3px] hover:bg-[#00000011] focus:outline "]');
|
||||
|
||||
List<BookData> bookList = [];
|
||||
|
||||
@ -76,32 +73,21 @@ class AnnasArchieve {
|
||||
'title': element.querySelector('h3')?.text,
|
||||
'thumbnail': element.querySelector('img')?.attributes['src'],
|
||||
'link': element.attributes['href'],
|
||||
'author': element
|
||||
.querySelector(
|
||||
'div[class="max-lg:line-clamp-[2] lg:truncate leading-[1.2] lg:leading-[1.35] max-lg:text-sm italic"]')
|
||||
?.text ??
|
||||
'author': element.querySelector('div[class="truncate italic"]')?.text ??
|
||||
'unknown',
|
||||
'publisher': element
|
||||
.querySelector(
|
||||
'div[class="truncate leading-[1.2] lg:leading-[1.35] max-lg:text-xs"]')
|
||||
?.text ??
|
||||
"unknown",
|
||||
'publisher':
|
||||
element.querySelector('div[class="truncate text-sm"]')?.text ??
|
||||
"unknown",
|
||||
'info': element
|
||||
.querySelector(
|
||||
'div[class="line-clamp-[2] leading-[1.2] text-[10px] lg:text-xs text-gray-500"]')
|
||||
.querySelector('div[class="truncate text-xs text-gray-500"]')
|
||||
?.text ??
|
||||
''
|
||||
};
|
||||
|
||||
if ((data['title'] != null && data['title'] != '') &&
|
||||
(data['link'] != null && data['link'] != '') &&
|
||||
(data['info'] != null &&
|
||||
((fileType == "") &&
|
||||
(data['info']!.contains('pdf') ||
|
||||
data['info']!.contains('epub') ||
|
||||
data['info']!.contains('cbr') ||
|
||||
data['info']!.contains('cbz')) ||
|
||||
((fileType != "") && data['info']!.contains(fileType))))) {
|
||||
(data['info']!.contains('pdf') ||
|
||||
data['info']!.contains('epub')))) {
|
||||
String link = baseUrl + data['link']!;
|
||||
String publisher = ((data['publisher']?.contains('0') == true &&
|
||||
data['publisher']!.length < 2) ||
|
||||
@ -129,71 +115,46 @@ class AnnasArchieve {
|
||||
if (info.contains('pdf') == true) {
|
||||
return 'pdf';
|
||||
} else {
|
||||
if (info.contains('cbr')) return "cbr";
|
||||
if (info.contains('cbz')) return "cbz";
|
||||
return "epub";
|
||||
return 'epub';
|
||||
}
|
||||
}
|
||||
|
||||
Future<String?> _getMirrorLink(
|
||||
String url, String userAgent, String cookie) async {
|
||||
Future<String?> _getMirrorLink(String url) async {
|
||||
try {
|
||||
final response = await dio.get(url,
|
||||
options: Options(extra: {
|
||||
'withCredentials': true
|
||||
}, headers: {
|
||||
"Host": "annas-archive.org",
|
||||
"Origin": "https://annas-archive.org",
|
||||
"Upgrade-Insecure-Requests": "1",
|
||||
"Sec-Fetch-Dest": "secure",
|
||||
"Sec-Fetch-Mode": "navigate",
|
||||
"Sec-Fetch-Site": "same-site",
|
||||
"Cookie": cookie,
|
||||
"User-Agent": userAgent
|
||||
}));
|
||||
|
||||
var document = parse(response.data.toString());
|
||||
|
||||
var pTag = document.querySelectorAll('p[class="mb-4"]');
|
||||
String? link = pTag[1].querySelector('a')?.attributes['href'];
|
||||
final response = await dio.get(url);
|
||||
var document = parse(response.toString());
|
||||
var pTag = document.querySelector('p[class="mb-4"]');
|
||||
String? link = pTag?.querySelector('a')?.attributes['href'];
|
||||
return link;
|
||||
} catch (e) {
|
||||
// print('${url} ${e}');
|
||||
if (e.toString().contains("403")) {
|
||||
throw jsonEncode({"code": "403", "url": url});
|
||||
}
|
||||
// print(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<BookInfoData?> _bookInfoParser(resData, url, userAgent, cookie) async {
|
||||
Future<BookInfoData?> _bookInfoParser(resData, url) async {
|
||||
var document = parse(resData.toString());
|
||||
var main = document.querySelector('main[class="main"]');
|
||||
var ul = main?.querySelectorAll('ul[class="mb-4"]');
|
||||
var ul = main?.querySelector('ul[class="mb-4"]');
|
||||
|
||||
List<String> mirrors = [];
|
||||
|
||||
if (ul != null) {
|
||||
var anchorTags = [];
|
||||
if (ul.length == 2) {
|
||||
anchorTags = ul[1].querySelectorAll('a');
|
||||
} else {
|
||||
anchorTags = ul[0].querySelectorAll('a');
|
||||
}
|
||||
var a = ul.querySelectorAll('a');
|
||||
|
||||
for (var element in anchorTags) {
|
||||
for (var element in a) {
|
||||
if (element.attributes['href']!.startsWith('https://')) {
|
||||
if (element.attributes['href'] != null &&
|
||||
element.attributes['href'].startsWith('https://1lib.sk') !=
|
||||
true) {
|
||||
if (element.attributes['href'] != null) {
|
||||
mirrors.add(element.attributes['href']!);
|
||||
}
|
||||
} else if (element.attributes['href'] != null &&
|
||||
element.attributes['href']!.startsWith('/slow_download')) {
|
||||
String? url = await _getMirrorLink(
|
||||
'$baseUrl${element.attributes['href']!}', userAgent, cookie);
|
||||
if (url != null && url.isNotEmpty) {
|
||||
mirrors.add(url);
|
||||
if (element.text.contains('Slow Partner Server #1') != true) {
|
||||
String? url =
|
||||
await _getMirrorLink('$baseUrl${element.attributes['href']!}');
|
||||
if (url != null && url.isNotEmpty) {
|
||||
mirrors.add(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -246,40 +207,15 @@ 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(
|
||||
{required String searchQuery,
|
||||
String content = "",
|
||||
String sort = "",
|
||||
String fileType = "",
|
||||
bool enableFilters = true}) async {
|
||||
String sort = ""}) async {
|
||||
try {
|
||||
final String encodedURL = urlEncoder(
|
||||
searchQuery: searchQuery,
|
||||
content: content,
|
||||
sort: sort,
|
||||
fileType: fileType,
|
||||
enableFilters: enableFilters);
|
||||
|
||||
final response = await dio.get(encodedURL,
|
||||
options: Options(headers: defaultDioHeaders));
|
||||
if (!enableFilters) {
|
||||
return _parser(response.data, "");
|
||||
}
|
||||
return _parser(response.data, fileType);
|
||||
final response = await dio.get(
|
||||
'$baseUrl/search?lang=&content=$content&ext=&sort=$sort&q=$searchQuery',
|
||||
);
|
||||
return _parser(response.data);
|
||||
} on DioException catch (e) {
|
||||
if (e.type == DioExceptionType.unknown) {
|
||||
throw "socketException";
|
||||
@ -288,15 +224,10 @@ class AnnasArchieve {
|
||||
}
|
||||
}
|
||||
|
||||
Future<BookInfoData> bookInfo(
|
||||
{required String url,
|
||||
required String userAgent,
|
||||
required String cookie}) async {
|
||||
Future<BookInfoData> bookInfo({required String url}) async {
|
||||
try {
|
||||
final response =
|
||||
await dio.get(url, options: Options(headers: defaultDioHeaders));
|
||||
BookInfoData? data =
|
||||
await _bookInfoParser(response.data, url, userAgent, cookie);
|
||||
final response = await dio.get(url);
|
||||
BookInfoData? data = await _bookInfoParser(response.data, url);
|
||||
if (data != null) {
|
||||
return data;
|
||||
} else {
|
||||
|
@ -9,38 +9,22 @@ class Sqlite {
|
||||
|
||||
Database dbInstance = await openDatabase(
|
||||
path,
|
||||
version: 5,
|
||||
version: 2,
|
||||
onCreate: (Database db, int version) async {
|
||||
await db.execute(
|
||||
'CREATE TABLE mybooks (id TEXT PRIMARY KEY, title TEXT,author TEXT,thumbnail TEXT,link TEXT,publisher TEXT,info TEXT,format TEXT,description TEXT)');
|
||||
await db.execute(
|
||||
'CREATE TABLE preferences (name TEXT PRIMARY KEY,value BOOLEAN)');
|
||||
if (isMobile) {
|
||||
await db.execute(
|
||||
'CREATE TABLE bookposition (fileName TEXT PRIMARY KEY,position TEXT)');
|
||||
await db.execute(
|
||||
'CREATE TABLE browserOptions (name TEXT PRIMARY KEY,value TEXT)');
|
||||
}
|
||||
},
|
||||
onUpgrade: (db, oldVersion, newVersion) async {
|
||||
List<dynamic> isTableExist = await db.query('sqlite_master',
|
||||
where: 'name = ?', whereArgs: ['bookposition']);
|
||||
List<dynamic> isPreferenceTableExist = await db.query('sqlite_master',
|
||||
where: 'name = ?', whereArgs: ['preferences']);
|
||||
List<dynamic> isbrowserOptionsExist = await db.query('sqlite_master',
|
||||
where: 'name = ?', whereArgs: ['browserOptions']);
|
||||
if (isPreferenceTableExist.isEmpty) {
|
||||
await db.execute(
|
||||
'CREATE TABLE preferences (name TEXT PRIMARY KEY,value BOOLEAN)');
|
||||
}
|
||||
if (isMobile && isTableExist.isEmpty) {
|
||||
await db.execute(
|
||||
'CREATE TABLE bookposition (fileName TEXT PRIMARY KEY,position TEXT)');
|
||||
}
|
||||
if (isMobile && isbrowserOptionsExist.isEmpty) {
|
||||
await db.execute(
|
||||
'CREATE TABLE browserOptions (name TEXT PRIMARY KEY,value TEXT)');
|
||||
}
|
||||
},
|
||||
);
|
||||
return dbInstance;
|
||||
@ -180,47 +164,4 @@ class MyLibraryDb {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> savePreference(String name, bool value) async {
|
||||
int boolInt = value ? 1 : 0;
|
||||
await dbInstance.insert(
|
||||
'preferences',
|
||||
{'name': name, 'value': boolInt},
|
||||
conflictAlgorithm: ConflictAlgorithm.replace,
|
||||
);
|
||||
}
|
||||
|
||||
Future<bool> getPreference(String name) async {
|
||||
List<Map<String, dynamic>> data = await dbInstance
|
||||
.query('preferences', where: 'name = ?', whereArgs: [name]);
|
||||
List<dynamic> dataList = List.generate(data.length, (i) {
|
||||
return {'name': data[i]['name'], 'value': data[i]['value']};
|
||||
});
|
||||
if (dataList.isNotEmpty) {
|
||||
return dataList[0]['value'] == 0 ? false : true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> setBrowserOptions(String name, String value) async {
|
||||
await dbInstance.insert(
|
||||
'browserOptions',
|
||||
{'name': name, 'value': value},
|
||||
conflictAlgorithm: ConflictAlgorithm.replace,
|
||||
);
|
||||
}
|
||||
|
||||
Future<String> getBrowserOptions(String name) async {
|
||||
List<Map<String, dynamic>> data = await dbInstance
|
||||
.query('browserOptions', where: 'name = ?', whereArgs: [name]);
|
||||
List<dynamic> dataList = List.generate(data.length, (i) {
|
||||
return {'name': data[i]['name'], 'value': data[i]['value']};
|
||||
});
|
||||
if (dataList.isNotEmpty) {
|
||||
return dataList[0]['value'];
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:chunked_downloader/chunked_downloader.dart';
|
||||
import 'files.dart';
|
||||
|
||||
Future<String> _getFilePath(String fileName) async {
|
||||
final path = await getAppDirectoryPath;
|
||||
return '$path/$fileName';
|
||||
final path = await getApplicationDocumentsDirectory();
|
||||
return '${path.path}/$fileName';
|
||||
}
|
||||
|
||||
List<String> _reorderMirrors(List<String> mirrors) {
|
||||
@ -15,8 +14,7 @@ List<String> _reorderMirrors(List<String> mirrors) {
|
||||
if (element.contains('ipfs') == true) {
|
||||
ipfsMirrors.add(element);
|
||||
} else {
|
||||
if (element.startsWith('https://annas-archive.org') != true &&
|
||||
element.startsWith('https://1lib.sk') != true) {
|
||||
if (element.startsWith('https://annas-archive.gs') != true) {
|
||||
httpsMirrors.add(element);
|
||||
}
|
||||
}
|
||||
@ -24,62 +22,45 @@ List<String> _reorderMirrors(List<String> mirrors) {
|
||||
return [...ipfsMirrors, ...httpsMirrors];
|
||||
}
|
||||
|
||||
Future<String?> _getAliveMirror(List<String> mirrors, Dio dio) async {
|
||||
for (var url in mirrors) {
|
||||
try {
|
||||
final response = await dio.head(url,
|
||||
options: Options(receiveTimeout: const Duration(seconds: 5)));
|
||||
if (response.statusCode == 200) {
|
||||
return url;
|
||||
}
|
||||
} catch (e) {
|
||||
// print("timeOut");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<void> downloadFile(
|
||||
{required List<String> mirrors,
|
||||
required String md5,
|
||||
required String format,
|
||||
required Function onProgress,
|
||||
required Function cancelDownlaod,
|
||||
required Function mirrorStatus,
|
||||
required Function onDownlaodFailed}) async {
|
||||
Dio dio = Dio();
|
||||
|
||||
String path = await _getFilePath('$md5.$format');
|
||||
List<String> orderedMirrors = _reorderMirrors(mirrors);
|
||||
|
||||
String? workingMirror = await _getAliveMirror(orderedMirrors, dio);
|
||||
|
||||
// print(workingMirror);
|
||||
// print(path);
|
||||
// print(orderedMirrors);
|
||||
// print(orderedMirrors[0]);
|
||||
|
||||
if (workingMirror != null) {
|
||||
try {
|
||||
var chunkedDownloader = await ChunkedDownloader(
|
||||
url: workingMirror,
|
||||
saveFilePath: path,
|
||||
chunkSize: 32 * 1024,
|
||||
onError: (error) {
|
||||
onDownlaodFailed();
|
||||
},
|
||||
onProgress: (received, total, speed) {
|
||||
onProgress(received, total);
|
||||
},
|
||||
onDone: (file) {})
|
||||
.start();
|
||||
try {
|
||||
CancelToken cancelToken = CancelToken();
|
||||
|
||||
mirrorStatus(true);
|
||||
cancelDownlaod(chunkedDownloader);
|
||||
} catch (e) {
|
||||
onDownlaodFailed();
|
||||
}
|
||||
} else {
|
||||
dio.download(
|
||||
orderedMirrors[0],
|
||||
path,
|
||||
options: Options(headers: {
|
||||
'Connection': 'Keep-Alive',
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'
|
||||
}),
|
||||
onReceiveProgress: (rcv, total) {
|
||||
onProgress(rcv, total);
|
||||
},
|
||||
deleteOnError: true,
|
||||
cancelToken: cancelToken,
|
||||
).catchError((err) {
|
||||
if (err.type != DioExceptionType.cancel) {
|
||||
onDownlaodFailed();
|
||||
}
|
||||
throw err;
|
||||
});
|
||||
|
||||
cancelDownlaod(cancelToken);
|
||||
} catch (e) {
|
||||
onDownlaodFailed();
|
||||
}
|
||||
}
|
||||
|
@ -4,32 +4,8 @@ import 'package:path_provider/path_provider.dart';
|
||||
import 'package:openlib/state/state.dart' show dbProvider, myLibraryProvider;
|
||||
|
||||
Future<String> get getAppDirectoryPath async {
|
||||
if (Platform.isAndroid) {
|
||||
final directory = await getExternalStorageDirectory();
|
||||
return directory!.path;
|
||||
} else {
|
||||
final directory = await getApplicationDocumentsDirectory();
|
||||
return directory.path;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> moveFilesToAndroidInternalStorage() async {
|
||||
try {
|
||||
final directory = await getApplicationDocumentsDirectory();
|
||||
final directoryExternal = await getExternalStorageDirectory();
|
||||
List<FileSystemEntity> files = Directory(directory.path).listSync();
|
||||
for (var element in files) {
|
||||
if ((element.path.contains('pdf')) || element.path.contains('epub')) {
|
||||
String fileName = element.path.split('/').last;
|
||||
File file = File(element.path);
|
||||
file.copySync('${directoryExternal!.path}/$fileName');
|
||||
file.deleteSync();
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore: avoid_print
|
||||
print(e);
|
||||
}
|
||||
final directory = await getApplicationDocumentsDirectory();
|
||||
return directory.path;
|
||||
}
|
||||
|
||||
Future<bool> isFileExists(String filePath) async {
|
||||
|
@ -52,97 +52,3 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,7 @@
|
||||
import 'dart:math';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:openlib/services/database.dart';
|
||||
import 'package:chunked_downloader/chunked_downloader.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
import 'package:openlib/services/open_library.dart';
|
||||
import 'package:openlib/services/annas_archieve.dart';
|
||||
@ -30,12 +29,8 @@ Map<String, String> sortValues = {
|
||||
'Smallest': 'smallest',
|
||||
};
|
||||
|
||||
List<String> fileType = ["All", "PDF", "Epub", "Cbr", "Cbz"];
|
||||
|
||||
final selectedIndexProvider = StateProvider<int>((ref) => 0);
|
||||
|
||||
final themeModeProvider = StateProvider<ThemeMode>((ref) => ThemeMode.light);
|
||||
|
||||
final selectedTypeState = StateProvider<String>((ref) => "All");
|
||||
|
||||
final getTypeValue = Provider.autoDispose<String>((ref) {
|
||||
@ -48,15 +43,6 @@ final getSortValue = Provider.autoDispose<String>((ref) {
|
||||
return sortValues[ref.read(selectedSortState)] ?? '';
|
||||
});
|
||||
|
||||
final selectedFileTypeState = StateProvider<String>((ref) => "All");
|
||||
|
||||
final getFileTypeValue = Provider.autoDispose<String>((ref) {
|
||||
if (ref.read(selectedFileTypeState) == "All") {
|
||||
return '';
|
||||
}
|
||||
return ref.read(selectedFileTypeState).toLowerCase();
|
||||
});
|
||||
|
||||
//searchQueryProvider
|
||||
|
||||
final searchQueryProvider = StateProvider<String>((ref) => "");
|
||||
@ -65,22 +51,9 @@ final searchQueryProvider = StateProvider<String>((ref) => "");
|
||||
|
||||
final getTrendingBooks = FutureProvider<List<TrendingBookData>>((ref) async {
|
||||
OpenLibrary openLibrary = OpenLibrary();
|
||||
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;
|
||||
return await openLibrary.trendingBooks();
|
||||
});
|
||||
|
||||
final enableFiltersState = StateProvider<bool>((ref) => true);
|
||||
|
||||
//Provider for Trending Books
|
||||
final searchProvider = FutureProvider.family
|
||||
.autoDispose<List<BookData>, String>((ref, searchQuery) async {
|
||||
@ -88,31 +61,21 @@ final searchProvider = FutureProvider.family
|
||||
List<BookData> data = await annasArchieve.searchBooks(
|
||||
searchQuery: searchQuery,
|
||||
content: ref.watch(getTypeValue),
|
||||
sort: ref.watch(getSortValue),
|
||||
fileType: ref.watch(getFileTypeValue),
|
||||
enableFilters: ref.watch(enableFiltersState));
|
||||
sort: ref.watch(getSortValue));
|
||||
return data;
|
||||
});
|
||||
|
||||
final cookieProvider = StateProvider<String>((ref) => "");
|
||||
final userAgentProvider = StateProvider<String>((ref) => "");
|
||||
|
||||
//Provider for Book Info
|
||||
final bookInfoProvider =
|
||||
FutureProvider.family<BookInfoData, String>((ref, url) async {
|
||||
AnnasArchieve annasArchieve = AnnasArchieve();
|
||||
BookInfoData data = await annasArchieve.bookInfo(
|
||||
url: url,
|
||||
userAgent: ref.read(userAgentProvider),
|
||||
cookie: ref.read(cookieProvider));
|
||||
BookInfoData data = await annasArchieve.bookInfo(url: url);
|
||||
return data;
|
||||
});
|
||||
|
||||
final downloadProgressProvider =
|
||||
StateProvider.autoDispose<double>((ref) => 0.0);
|
||||
|
||||
final mirrorStatusProvider = StateProvider.autoDispose<bool>((ref) => false);
|
||||
|
||||
final totalFileSizeInBytes = StateProvider.autoDispose<int>((ref) => 0);
|
||||
final downloadedFileSizeInBytes = StateProvider.autoDispose<int>((ref) => 0);
|
||||
|
||||
@ -132,8 +95,8 @@ final getDownloadedFileSize = StateProvider.autoDispose<String>((ref) {
|
||||
return bytesToFileSize(ref.watch(downloadedFileSizeInBytes));
|
||||
});
|
||||
|
||||
final cancelCurrentDownload = StateProvider<ChunkedDownloader>((ref) {
|
||||
return ChunkedDownloader(saveFilePath: "", url: "");
|
||||
final cancelCurrentDownload = StateProvider<CancelToken>((ref) {
|
||||
return CancelToken();
|
||||
});
|
||||
|
||||
final dbProvider = Provider<MyLibraryDb>((ref) => throw UnimplementedError());
|
||||
@ -178,9 +141,6 @@ final getBookPosition =
|
||||
return await ref.read(dbProvider).getBookState(fileName);
|
||||
});
|
||||
|
||||
final openPdfWithExternalAppProvider = StateProvider<bool>((ref) => false);
|
||||
final openEpubWithExternalAppProvider = StateProvider<bool>((ref) => false);
|
||||
|
||||
final filePathProvider =
|
||||
FutureProvider.family<String, String>((ref, fileName) async {
|
||||
String path = await getFilePath(fileName);
|
||||
|
@ -1,131 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import 'package:openlib/ui/components/snack_bar_widget.dart';
|
||||
import 'package:openlib/ui/components/page_title_widget.dart';
|
||||
|
||||
class AboutPage extends StatelessWidget {
|
||||
const AboutPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
title: const Text("Openlib"),
|
||||
titleTextStyle: Theme.of(context).textTheme.displayLarge,
|
||||
),
|
||||
body: const SingleChildScrollView(
|
||||
physics: BouncingScrollPhysics(),
|
||||
scrollDirection: Axis.vertical,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(left: 5, right: 5, top: 10),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TitleText("About"),
|
||||
Padding(
|
||||
padding:
|
||||
EdgeInsets.only(left: 7, right: 7, top: 13, bottom: 10),
|
||||
child: Text(
|
||||
"An Open source app to download and read books from shadow library (Anna`s Archive)",
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 7, right: 7, top: 10),
|
||||
child: Text(
|
||||
"Version",
|
||||
style: TextStyle(fontSize: 19, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 7, right: 7, top: 5),
|
||||
child: Text(
|
||||
"1.0.3",
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.grey),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 7, right: 7, top: 15),
|
||||
child: Text(
|
||||
"Github",
|
||||
style: TextStyle(fontSize: 19, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
_UrlText(
|
||||
text: 'Open Github Page',
|
||||
url: 'https://github.com/dstark5/Openlib',
|
||||
),
|
||||
_UrlText(
|
||||
text: 'Contribute To Openlib',
|
||||
url:
|
||||
'https://github.com/dstark5/Openlib/blob/main/CONTRIBUTING.md'),
|
||||
_UrlText(
|
||||
text: 'Report An Issue',
|
||||
url: 'https://github.com/dstark5/Openlib/issues'),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 7, right: 7, top: 15),
|
||||
child: Text(
|
||||
"Licence",
|
||||
style: TextStyle(fontSize: 19, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
_UrlText(
|
||||
text: "GPL v3.0 license",
|
||||
url: 'https://www.gnu.org/licenses/gpl-3.0.en.html'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _UrlText extends StatelessWidget {
|
||||
const _UrlText({Key? key, required this.text, required this.url})
|
||||
: super(key: key);
|
||||
|
||||
final String url;
|
||||
final String text;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 7, right: 7, top: 5),
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
final Uri uri = Uri.parse(url);
|
||||
if (!await launchUrl(uri, mode: LaunchMode.externalApplication)) {
|
||||
// ignore: use_build_context_synchronously
|
||||
showSnackBar(context: context, message: 'Could not launch $uri');
|
||||
}
|
||||
},
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
text,
|
||||
style: const TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.blueAccent,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 2,
|
||||
),
|
||||
const Icon(
|
||||
Icons.launch,
|
||||
size: 17,
|
||||
color: Colors.blueAccent,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
import 'dart:convert';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:openlib/services/database.dart';
|
||||
@ -16,125 +15,44 @@ import 'package:openlib/state/state.dart'
|
||||
getTotalFileSize,
|
||||
getDownloadedFileSize,
|
||||
cancelCurrentDownload,
|
||||
mirrorStatusProvider,
|
||||
dbProvider,
|
||||
checkIdExists,
|
||||
myLibraryProvider;
|
||||
|
||||
import 'package:chunked_downloader/chunked_downloader.dart';
|
||||
|
||||
import 'package:openlib/ui/components/book_info_widget.dart';
|
||||
import 'package:openlib/ui/components/file_buttons_widget.dart';
|
||||
import 'package:openlib/ui/components/snack_bar_widget.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:openlib/ui/webview_page.dart';
|
||||
|
||||
class BookInfoPage extends ConsumerWidget {
|
||||
const BookInfoPage({Key? key, required this.url}) : super(key: key);
|
||||
const BookInfoPage({Key? key, required this.url, required this.title})
|
||||
: super(key: key);
|
||||
|
||||
final String url;
|
||||
final String title;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final bookInfo = ref.watch(bookInfoProvider(url));
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
title: const Text("Openlib"),
|
||||
titleTextStyle: Theme.of(context).textTheme.displayLarge,
|
||||
// backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
title: Text(title),
|
||||
titleSpacing: 0,
|
||||
// titleTextStyle: Theme.of(context).textTheme.displayLarge,
|
||||
),
|
||||
body: bookInfo.when(
|
||||
skipLoadingOnRefresh: false,
|
||||
data: (data) {
|
||||
return BookInfoWidget(
|
||||
data: data, child: ActionButtonWidget(data: data));
|
||||
},
|
||||
error: (err, _) {
|
||||
if (err.toString().contains("403")) {
|
||||
var errJson = jsonDecode(err.toString());
|
||||
|
||||
if (SchedulerBinding.instance.schedulerPhase ==
|
||||
SchedulerPhase.persistentCallbacks) {
|
||||
SchedulerBinding.instance.addPostFrameCallback((_) {
|
||||
Future.delayed(
|
||||
const Duration(seconds: 3),
|
||||
() => Navigator.pushReplacement(context,
|
||||
MaterialPageRoute(builder: (BuildContext context) {
|
||||
return Webview(url: errJson["url"]);
|
||||
})));
|
||||
});
|
||||
}
|
||||
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 210,
|
||||
child: SvgPicture.asset(
|
||||
'assets/captcha.svg',
|
||||
width: 210,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 30,
|
||||
),
|
||||
Text(
|
||||
"Captcha required",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).textTheme.headlineMedium?.color,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
"you will be redirected to solve captcha",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).textTheme.headlineSmall?.color,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(30, 15, 30, 10),
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color.fromARGB(255, 255, 186, 186),
|
||||
borderRadius: BorderRadius.circular(5)),
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
child: Text(
|
||||
"If you have solved the captcha then you will be automatically redirected to the results page . In case you seeing this page even after completing try using a VPN .",
|
||||
textAlign: TextAlign.start,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
} else {
|
||||
return CustomErrorWidget(
|
||||
error: err,
|
||||
stackTrace: _,
|
||||
onRefresh: () {
|
||||
// ignore: unused_result
|
||||
ref.refresh(bookInfoProvider(url));
|
||||
},
|
||||
);
|
||||
}
|
||||
return CustomErrorWidget(
|
||||
error: err,
|
||||
stackTrace: _,
|
||||
onRefresh: () {
|
||||
// ignore: unused_result
|
||||
ref.refresh(bookInfoProvider(url));
|
||||
},
|
||||
);
|
||||
},
|
||||
loading: () {
|
||||
return Center(
|
||||
@ -143,7 +61,6 @@ class BookInfoPage extends ConsumerWidget {
|
||||
height: 25,
|
||||
child: CircularProgressIndicator(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
strokeCap: StrokeCap.round,
|
||||
),
|
||||
));
|
||||
},
|
||||
@ -181,21 +98,23 @@ class _ActionButtonWidgetState extends ConsumerState<ActionButtonWidget> {
|
||||
} else {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 21, bottom: 21),
|
||||
child: TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).colorScheme.secondary,
|
||||
textStyle: const TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w900,
|
||||
color: Colors.white,
|
||||
)),
|
||||
child: ElevatedButton(
|
||||
// style: ElevatedButton.styleFrom(
|
||||
// // backgroundColor: Theme.of(context).colorScheme.secondary,
|
||||
// textStyle: const TextStyle(
|
||||
// // fontSize: 13,
|
||||
// fontWeight: FontWeight.w500,
|
||||
// // color: Colors.white,
|
||||
// )),
|
||||
onPressed: () async {
|
||||
await downloadFileWidget(ref, context, widget.data);
|
||||
},
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: Text('Add To My Library'),
|
||||
),
|
||||
child: const Text('Add To My Library',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
// color: Colors.white,
|
||||
)),
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -206,7 +125,6 @@ class _ActionButtonWidgetState extends ConsumerState<ActionButtonWidget> {
|
||||
loading: () {
|
||||
return CircularProgressIndicator(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
strokeCap: StrokeCap.round,
|
||||
);
|
||||
},
|
||||
);
|
||||
@ -245,12 +163,9 @@ Future<void> downloadFileWidget(
|
||||
showSnackBar(context: context, message: 'Book has been downloaded!');
|
||||
}
|
||||
},
|
||||
cancelDownlaod: (ChunkedDownloader downloadToken) {
|
||||
cancelDownlaod: (CancelToken downloadToken) {
|
||||
ref.read(cancelCurrentDownload.notifier).state = downloadToken;
|
||||
},
|
||||
mirrorStatus: (val) {
|
||||
ref.read(mirrorStatusProvider.notifier).state = val;
|
||||
},
|
||||
onDownlaodFailed: () {
|
||||
Navigator.of(context).pop();
|
||||
showSnackBar(
|
||||
@ -275,162 +190,38 @@ class _ShowDialog extends ConsumerWidget {
|
||||
final downloadProgress = ref.watch(downloadProgressProvider);
|
||||
final fileSize = ref.watch(getTotalFileSize);
|
||||
final downloadedFileSize = ref.watch(getDownloadedFileSize);
|
||||
final mirrorStatus = ref.watch(mirrorStatusProvider);
|
||||
|
||||
if (downloadProgress == 1.0) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
|
||||
return Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(15.0),
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
height: 285,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: Theme.of(context).colorScheme.tertiaryContainer,
|
||||
),
|
||||
padding: const EdgeInsets.fromLTRB(20, 20, 20, 20),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Text(
|
||||
"Downloading Book",
|
||||
style: TextStyle(
|
||||
fontSize: 19,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).colorScheme.tertiary,
|
||||
decoration: TextDecoration.none),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.tertiary
|
||||
.withAlpha(170),
|
||||
decoration: TextDecoration.none),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
textAlign: TextAlign.start,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
mirrorStatus
|
||||
? const Icon(
|
||||
Icons.check_circle,
|
||||
size: 15,
|
||||
color: Colors.green,
|
||||
)
|
||||
: SizedBox(
|
||||
width: 9,
|
||||
height: 9,
|
||||
child: CircularProgressIndicator(
|
||||
color:
|
||||
Theme.of(context).colorScheme.secondary,
|
||||
strokeWidth: 2.5,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 3,
|
||||
),
|
||||
Text(
|
||||
"Checking mirror availability",
|
||||
style: TextStyle(
|
||||
fontSize: 11.5,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.tertiary
|
||||
.withAlpha(140),
|
||||
decoration: TextDecoration.none),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
textAlign: TextAlign.start,
|
||||
),
|
||||
]),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Text(
|
||||
'$downloadedFileSize/$fileSize',
|
||||
style: TextStyle(
|
||||
fontSize: 9,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
decoration: TextDecoration.none,
|
||||
letterSpacing: 1),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
textAlign: TextAlign.start,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: ClipRRect(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(50)),
|
||||
child: LinearProgressIndicator(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
backgroundColor: Theme.of(context)
|
||||
.colorScheme
|
||||
.tertiary
|
||||
.withAlpha(50),
|
||||
value: downloadProgress,
|
||||
minHeight: 4,
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
backgroundColor:
|
||||
Theme.of(context).colorScheme.secondary,
|
||||
textStyle: const TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w900,
|
||||
color: Colors.white,
|
||||
)),
|
||||
onPressed: () {
|
||||
ref.read(cancelCurrentDownload).stop();
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.all(3.0),
|
||||
child: Text('Cancel'),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
return AlertDialog(
|
||||
title: const Text("Downloading Book"),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(title),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
LinearProgressIndicator(
|
||||
value: downloadProgress,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Text(
|
||||
'$downloadedFileSize/$fileSize',
|
||||
)
|
||||
],
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
ref.read(cancelCurrentDownload).cancel();
|
||||
Navigator.pop(context, 'Cancel');
|
||||
},
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@ -2,18 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:openlib/ui/extensions.dart';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
|
||||
String? getFileType(String? info) {
|
||||
if (info != null && info.isNotEmpty) {
|
||||
info = info.toLowerCase();
|
||||
if (info.contains('pdf')) return "PDF";
|
||||
if (info.contains('epub')) return "Epub";
|
||||
if (info.contains('cbr')) return "Cbr";
|
||||
if (info.contains('cbz')) return "Cbz";
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO: Redesign this widget
|
||||
class BookInfoCard extends StatelessWidget {
|
||||
const BookInfoCard(
|
||||
{Key? key,
|
||||
@ -21,7 +10,6 @@ class BookInfoCard extends StatelessWidget {
|
||||
required this.author,
|
||||
required this.publisher,
|
||||
required this.thumbnail,
|
||||
required this.info,
|
||||
required this.link,
|
||||
required this.onClick})
|
||||
: super(key: key);
|
||||
@ -30,34 +18,34 @@ class BookInfoCard extends StatelessWidget {
|
||||
final String author;
|
||||
final String publisher;
|
||||
final String? thumbnail;
|
||||
final String? info;
|
||||
final String link;
|
||||
final VoidCallback onClick;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String? fileType = getFileType(info);
|
||||
|
||||
return InkWell(
|
||||
onTap: onClick,
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
height: 120,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
color: Theme.of(context).colorScheme.tertiaryContainer,
|
||||
return Card(
|
||||
shadowColor: Colors.black.withOpacity(0),
|
||||
margin: const EdgeInsets.only(bottom: 20),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
side: BorderSide(
|
||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
||||
width: 1,
|
||||
),
|
||||
margin: const EdgeInsets.only(bottom: 10),
|
||||
),
|
||||
child: InkWell(
|
||||
onTap: onClick,
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
CachedNetworkImage(
|
||||
height: 120,
|
||||
width: 90,
|
||||
height: 140,
|
||||
width: 105,
|
||||
imageUrl: thumbnail ?? "",
|
||||
imageBuilder: (context, imageProvider) => Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(5)),
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
image: DecorationImage(
|
||||
image: imageProvider,
|
||||
fit: BoxFit.fill,
|
||||
@ -67,7 +55,7 @@ class BookInfoCard extends StatelessWidget {
|
||||
placeholder: (context, url) => Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
color: "#F8C0C8".toColor(),
|
||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
||||
),
|
||||
height: 120,
|
||||
width: 90,
|
||||
@ -76,7 +64,7 @@ class BookInfoCard extends StatelessWidget {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
color: "#F8C0C8".toColor(),
|
||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
||||
),
|
||||
height: 120,
|
||||
width: 90,
|
||||
@ -88,7 +76,7 @@ class BookInfoCard extends StatelessWidget {
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(5),
|
||||
padding: const EdgeInsets.all(18),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: Column(
|
||||
@ -97,70 +85,38 @@ class BookInfoCard extends StatelessWidget {
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).colorScheme.tertiary,
|
||||
// color: Colors.black,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: Text(
|
||||
publisher,
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
// color: "#4D4D4D".toColor(),
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
publisher,
|
||||
author,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
color:
|
||||
Theme.of(context).textTheme.headlineMedium?.color,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
// color: "#7B7B7B".toColor(),
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
if (fileType != null)
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: "#a5a5a5".toColor(),
|
||||
borderRadius: BorderRadius.circular(2.5),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(3, 2, 3, 2),
|
||||
child: Text(
|
||||
fileType,
|
||||
style: const TextStyle(
|
||||
fontSize: 8.5,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
letterSpacing: 0.5,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (fileType != null)
|
||||
const SizedBox(
|
||||
width: 3,
|
||||
),
|
||||
Expanded(
|
||||
child: Text(
|
||||
author,
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context)
|
||||
.textTheme
|
||||
.headlineSmall
|
||||
?.color,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:openlib/ui/extensions.dart';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
|
||||
class BookInfoWidget extends StatelessWidget {
|
||||
@ -10,14 +11,11 @@ class BookInfoWidget extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String description = data.description.toString().length < 3
|
||||
? "No Description available"
|
||||
: data.description.toString();
|
||||
return SingleChildScrollView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
// physics: const BouncingScrollPhysics(),
|
||||
scrollDirection: Axis.vertical,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 15, right: 15, top: 10),
|
||||
padding: const EdgeInsets.only(left: 20, right: 20, top: 10),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@ -28,8 +26,8 @@ class BookInfoWidget extends StatelessWidget {
|
||||
),
|
||||
Center(
|
||||
child: CachedNetworkImage(
|
||||
height: 230,
|
||||
width: 170,
|
||||
height: 240,
|
||||
width: 180,
|
||||
imageUrl: data.thumbnail,
|
||||
imageBuilder: (context, imageProvider) => Container(
|
||||
decoration: BoxDecoration(
|
||||
@ -45,8 +43,8 @@ class BookInfoWidget extends StatelessWidget {
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
color: Colors.grey,
|
||||
),
|
||||
height: 230,
|
||||
width: 170,
|
||||
height: 240,
|
||||
width: 180,
|
||||
),
|
||||
errorWidget: (context, url, error) {
|
||||
return Container(
|
||||
@ -65,66 +63,70 @@ class BookInfoWidget extends StatelessWidget {
|
||||
),
|
||||
_TopPaddedText(
|
||||
text: data.title,
|
||||
fontSize: 19,
|
||||
fontSize: 22,
|
||||
topPadding: 15,
|
||||
color: Theme.of(context).colorScheme.tertiary,
|
||||
fontWeight: FontWeight.w600,
|
||||
// color: Colors.black,
|
||||
maxLines: 7,
|
||||
),
|
||||
_TopPaddedText(
|
||||
text: data.publisher ?? "unknown",
|
||||
fontSize: 15,
|
||||
fontSize: 17,
|
||||
topPadding: 7,
|
||||
color: Theme.of(context).textTheme.headlineMedium!.color!,
|
||||
color: Theme.of(context).colorScheme.onSecondaryContainer,
|
||||
maxLines: 4,
|
||||
),
|
||||
_TopPaddedText(
|
||||
text: data.author ?? "unknown",
|
||||
fontSize: 13,
|
||||
fontSize: 20,
|
||||
topPadding: 7,
|
||||
color: Theme.of(context).textTheme.headlineSmall!.color!,
|
||||
color: "#7F7F7F".toColor(),
|
||||
maxLines: 3,
|
||||
),
|
||||
_TopPaddedText(
|
||||
text: data.info ?? "",
|
||||
fontSize: 11,
|
||||
topPadding: 9,
|
||||
color: Theme.of(context)
|
||||
.textTheme
|
||||
.headlineSmall!
|
||||
.color!
|
||||
.withAlpha(155),
|
||||
color: "#A9A8A2".toColor(),
|
||||
maxLines: 4,
|
||||
),
|
||||
// child slot of page
|
||||
child,
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Description",
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w900,
|
||||
color: Theme.of(context).colorScheme.tertiary,
|
||||
Card(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Description",
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 15, bottom: 10),
|
||||
child: Text(
|
||||
data.description ?? "",
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 7, bottom: 10),
|
||||
child: Text(
|
||||
description,
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.bold,
|
||||
color:
|
||||
Theme.of(context).colorScheme.tertiary.withAlpha(150),
|
||||
letterSpacing: 1.5,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
@ -137,14 +139,16 @@ class _TopPaddedText extends StatelessWidget {
|
||||
final String text;
|
||||
final double fontSize;
|
||||
final double topPadding;
|
||||
final Color color;
|
||||
final Color? color;
|
||||
final int maxLines;
|
||||
final FontWeight fontWeight;
|
||||
|
||||
const _TopPaddedText(
|
||||
{required this.text,
|
||||
required this.fontSize,
|
||||
required this.topPadding,
|
||||
required this.color,
|
||||
this.color,
|
||||
this.fontWeight = FontWeight.w400,
|
||||
required this.maxLines,
|
||||
Key? key})
|
||||
: super(key: key);
|
||||
@ -157,9 +161,9 @@ class _TopPaddedText extends StatelessWidget {
|
||||
text,
|
||||
style: TextStyle(
|
||||
fontSize: fontSize,
|
||||
fontWeight: FontWeight.w900,
|
||||
fontWeight: fontWeight,
|
||||
color: color,
|
||||
letterSpacing: 0.5,
|
||||
// letterSpacing: 0.5,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: maxLines,
|
||||
|
@ -16,120 +16,28 @@ class ShowDeleteDialog extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(15.0),
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
height: 219,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: Theme.of(context).colorScheme.tertiaryContainer,
|
||||
),
|
||||
padding: const EdgeInsets.fromLTRB(20, 50, 20, 20),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Text(
|
||||
"Delete Book",
|
||||
style: TextStyle(
|
||||
fontSize: 19,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).colorScheme.tertiary,
|
||||
decoration: TextDecoration.none),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Text(
|
||||
"This is permanent and cannot be undone",
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.tertiary
|
||||
.withAlpha(170),
|
||||
decoration: TextDecoration.none),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
textAlign: TextAlign.start,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(
|
||||
style: ButtonStyle(
|
||||
shape: MaterialStateProperty.all(
|
||||
RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(50.0),
|
||||
side: BorderSide(
|
||||
width: 3,
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.secondary),
|
||||
),
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
ref.read(deleteFileFromMyLib(
|
||||
FileName(md5: id, format: format)));
|
||||
Navigator.of(context).pop();
|
||||
// if (false) {
|
||||
// return
|
||||
// }
|
||||
|
||||
showSnackBar(
|
||||
context: context,
|
||||
message: 'Book has been Deleted!');
|
||||
return AlertDialog(
|
||||
title: const Text("Delete Book?"),
|
||||
content: const Text("This is permanent and cannot be undone"),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
ref.read(deleteFileFromMyLib(FileName(md5: id, format: format)));
|
||||
Navigator.of(context).pop();
|
||||
|
||||
onDelete();
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(5.0),
|
||||
child: Text(
|
||||
'Delete',
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).colorScheme.tertiary,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
backgroundColor:
|
||||
Theme.of(context).colorScheme.secondary,
|
||||
textStyle: const TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w900,
|
||||
color: Colors.white,
|
||||
)),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.all(5.0),
|
||||
child: Text('Cancel'),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
showSnackBar(context: context, message: 'Book has been Deleted!');
|
||||
|
||||
onDelete();
|
||||
},
|
||||
child: const Text('Delete'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, 'Cancel'),
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@ -1,14 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:open_file/open_file.dart';
|
||||
|
||||
import 'package:openlib/services/files.dart' show getFilePath;
|
||||
import 'package:openlib/ui/components/snack_bar_widget.dart';
|
||||
import 'package:openlib/ui/components/delete_dialog_widget.dart';
|
||||
import 'package:openlib/ui/epub_viewer.dart' show launchEpubViewer;
|
||||
import 'package:openlib/ui/pdf_viewer.dart' show launchPdfViewer;
|
||||
import 'package:openlib/ui/epub_viewer.dart';
|
||||
import 'package:openlib/ui/pdf_viewer.dart';
|
||||
|
||||
class FileOpenAndDeleteButtons extends ConsumerWidget {
|
||||
class FileOpenAndDeleteButtons extends StatelessWidget {
|
||||
final String id;
|
||||
final String format;
|
||||
final Function onDelete;
|
||||
@ -21,31 +17,40 @@ class FileOpenAndDeleteButtons extends ConsumerWidget {
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 21, bottom: 21),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TextButton(
|
||||
FilledButton(
|
||||
style: TextButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).colorScheme.secondary,
|
||||
textStyle: TextStyle(
|
||||
textStyle: const TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w900,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
color: Colors.white,
|
||||
)),
|
||||
onPressed: () async {
|
||||
if (format == 'pdf') {
|
||||
await launchPdfViewer(
|
||||
fileName: '$id.$format', context: context, ref: ref);
|
||||
} else if (format == 'epub') {
|
||||
await launchEpubViewer(
|
||||
fileName: '$id.$format', context: context, ref: ref);
|
||||
} else {
|
||||
await openCbrAndCbz(fileName: '$id.$format', context: context);
|
||||
}
|
||||
onPressed: () => {
|
||||
if (format == 'pdf')
|
||||
{
|
||||
Navigator.push(context,
|
||||
MaterialPageRoute(builder: (BuildContext context) {
|
||||
return PdfView(
|
||||
fileName: '$id.$format',
|
||||
);
|
||||
}))
|
||||
}
|
||||
else
|
||||
{
|
||||
Navigator.push(context,
|
||||
MaterialPageRoute(builder: (BuildContext context) {
|
||||
return EpubViewerWidget(
|
||||
fileName: '$id.$format',
|
||||
);
|
||||
}))
|
||||
}
|
||||
},
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.fromLTRB(17, 8, 17, 8),
|
||||
@ -55,7 +60,7 @@ class FileOpenAndDeleteButtons extends ConsumerWidget {
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
TextButton(
|
||||
OutlinedButton(
|
||||
style: ButtonStyle(
|
||||
shape: MaterialStateProperty.all(
|
||||
RoundedRectangleBorder(
|
||||
@ -84,7 +89,7 @@ class FileOpenAndDeleteButtons extends ConsumerWidget {
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).colorScheme.tertiary,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -94,16 +99,3 @@ class FileOpenAndDeleteButtons extends ConsumerWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> openCbrAndCbz(
|
||||
{required String fileName, required BuildContext context}) async {
|
||||
try {
|
||||
String path = await getFilePath(fileName);
|
||||
await OpenFile.open(path);
|
||||
} catch (e) {
|
||||
// ignore: avoid_print
|
||||
// print(e);
|
||||
// ignore: use_build_context_synchronously
|
||||
showSnackBar(context: context, message: 'Unable to open pdf!');
|
||||
}
|
||||
}
|
||||
|
@ -7,13 +7,13 @@ class TitleText extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 10, bottom: 7),
|
||||
padding: const EdgeInsets.only(left: 10, right: 10, top: 20, bottom: 10),
|
||||
child: Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
fontSize: 19,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
style: const TextStyle(
|
||||
fontSize: 25,
|
||||
fontWeight: FontWeight.w500,
|
||||
// color: Theme.of(context).colorScheme.onInverseSurface,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -5,14 +5,14 @@ void showSnackBar({required BuildContext context, required String message}) {
|
||||
content: Text(
|
||||
message,
|
||||
style: const TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
fontSize: 12,
|
||||
// fontWeight: FontWeight.bold,
|
||||
// color: Colors.white,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
// ignore: use_build_context_synchronously
|
||||
backgroundColor: Theme.of(context).colorScheme.secondary,
|
||||
// backgroundColor: Theme.of(context).colorScheme.secondary,
|
||||
width: 300,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(50))),
|
||||
|
@ -1,67 +1,10 @@
|
||||
import 'dart:io';
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:epub_view/epub_view.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:vocsy_epub_viewer/epub_viewer.dart';
|
||||
import 'package:open_file/open_file.dart';
|
||||
|
||||
import 'package:openlib/services/files.dart' show getFilePath;
|
||||
import 'package:openlib/ui/components/snack_bar_widget.dart';
|
||||
import 'package:openlib/state/state.dart'
|
||||
show
|
||||
filePathProvider,
|
||||
saveEpubState,
|
||||
dbProvider,
|
||||
getBookPosition,
|
||||
openEpubWithExternalAppProvider;
|
||||
|
||||
Future<void> launchEpubViewer(
|
||||
{required String fileName,
|
||||
required BuildContext context,
|
||||
required WidgetRef ref}) async {
|
||||
if (Platform.isAndroid || Platform.isIOS) {
|
||||
String path = await getFilePath(fileName);
|
||||
String? epubConfig = await ref.read(dbProvider).getBookState(fileName);
|
||||
bool openWithExternalApp = ref.watch(openEpubWithExternalAppProvider);
|
||||
|
||||
if (openWithExternalApp) {
|
||||
await OpenFile.open(path);
|
||||
} else {
|
||||
try {
|
||||
VocsyEpub.setConfig(
|
||||
// ignore: use_build_context_synchronously
|
||||
themeColor: const Color.fromARGB(255, 210, 15, 1),
|
||||
identifier: "iosBook",
|
||||
scrollDirection: EpubScrollDirection.HORIZONTAL,
|
||||
);
|
||||
|
||||
if ((epubConfig?.isNotEmpty ?? true) &&
|
||||
(epubConfig != null) &&
|
||||
(!(epubConfig.startsWith('epubcfi')))) {
|
||||
VocsyEpub.open(path,
|
||||
lastLocation: EpubLocator.fromJson(json.decode(epubConfig)));
|
||||
} else {
|
||||
VocsyEpub.open(path);
|
||||
}
|
||||
|
||||
VocsyEpub.locatorStream.listen((locator) async {
|
||||
await saveEpubState(fileName, locator, ref);
|
||||
// convert locator from string to json and save to your database to be retrieved later
|
||||
});
|
||||
} catch (e) {
|
||||
// ignore: use_build_context_synchronously
|
||||
showSnackBar(context: context, message: 'Unable to open pdf!');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) {
|
||||
return EpubViewerWidget(
|
||||
fileName: fileName,
|
||||
);
|
||||
}));
|
||||
}
|
||||
}
|
||||
show filePathProvider, saveEpubState, getBookPosition;
|
||||
|
||||
class EpubViewerWidget extends ConsumerStatefulWidget {
|
||||
const EpubViewerWidget({super.key, required this.fileName});
|
||||
@ -81,18 +24,18 @@ class _EpubViewState extends ConsumerState<EpubViewerWidget> {
|
||||
}, error: (error, stack) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
// backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
title: const Text("Openlib"),
|
||||
titleTextStyle: Theme.of(context).textTheme.displayLarge,
|
||||
// titleTextStyle: Theme.of(context).textTheme.displayLarge,
|
||||
),
|
||||
body: Center(child: Text(error.toString())),
|
||||
);
|
||||
}, loading: () {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
// backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
title: const Text("Openlib"),
|
||||
titleTextStyle: Theme.of(context).textTheme.displayLarge,
|
||||
// titleTextStyle: Theme.of(context).textTheme.displayLarge,
|
||||
),
|
||||
body: Center(
|
||||
child: SizedBox(
|
||||
@ -151,9 +94,10 @@ class _EpubViewerState extends ConsumerState<EpubViewer> {
|
||||
final position = ref.watch(getBookPosition(widget.fileName));
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
// backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
title: const Text("Openlib"),
|
||||
titleTextStyle: Theme.of(context).textTheme.displayLarge,
|
||||
titleSpacing: 0,
|
||||
// titleTextStyle: Theme.of(context).textTheme.displayLarge,
|
||||
),
|
||||
endDrawer: Drawer(
|
||||
child: EpubViewTableOfContents(controller: _epubReaderController),
|
||||
|
@ -7,17 +7,18 @@ import 'package:openlib/ui/components/book_info_widget.dart';
|
||||
import 'package:openlib/ui/components/file_buttons_widget.dart';
|
||||
|
||||
class BookPage extends StatelessWidget {
|
||||
const BookPage({Key? key, required this.id}) : super(key: key);
|
||||
const BookPage({Key? key, required this.id, required this.title})
|
||||
: super(key: key);
|
||||
|
||||
final String id;
|
||||
final String title;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
title: const Text("Openlib"),
|
||||
titleTextStyle: Theme.of(context).textTheme.displayLarge,
|
||||
title: Text(title),
|
||||
titleSpacing: 0,
|
||||
),
|
||||
body: Consumer(
|
||||
builder: (BuildContext context, WidgetRef ref, _) {
|
||||
@ -42,12 +43,8 @@ class BookPage extends StatelessWidget {
|
||||
);
|
||||
} else {
|
||||
return Center(
|
||||
child: SizedBox(
|
||||
width: 25,
|
||||
height: 25,
|
||||
child: CircularProgressIndicator(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
child: CircularProgressIndicator(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
));
|
||||
}
|
||||
});
|
||||
|
@ -21,7 +21,6 @@ class MyLibraryPage extends ConsumerWidget {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 5, right: 5, top: 10),
|
||||
child: CustomScrollView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
slivers: <Widget>[
|
||||
const SliverToBoxAdapter(
|
||||
child: TitleText("My Library"),
|
||||
@ -35,12 +34,11 @@ class MyLibraryPage extends ConsumerWidget {
|
||||
author: i.author ?? "",
|
||||
publisher: i.publisher ?? "",
|
||||
thumbnail: i.thumbnail,
|
||||
info: i.info,
|
||||
link: i.link,
|
||||
onClick: () {
|
||||
Navigator.push(context, MaterialPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
return BookPage(id: i.id);
|
||||
return BookPage(id: i.id, title: i.title);
|
||||
}));
|
||||
}))
|
||||
.toList()),
|
||||
@ -83,12 +81,8 @@ class MyLibraryPage extends ConsumerWidget {
|
||||
},
|
||||
loading: () {
|
||||
return Center(
|
||||
child: SizedBox(
|
||||
width: 25,
|
||||
height: 25,
|
||||
child: CircularProgressIndicator(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
child: CircularProgressIndicator(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
));
|
||||
},
|
||||
);
|
||||
|
@ -7,31 +7,10 @@ import 'package:openlib/state/state.dart'
|
||||
pdfCurrentPage,
|
||||
totalPdfPage,
|
||||
savePdfState,
|
||||
openPdfWithExternalAppProvider,
|
||||
getBookPosition;
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:open_file/open_file.dart';
|
||||
import 'dart:io' show Platform;
|
||||
|
||||
import 'package:openlib/services/files.dart' show getFilePath;
|
||||
|
||||
Future<void> launchPdfViewer(
|
||||
{required String fileName,
|
||||
required BuildContext context,
|
||||
required WidgetRef ref}) async {
|
||||
bool openWithExternalApp = ref.watch(openPdfWithExternalAppProvider);
|
||||
if (openWithExternalApp) {
|
||||
String path = await getFilePath(fileName);
|
||||
await OpenFile.open(path);
|
||||
} else {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) {
|
||||
return PdfView(
|
||||
fileName: fileName,
|
||||
);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
class PdfView extends ConsumerStatefulWidget {
|
||||
const PdfView({super.key, required this.fileName});
|
||||
|
||||
@ -59,9 +38,9 @@ class _PdfViewState extends ConsumerState<PdfView> {
|
||||
}, loading: () {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
// backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
title: const Text("Openlib"),
|
||||
titleTextStyle: Theme.of(context).textTheme.displayLarge,
|
||||
// titleTextStyle: Theme.of(context).textTheme.displayLarge,
|
||||
),
|
||||
body: Center(
|
||||
child: SizedBox(
|
||||
@ -132,9 +111,10 @@ class _PdfViewerState extends ConsumerState<PdfViewer> {
|
||||
final totalPages = ref.watch(totalPdfPage);
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
// backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
title: const Text("Openlib"),
|
||||
titleTextStyle: Theme.of(context).textTheme.displayLarge,
|
||||
titleSpacing: 0,
|
||||
// titleTextStyle: Theme.of(context).textTheme.displayLarge,
|
||||
actions: isMobile
|
||||
? [
|
||||
IconButton(
|
||||
@ -207,12 +187,8 @@ class _PdfViewerState extends ConsumerState<PdfViewer> {
|
||||
},
|
||||
loading: () {
|
||||
return Center(
|
||||
child: SizedBox(
|
||||
width: 25,
|
||||
height: 25,
|
||||
child: CircularProgressIndicator(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
child: CircularProgressIndicator(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
));
|
||||
},
|
||||
)
|
||||
|
@ -10,7 +10,10 @@ import 'package:openlib/ui/components/book_card_widget.dart';
|
||||
import 'package:openlib/state/state.dart' show searchProvider;
|
||||
|
||||
class ResultPage extends ConsumerWidget {
|
||||
const ResultPage({super.key, required this.searchQuery});
|
||||
const ResultPage({
|
||||
super.key,
|
||||
required this.searchQuery,
|
||||
});
|
||||
|
||||
final String searchQuery;
|
||||
|
||||
@ -20,22 +23,30 @@ class ResultPage extends ConsumerWidget {
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
title: const Text("Openlib"),
|
||||
titleTextStyle: Theme.of(context).textTheme.displayLarge,
|
||||
// backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
title: const Text(
|
||||
"Results",
|
||||
style: TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
titleSpacing: 0,
|
||||
|
||||
// titleTextStyle: Theme.of(context).textTheme.displayLarge,
|
||||
),
|
||||
body: searchBooks.when(
|
||||
data: (data) {
|
||||
if (data.isNotEmpty) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 5, right: 5, top: 10),
|
||||
padding: const EdgeInsets.only(
|
||||
left: 5,
|
||||
right: 5,
|
||||
),
|
||||
child: CustomScrollView(
|
||||
slivers: <Widget>[
|
||||
const SliverToBoxAdapter(
|
||||
child: TitleText("Results"),
|
||||
),
|
||||
// const SliverToBoxAdapter(
|
||||
// child: TitleText("Results"),
|
||||
// ),
|
||||
SliverPadding(
|
||||
padding: const EdgeInsets.only(left: 5, right: 5, top: 10),
|
||||
padding: const EdgeInsets.only(left: 5, right: 5, top: 15),
|
||||
sliver: SliverList(
|
||||
delegate: SliverChildListDelegate(data
|
||||
.map((i) => BookInfoCard(
|
||||
@ -43,12 +54,12 @@ class ResultPage extends ConsumerWidget {
|
||||
author: i.author ?? "unknown",
|
||||
publisher: i.publisher ?? "unknown",
|
||||
thumbnail: i.thumbnail!,
|
||||
info: i.info,
|
||||
link: i.link,
|
||||
onClick: () {
|
||||
Navigator.push(context, MaterialPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
return BookInfoPage(url: i.link);
|
||||
return BookInfoPage(
|
||||
url: i.link, title: i.title);
|
||||
}));
|
||||
},
|
||||
))
|
||||
@ -101,23 +112,14 @@ class ResultPage extends ConsumerWidget {
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(left: 5, right: 5, top: 10),
|
||||
child: TitleText("Results"),
|
||||
),
|
||||
Expanded(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Center(
|
||||
child: SizedBox(
|
||||
width: 25,
|
||||
height: 25,
|
||||
child: CircularProgressIndicator(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
strokeCap: StrokeCap.round,
|
||||
),
|
||||
child: CircularProgressIndicator(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
))
|
||||
],
|
||||
),
|
||||
|
@ -7,33 +7,24 @@ import 'package:openlib/state/state.dart'
|
||||
searchQueryProvider,
|
||||
selectedTypeState,
|
||||
selectedSortState,
|
||||
selectedFileTypeState,
|
||||
typeValues,
|
||||
fileType,
|
||||
sortValues,
|
||||
enableFiltersState;
|
||||
import 'components/snack_bar_widget.dart';
|
||||
sortValues;
|
||||
|
||||
class SearchPage extends ConsumerWidget {
|
||||
const SearchPage({Key? key}) : super(key: key);
|
||||
|
||||
void onSubmit(BuildContext context, WidgetRef ref) {
|
||||
if (ref.read(searchQueryProvider).isNotEmpty) {
|
||||
ref.read(enableFiltersState.notifier).state = true;
|
||||
Navigator.push(context,
|
||||
MaterialPageRoute(builder: (BuildContext context) {
|
||||
return ResultPage(searchQuery: ref.read(searchQueryProvider));
|
||||
}));
|
||||
} else {
|
||||
showSnackBar(context: context, message: 'Search field is empty');
|
||||
}
|
||||
Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) {
|
||||
return ResultPage(
|
||||
searchQuery: ref.read(searchQueryProvider),
|
||||
);
|
||||
}));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final dropdownTypeValue = ref.watch(selectedTypeState);
|
||||
final dropdownSortValue = ref.watch(selectedSortState);
|
||||
final dropDownFileTypeValue = ref.watch(selectedFileTypeState);
|
||||
|
||||
return SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
@ -48,21 +39,23 @@ class SearchPage extends ConsumerWidget {
|
||||
padding: const EdgeInsets.only(left: 7, right: 7, top: 10),
|
||||
child: TextField(
|
||||
showCursor: true,
|
||||
cursorColor: Theme.of(context).colorScheme.secondary,
|
||||
// cursorColor: Theme.of(context).colorScheme.secondary,
|
||||
|
||||
decoration: InputDecoration(
|
||||
enabledBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.grey, width: 2),
|
||||
borderRadius: BorderRadius.all(Radius.circular(50)),
|
||||
label: const Text("Search"),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.outline, width: 2),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(50)),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.tertiary,
|
||||
width: 2),
|
||||
color: Theme.of(context).colorScheme.outline, width: 2),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(50)),
|
||||
),
|
||||
suffixIcon: IconButton(
|
||||
padding: const EdgeInsets.only(right: 5),
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
padding: const EdgeInsets.only(right: 10),
|
||||
// color: Theme.of(context).colorScheme.secondary,
|
||||
icon: const Icon(
|
||||
Icons.search,
|
||||
size: 23,
|
||||
@ -70,16 +63,16 @@ class SearchPage extends ConsumerWidget {
|
||||
onPressed: () => onSubmit(context, ref),
|
||||
),
|
||||
filled: true,
|
||||
hintStyle: const TextStyle(
|
||||
color: Colors.grey, fontWeight: FontWeight.bold),
|
||||
hintText: "Search",
|
||||
fillColor: Theme.of(context).colorScheme.primary,
|
||||
// hintStyle: const TextStyle(
|
||||
// color: Colors.grey, fontWeight: FontWeight.bold),
|
||||
hintText: "Search books ,author or ISBN",
|
||||
// fillColor: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
onSubmitted: (String value) => onSubmit(context, ref),
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.tertiary,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 12,
|
||||
style: const TextStyle(
|
||||
// color: Theme.of(context).colorScheme.tertiary,
|
||||
// fontWeight: FontWeight.bold,
|
||||
fontSize: 14,
|
||||
),
|
||||
onChanged: (String value) {
|
||||
ref.read(searchQueryProvider.notifier).state = value;
|
||||
@ -94,17 +87,19 @@ class SearchPage extends ConsumerWidget {
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Type',
|
||||
labelStyle: TextStyle(
|
||||
fontSize: 11,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
enabledBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.grey, width: 2),
|
||||
borderRadius: BorderRadius.all(Radius.circular(50)),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
width: 2),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(50)),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.tertiary,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
width: 2),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(50)),
|
||||
),
|
||||
@ -118,8 +113,7 @@ class SearchPage extends ConsumerWidget {
|
||||
value: value,
|
||||
child: Text(
|
||||
value,
|
||||
style: const TextStyle(
|
||||
fontSize: 12, fontWeight: FontWeight.bold),
|
||||
style: const TextStyle(fontSize: 14),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
@ -137,17 +131,19 @@ class SearchPage extends ConsumerWidget {
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Sort by',
|
||||
labelStyle: TextStyle(
|
||||
fontSize: 11,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
enabledBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.grey, width: 2),
|
||||
borderRadius: BorderRadius.all(Radius.circular(50)),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
width: 2),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(50)),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.tertiary,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
width: 2),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(50)),
|
||||
),
|
||||
@ -160,8 +156,7 @@ class SearchPage extends ConsumerWidget {
|
||||
value: value,
|
||||
child: Text(
|
||||
value,
|
||||
style: const TextStyle(
|
||||
fontSize: 12, fontWeight: FontWeight.bold),
|
||||
style: const TextStyle(fontSize: 14),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
@ -171,47 +166,6 @@ class SearchPage extends ConsumerWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 7, right: 7, top: 19),
|
||||
child: SizedBox(
|
||||
width: 165,
|
||||
child: DropdownButtonFormField(
|
||||
decoration: InputDecoration(
|
||||
labelText: 'File type',
|
||||
labelStyle: TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
enabledBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.grey, width: 2),
|
||||
borderRadius: BorderRadius.all(Radius.circular(50)),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.tertiary,
|
||||
width: 2),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(50)),
|
||||
),
|
||||
),
|
||||
value: dropDownFileTypeValue,
|
||||
items: fileType.map<DropdownMenuItem<String>>((String value) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: value,
|
||||
child: Text(
|
||||
value,
|
||||
style: const TextStyle(
|
||||
fontSize: 12, fontWeight: FontWeight.bold),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
onChanged: (String? val) {
|
||||
ref.read(selectedFileTypeState.notifier).state =
|
||||
val ?? 'All';
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -1,163 +0,0 @@
|
||||
import 'dart:io';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:openlib/ui/components/page_title_widget.dart';
|
||||
import 'package:openlib/ui/about_page.dart';
|
||||
import 'package:openlib/state/state.dart'
|
||||
show
|
||||
themeModeProvider,
|
||||
openPdfWithExternalAppProvider,
|
||||
openEpubWithExternalAppProvider,
|
||||
dbProvider;
|
||||
|
||||
class SettingsPage extends ConsumerWidget {
|
||||
const SettingsPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 5, right: 5, top: 10),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const TitleText("Settings"),
|
||||
_PaddedContainer(
|
||||
children: [
|
||||
Text(
|
||||
"Dark Mode",
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).colorScheme.tertiary,
|
||||
),
|
||||
),
|
||||
Switch(
|
||||
// This bool value toggles the switch.
|
||||
value: ref.watch(themeModeProvider) == ThemeMode.dark,
|
||||
activeColor: Colors.red,
|
||||
onChanged: (bool value) {
|
||||
ref.read(themeModeProvider.notifier).state =
|
||||
value == true ? ThemeMode.dark : ThemeMode.light;
|
||||
ref.read(dbProvider).savePreference('darkMode', value);
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
|
||||
systemNavigationBarColor:
|
||||
value ? Colors.black : Colors.grey.shade200));
|
||||
}
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
_PaddedContainer(
|
||||
children: [
|
||||
Text(
|
||||
"Open PDF with External Reader",
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).colorScheme.tertiary,
|
||||
),
|
||||
),
|
||||
Switch(
|
||||
// This bool value toggles the switch.
|
||||
value: ref.watch(openPdfWithExternalAppProvider),
|
||||
activeColor: Colors.red,
|
||||
onChanged: (bool value) {
|
||||
ref.read(openPdfWithExternalAppProvider.notifier).state =
|
||||
value;
|
||||
ref
|
||||
.read(dbProvider)
|
||||
.savePreference('openPdfwithExternalApp', value);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
_PaddedContainer(
|
||||
children: [
|
||||
Text(
|
||||
"Open Epub with External Reader",
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).colorScheme.tertiary,
|
||||
),
|
||||
),
|
||||
Switch(
|
||||
// This bool value toggles the switch.
|
||||
value: ref.watch(
|
||||
openEpubWithExternalAppProvider,
|
||||
),
|
||||
activeColor: Colors.red,
|
||||
onChanged: (bool value) {
|
||||
ref.read(openEpubWithExternalAppProvider.notifier).state =
|
||||
value;
|
||||
ref
|
||||
.read(dbProvider)
|
||||
.savePreference('openEpubwithExternalApp', value);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
_PaddedContainer(
|
||||
onClick: () {
|
||||
Navigator.push(context,
|
||||
MaterialPageRoute(builder: (BuildContext context) {
|
||||
return const AboutPage();
|
||||
}));
|
||||
},
|
||||
children: [
|
||||
Text(
|
||||
"About",
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).colorScheme.tertiary,
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PaddedContainer extends StatelessWidget {
|
||||
const _PaddedContainer({Key? key, this.onClick, required this.children})
|
||||
: super(key: key);
|
||||
|
||||
final VoidCallback? onClick;
|
||||
final List<Widget> children;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 5, right: 5, top: 10),
|
||||
child: InkWell(
|
||||
onTap: onClick,
|
||||
child: Container(
|
||||
height: 61,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
color: Theme.of(context).colorScheme.tertiaryContainer,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: children,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:openlib/ui/extensions.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
|
||||
ThemeData lightTheme = ThemeData(
|
||||
primaryColor: Colors.white,
|
||||
colorScheme: ColorScheme.light(
|
||||
primary: Colors.white,
|
||||
secondary: '#FB0101'.toColor(),
|
||||
tertiary: Colors.black,
|
||||
tertiaryContainer: '#F2F2F2'.toColor(),
|
||||
),
|
||||
textTheme: TextTheme(
|
||||
displayLarge: const TextStyle(
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 21,
|
||||
),
|
||||
displayMedium: const TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
headlineMedium: TextStyle(
|
||||
color: "#595E60".toColor(),
|
||||
),
|
||||
headlineSmall: TextStyle(
|
||||
color: "#7F7F7F".toColor(),
|
||||
)),
|
||||
fontFamily: GoogleFonts.nunito().fontFamily,
|
||||
useMaterial3: true,
|
||||
textSelectionTheme: TextSelectionThemeData(
|
||||
selectionColor: '#FB0101'.toColor(),
|
||||
selectionHandleColor: '#FB0101'.toColor(),
|
||||
),
|
||||
);
|
||||
|
||||
ThemeData darkTheme = ThemeData(
|
||||
primaryColor: Colors.black,
|
||||
colorScheme: ColorScheme.dark(
|
||||
primary: Colors.black,
|
||||
secondary: '#FB0101'.toColor(),
|
||||
tertiary: Colors.white,
|
||||
tertiaryContainer: '#2B2B2B'.toColor(),
|
||||
),
|
||||
textTheme: TextTheme(
|
||||
displayLarge: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 21,
|
||||
),
|
||||
displayMedium: const TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
headlineMedium: TextStyle(
|
||||
color: "#F5F5F5".toColor(),
|
||||
),
|
||||
headlineSmall: TextStyle(
|
||||
color: "#E8E2E2".toColor(),
|
||||
),
|
||||
),
|
||||
fontFamily: GoogleFonts.nunito().fontFamily,
|
||||
useMaterial3: true,
|
||||
textSelectionTheme: TextSelectionThemeData(
|
||||
selectionColor: '#FB0101'.toColor(),
|
||||
selectionHandleColor: '#FB0101'.toColor(),
|
||||
),
|
||||
);
|
@ -6,8 +6,7 @@ import 'extensions.dart';
|
||||
import 'package:openlib/ui/components/page_title_widget.dart';
|
||||
import 'package:openlib/ui/components/error_widget.dart';
|
||||
import 'package:openlib/ui/results_page.dart';
|
||||
import 'package:openlib/state/state.dart'
|
||||
show getTrendingBooks, enableFiltersState;
|
||||
import 'package:openlib/state/state.dart' show getTrendingBooks;
|
||||
|
||||
class TrendingPage extends ConsumerWidget {
|
||||
const TrendingPage({super.key});
|
||||
@ -18,134 +17,122 @@ class TrendingPage extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final trendingBooks = ref.watch(getTrendingBooks);
|
||||
return trendingBooks.when(
|
||||
skipLoadingOnRefresh: false,
|
||||
data: (data) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 5, right: 5, top: 10),
|
||||
child: CustomScrollView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
slivers: [
|
||||
const SliverToBoxAdapter(
|
||||
child: TitleText("Trending"),
|
||||
return trendingBooks.when(data: (data) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 5, right: 5, top: 0),
|
||||
child: CustomScrollView(
|
||||
slivers: [
|
||||
const SliverToBoxAdapter(
|
||||
child: TitleText("Trending"),
|
||||
),
|
||||
SliverPadding(
|
||||
padding: const EdgeInsets.all(5),
|
||||
sliver: SliverGrid(
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 3,
|
||||
mainAxisSpacing: 10.0,
|
||||
crossAxisSpacing: 13.0,
|
||||
mainAxisExtent: 205,
|
||||
),
|
||||
SliverPadding(
|
||||
padding: const EdgeInsets.all(5),
|
||||
sliver: SliverGrid(
|
||||
gridDelegate:
|
||||
const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 3,
|
||||
mainAxisSpacing: 10.0,
|
||||
crossAxisSpacing: 13.0,
|
||||
mainAxisExtent: 205,
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(BuildContext context, int index) {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
ref.read(enableFiltersState.notifier).state = false;
|
||||
Navigator.push(context, MaterialPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
return ResultPage(
|
||||
searchQuery: data[index].title!);
|
||||
}));
|
||||
},
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
CachedNetworkImage(
|
||||
height: imageHeight,
|
||||
width: imageWidth,
|
||||
imageUrl: data[index].thumbnail!,
|
||||
imageBuilder: (context, imageProvider) =>
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: Colors.grey,
|
||||
spreadRadius: 0.1,
|
||||
blurRadius: 1)
|
||||
],
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(5)),
|
||||
image: DecorationImage(
|
||||
image: imageProvider,
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
),
|
||||
),
|
||||
placeholder: (context, url) => Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
color: "#E3E8E9".toColor(),
|
||||
),
|
||||
height: imageHeight,
|
||||
width: imageWidth,
|
||||
),
|
||||
errorWidget: (context, url, error) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius:
|
||||
BorderRadius.circular(5),
|
||||
color: Colors.grey,
|
||||
),
|
||||
height: imageHeight,
|
||||
width: imageWidth,
|
||||
child: const Center(
|
||||
child: Icon(Icons.image_rounded),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 4),
|
||||
child: SizedBox(
|
||||
width: imageWidth,
|
||||
child: Text(
|
||||
data[index].title!,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.displayMedium,
|
||||
maxLines: 2,
|
||||
),
|
||||
),
|
||||
),
|
||||
]),
|
||||
),
|
||||
);
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(BuildContext context, int index) {
|
||||
return InkWell(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
onTap: () {
|
||||
Navigator.push(context,
|
||||
MaterialPageRoute(builder: (BuildContext context) {
|
||||
return ResultPage(
|
||||
searchQuery: data[index].title!,
|
||||
);
|
||||
}));
|
||||
},
|
||||
childCount: data.length,
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
CachedNetworkImage(
|
||||
height: imageHeight,
|
||||
width: imageWidth,
|
||||
imageUrl: data[index].thumbnail!,
|
||||
imageBuilder: (context, imageProvider) =>
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(5)),
|
||||
image: DecorationImage(
|
||||
image: imageProvider,
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
),
|
||||
),
|
||||
placeholder: (context, url) => Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
// color: "#E3E8E9".toColor(),
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.surfaceVariant,
|
||||
),
|
||||
height: imageHeight,
|
||||
width: imageWidth,
|
||||
),
|
||||
errorWidget: (context, url, error) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.surfaceVariant,
|
||||
),
|
||||
height: imageHeight,
|
||||
width: imageWidth,
|
||||
child: const Center(
|
||||
child: Icon(Icons.image_rounded),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
child: SizedBox(
|
||||
width: imageWidth,
|
||||
child: Text(
|
||||
data[index].title!,
|
||||
style: const TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w400),
|
||||
maxLines: 2,
|
||||
),
|
||||
),
|
||||
),
|
||||
]),
|
||||
);
|
||||
},
|
||||
childCount: data.length,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
],
|
||||
),
|
||||
);
|
||||
}, error: (error, _) {
|
||||
return CustomErrorWidget(
|
||||
error: error,
|
||||
stackTrace: _,
|
||||
onRefresh: () {
|
||||
// ignore: unused_result
|
||||
ref.refresh(getTrendingBooks);
|
||||
},
|
||||
error: (error, _) {
|
||||
return CustomErrorWidget(
|
||||
error: error,
|
||||
stackTrace: _,
|
||||
onRefresh: () {
|
||||
// ignore: unused_result
|
||||
ref.refresh(getTrendingBooks);
|
||||
},
|
||||
);
|
||||
},
|
||||
loading: () {
|
||||
return Center(
|
||||
child: SizedBox(
|
||||
width: 25,
|
||||
height: 25,
|
||||
child: CircularProgressIndicator(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
strokeCap: StrokeCap.round,
|
||||
);
|
||||
}, loading: () {
|
||||
return const Center(
|
||||
child: SizedBox(
|
||||
width: 25,
|
||||
height: 25,
|
||||
child: CircularProgressIndicator(
|
||||
// color: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
));
|
||||
});
|
||||
));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,75 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
import 'package:webview_cookie_manager/webview_cookie_manager.dart'
|
||||
as cookiejar;
|
||||
|
||||
import 'package:openlib/state/state.dart'
|
||||
show cookieProvider, userAgentProvider, dbProvider, bookInfoProvider;
|
||||
|
||||
class Webview extends ConsumerStatefulWidget {
|
||||
const Webview({Key? key, required this.url}) : super(key: key);
|
||||
final String url;
|
||||
@override
|
||||
// ignore: library_private_types_in_public_api
|
||||
_WebviewState createState() => _WebviewState();
|
||||
}
|
||||
|
||||
class _WebviewState extends ConsumerState<Webview> {
|
||||
WebViewController controller = WebViewController();
|
||||
|
||||
final cookieManager = cookiejar.WebviewCookieManager();
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
title: const Text("Solve Captcha"),
|
||||
),
|
||||
body: SafeArea(
|
||||
child: WebViewWidget(
|
||||
controller: controller
|
||||
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
||||
..setBackgroundColor(const Color(0x00000000))
|
||||
..loadRequest(Uri.parse(widget.url))
|
||||
..getUserAgent().then((value) {
|
||||
ref.read(userAgentProvider.notifier).state = value!;
|
||||
ref.read(dbProvider).setBrowserOptions('userAgent', value);
|
||||
})
|
||||
..setNavigationDelegate(NavigationDelegate(
|
||||
onPageStarted: (url) async {
|
||||
var urlStatusCode = await controller.runJavaScriptReturningResult(
|
||||
"var xhr = new XMLHttpRequest();xhr.open('GET', window.location.href, false);xhr.send(null);xhr.status;");
|
||||
|
||||
if (urlStatusCode.toString().contains('200')) {
|
||||
final cookies = await cookieManager
|
||||
.getCookies("https://annas-archive.org");
|
||||
|
||||
List<String> cookie = [];
|
||||
for (var element in cookies) {
|
||||
if (element.name == 'cf_clearance' ||
|
||||
element.name == 'cf_chl_2') {
|
||||
cookie.add(element.toString().split(';')[0]);
|
||||
}
|
||||
}
|
||||
|
||||
String cfClearance = cookie.join('; ');
|
||||
|
||||
ref.read(cookieProvider.notifier).state = cfClearance;
|
||||
|
||||
await ref
|
||||
.read(dbProvider)
|
||||
.setBrowserOptions('cookie', cfClearance);
|
||||
|
||||
ref.invalidate(bookInfoProvider);
|
||||
|
||||
// ignore: use_build_context_synchronously
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -6,9 +6,13 @@
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <dynamic_color/dynamic_color_plugin.h>
|
||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||
|
||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
g_autoptr(FlPluginRegistrar) dynamic_color_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "DynamicColorPlugin");
|
||||
dynamic_color_plugin_register_with_registrar(dynamic_color_registrar);
|
||||
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
||||
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
||||
|
@ -3,6 +3,7 @@
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
dynamic_color
|
||||
url_launcher_linux
|
||||
)
|
||||
|
||||
|
@ -5,11 +5,13 @@
|
||||
import FlutterMacOS
|
||||
import Foundation
|
||||
|
||||
import dynamic_color
|
||||
import path_provider_foundation
|
||||
import sqflite
|
||||
import url_launcher_macos
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin"))
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||
|
268
pubspec.lock
268
pubspec.lock
@ -5,10 +5,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: archive
|
||||
sha256: "49b1fad315e57ab0bbc15bcbb874e83116a1d78f77ebd500a4af6c9407d6b28e"
|
||||
sha256: "0c8368c9b3f0abbc193b9d6133649a614204b528982bebc7026372d61677ce3a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.3.8"
|
||||
version: "3.3.7"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -73,14 +73,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -101,10 +93,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687
|
||||
sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.17.2"
|
||||
version: "1.17.1"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -133,10 +125,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: cupertino_icons
|
||||
sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d
|
||||
sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.6"
|
||||
version: "1.0.5"
|
||||
dev:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -149,10 +141,18 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: dio
|
||||
sha256: ce75a1b40947fea0a0e16ce73337122a86762e38b982e1ccb909daa3b9bc4197
|
||||
sha256: "3866d67f93523161b643187af65f5ac08bc991a5bcdaf41a2d587fe4ccb49993"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.3.2"
|
||||
version: "5.3.0"
|
||||
dynamic_color:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: dynamic_color
|
||||
sha256: de4798a7069121aee12d5895315680258415de9b00e717723a1bd73d58f0126d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.6.6"
|
||||
epub_view:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -189,10 +189,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878"
|
||||
sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
version: "2.0.2"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -242,10 +242,10 @@ packages:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_lints
|
||||
sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04
|
||||
sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.3"
|
||||
version: "2.0.2"
|
||||
flutter_pdfview:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -258,10 +258,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_riverpod
|
||||
sha256: b04d4e9435a563673746ccb328d22018c6c9496bb547e11dd56c1b0cc9829fe5
|
||||
sha256: b83ac5827baadefd331ea1d85110f34645827ea234ccabf53a655f41901a9bf4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.10"
|
||||
version: "2.3.6"
|
||||
flutter_svg:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -292,10 +292,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: google_fonts
|
||||
sha256: "6b6f10f0ce3c42f6552d1c70d2c28d764cf22bb487f50f66cca31dcd5194f4d6"
|
||||
sha256: e20ff62b158b96f392bfc8afe29dee1503c94fbea2cbe8186fd59b756b8ae982
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.4"
|
||||
version: "5.1.0"
|
||||
google_nav_bar:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -316,10 +316,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http
|
||||
sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2"
|
||||
sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.13.6"
|
||||
version: "1.1.0"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -372,18 +372,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
|
||||
sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.16"
|
||||
version: "0.12.15"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
|
||||
sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.0"
|
||||
version: "0.2.0"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -400,14 +400,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
open_file:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: open_file
|
||||
sha256: a5a32d44acb7c899987d0999e1e3cbb0a0f1adebbf41ac813ec6d2d8faa0af20
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.3.2"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -428,90 +420,50 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: path_provider
|
||||
sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa
|
||||
sha256: "3087813781ab814e4157b172f1a11c46be20179fcc9bea043e0fba36bc0acaa2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
version: "2.0.15"
|
||||
path_provider_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_android
|
||||
sha256: "6b8b19bd80da4f11ce91b2d1fb931f3006911477cec227cce23d3253d80df3f1"
|
||||
sha256: "2cec049d282c7f13c594b4a73976b0b4f2d7a1838a6dd5aaf7bd9719196bee86"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
version: "2.0.27"
|
||||
path_provider_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_foundation
|
||||
sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d"
|
||||
sha256: "916731ccbdce44d545414dd9961f26ba5fbaa74bcbb55237d8e65a623a8c7297"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.1"
|
||||
version: "2.2.4"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_linux
|
||||
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
|
||||
sha256: ffbb8cc9ed2c9ec0e4b7a541e56fd79b138e8f47d2fb86815f15358a349b3b57
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
version: "2.1.11"
|
||||
path_provider_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_platform_interface
|
||||
sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c"
|
||||
sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
version: "2.0.6"
|
||||
path_provider_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_windows
|
||||
sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
|
||||
sha256: "1cb68ba4cd3a795033de62ba1b7b4564dace301f952de6bfb3cd91b202b6ee96"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
permission_handler:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: permission_handler
|
||||
sha256: "63e5216aae014a72fe9579ccd027323395ce7a98271d9defa9d57320d001af81"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.4.3"
|
||||
permission_handler_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_android
|
||||
sha256: d74e77a5ecd38649905db0a7d05ef16bed42ff263b9efb73ed794317c5764ec3
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.3.4"
|
||||
permission_handler_apple:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_apple
|
||||
sha256: "99e220bce3f8877c78e4ace901082fb29fa1b4ebde529ad0932d8d664b34f3f5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.1.4"
|
||||
permission_handler_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_platform_interface
|
||||
sha256: "7c6b1500385dd1d2ca61bb89e2488ca178e274a69144d26bbd65e33eae7c02a9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.11.3"
|
||||
permission_handler_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_windows
|
||||
sha256: cc074aace208760f1eee6aa4fae766b45d947df85bc831cde77009cdb4720098
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.3"
|
||||
version: "2.1.7"
|
||||
petitparser:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -524,18 +476,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102
|
||||
sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
version: "3.1.0"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: plugin_platform_interface
|
||||
sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d
|
||||
sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.6"
|
||||
version: "2.1.4"
|
||||
pointycastle:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -556,10 +508,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: riverpod
|
||||
sha256: "6c0a2c30c04206ac05494bcccd8148b76866e1a9248a5a8c84ca7b16fbcb3f6a"
|
||||
sha256: "80e48bebc83010d5e67a11c9514af6b44bbac1ec77b4333c8ea65dbc79e2d8ef"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.10"
|
||||
version: "2.3.6"
|
||||
rxdart:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -585,18 +537,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
|
||||
sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.0"
|
||||
sprintf:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sprintf
|
||||
sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.0"
|
||||
version: "1.9.1"
|
||||
sqflite:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -641,10 +585,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: state_notifier
|
||||
sha256: b8677376aa54f2d7c58280d5a007f9e8774f1968d1fb1c096adcb4792fba29bb
|
||||
sha256: "8fe42610f179b843b12371e40db58c9444f8757f8b69d181c97e50787caed289"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
version: "0.7.2+1"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -681,10 +625,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8"
|
||||
sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.0"
|
||||
version: "0.5.1"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -705,74 +649,74 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: url_launcher
|
||||
sha256: "47e208a6711459d813ba18af120d9663c20bdf6985d6ad39fe165d2538378d27"
|
||||
sha256: "781bd58a1eb16069412365c98597726cd8810ae27435f04b3b4d3a470bacd61e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.14"
|
||||
version: "6.1.12"
|
||||
url_launcher_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_android
|
||||
sha256: b04af59516ab45762b2ca6da40fa830d72d0f6045cd97744450b73493fa76330
|
||||
sha256: "3dd2388cc0c42912eee04434531a26a82512b9cb1827e0214430c9bcbddfe025"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.0"
|
||||
version: "6.0.38"
|
||||
url_launcher_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_ios
|
||||
sha256: "7c65021d5dee51813d652357bc65b8dd4a6177082a9966bc8ba6ee477baa795f"
|
||||
sha256: "9af7ea73259886b92199f9e42c116072f05ff9bea2dcb339ab935dfc957392c2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.5"
|
||||
version: "6.1.4"
|
||||
url_launcher_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_linux
|
||||
sha256: b651aad005e0cb06a01dbd84b428a301916dc75f0e7ea6165f80057fee2d8e8e
|
||||
sha256: "207f4ddda99b95b4d4868320a352d374b0b7e05eefad95a4a26f57da413443f5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.6"
|
||||
version: "3.0.5"
|
||||
url_launcher_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_macos
|
||||
sha256: b55486791f666e62e0e8ff825e58a023fd6b1f71c49926483f1128d3bbd8fe88
|
||||
sha256: "1c4fdc0bfea61a70792ce97157e5cc17260f61abbe4f39354513f39ec6fd73b1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.7"
|
||||
version: "3.0.6"
|
||||
url_launcher_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_platform_interface
|
||||
sha256: "95465b39f83bfe95fcb9d174829d6476216f2d548b79c38ab2506e0458787618"
|
||||
sha256: bfdfa402f1f3298637d71ca8ecfe840b4696698213d5346e9d12d4ab647ee2ea
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.5"
|
||||
version: "2.1.3"
|
||||
url_launcher_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_web
|
||||
sha256: "2942294a500b4fa0b918685aff406773ba0a4cd34b7f42198742a94083020ce5"
|
||||
sha256: cc26720eefe98c1b71d85f9dc7ef0cada5132617046369d9dc296b3ecaa5cbb4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.20"
|
||||
version: "2.0.18"
|
||||
url_launcher_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_windows
|
||||
sha256: "95fef3129dc7cfaba2bc3d5ba2e16063bb561fc6d78e63eee16162bc70029069"
|
||||
sha256: "7967065dd2b5fccc18c653b97958fdf839c5478c28e767c61ee879f4e7882422"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.8"
|
||||
version: "3.0.7"
|
||||
uuid:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: uuid
|
||||
sha256: e03928880bdbcbf496fb415573f5ab7b1ea99b9b04f669c01104d085893c3134
|
||||
sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
version: "3.0.7"
|
||||
vector_graphics:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -805,78 +749,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
vocsy_epub_viewer:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: vocsy_epub_viewer
|
||||
sha256: "30aab4a5be22e8c98f22fed3345f6989caaca861178c38cab2a8b8cd66ad53de"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.4-beta"
|
||||
webview_cookie_manager:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: webview_cookie_manager
|
||||
sha256: "425a9feac5cd2cb62a71da3dda5ac2eaf9ece5481ee8d79f3868dc5ba8223ad3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.6"
|
||||
webview_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: webview_flutter
|
||||
sha256: c1ab9b81090705c6069197d9fdc1625e587b52b8d70cdde2339d177ad0dbb98e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.4.1"
|
||||
webview_flutter_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter_android
|
||||
sha256: b0cd33dd7d3dd8e5f664e11a19e17ba12c352647269921a3b568406b001f1dff
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.12.0"
|
||||
webview_flutter_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter_platform_interface
|
||||
sha256: "6d9213c65f1060116757a7c473247c60f3f7f332cac33dc417c9e362a9a13e4f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.6.0"
|
||||
webview_flutter_wkwebview:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter_wkwebview
|
||||
sha256: "30b9af6bdd457b44c08748b9190d23208b5165357cc2eb57914fee1366c42974"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.9.1"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: "9e82a402b7f3d518fb9c02d0e9ae45952df31b9bf34d77baf19da2de03fc2aaa"
|
||||
sha256: dfdf0136e0aa7a1b474ea133e67cb0154a0acd2599c4f3ada3b49d38d38793ee
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.7"
|
||||
version: "5.0.5"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xdg_directories
|
||||
sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2"
|
||||
sha256: e0b1147eec179d3911f1f19b59206448f78195ca1d20514134e10641b7d7fbff
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.3"
|
||||
version: "1.0.1"
|
||||
xml:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -894,5 +782,5 @@ packages:
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
sdks:
|
||||
dart: ">=3.1.0 <4.0.0"
|
||||
flutter: ">=3.13.0"
|
||||
dart: ">=3.0.5 <4.0.0"
|
||||
flutter: ">=3.10.0"
|
||||
|
13
pubspec.yaml
13
pubspec.yaml
@ -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
|
||||
# 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.
|
||||
version: 1.0.3+6
|
||||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
sdk: '>=3.0.5 <4.0.0'
|
||||
@ -36,7 +36,6 @@ dependencies:
|
||||
|
||||
epub_view: ^3.2.0
|
||||
flutter_pdfview: ^1.2.7
|
||||
vocsy_epub_viewer: ^2.0.0
|
||||
# syncfusion_flutter_pdfviewer: ^22.2.5
|
||||
# pdfx: ^2.4.0
|
||||
|
||||
@ -45,16 +44,11 @@ dependencies:
|
||||
|
||||
sqflite: ^2.3.0
|
||||
path_provider: ^2.0.15
|
||||
permission_handler: ^10.4.3
|
||||
open_file: ^3.3.2
|
||||
webview_flutter: ^4.4.1
|
||||
webview_cookie_manager: ^2.0.6
|
||||
|
||||
flutter_svg: ^2.0.7
|
||||
google_fonts:
|
||||
google_fonts: ^5.1.0
|
||||
|
||||
cached_network_image: ^3.2.3
|
||||
chunked_downloader: ^0.0.2
|
||||
|
||||
sqflite_common_ffi: ^2.3.0+2
|
||||
url_launcher: ^6.1.12
|
||||
@ -62,6 +56,7 @@ dependencies:
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
cupertino_icons: ^1.0.2
|
||||
dev: ^1.0.0
|
||||
dynamic_color: ^1.6.6
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
@ -124,4 +119,4 @@ flutter:
|
||||
# weight: 700
|
||||
#
|
||||
# For details regarding fonts from package dependencies,
|
||||
# see https://flutter.dev/custom-fonts/#from-packages
|
||||
# see https://flutter.dev/custom-fonts/#from-packages
|
||||
|
@ -6,12 +6,12 @@
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <permission_handler_windows/permission_handler_windows_plugin.h>
|
||||
#include <dynamic_color/dynamic_color_plugin_c_api.h>
|
||||
#include <url_launcher_windows/url_launcher_windows.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
PermissionHandlerWindowsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
|
||||
DynamicColorPluginCApiRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("DynamicColorPluginCApi"));
|
||||
UrlLauncherWindowsRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
permission_handler_windows
|
||||
dynamic_color
|
||||
url_launcher_windows
|
||||
)
|
||||
|
||||
|
@ -31,11 +31,6 @@ bool FlutterWindow::OnCreate() {
|
||||
this->Show();
|
||||
});
|
||||
|
||||
// Flutter can complete the first frame before the "show window" callback is
|
||||
// registered. The following call ensures a frame is pending to ensure the
|
||||
// window is shown. It is a no-op if the first frame hasn't completed yet.
|
||||
flutter_controller_->ForceRedraw();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user