13 Commits

38 changed files with 652 additions and 1996 deletions

View File

@ -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.

View File

@ -3,13 +3,13 @@
#### An Open source app to download and read books from shadow library ([Annas Archive](https://annas-archive.org/)).
[![made-with-flutter](https://img.shields.io/badge/Made%20with-Flutter-4361ee.svg)](https://flutter.dev/) [![GPLv3 License](https://img.shields.io/badge/License-GPL%20v3-e63946.svg)](https://opensource.org/licenses/) ![version](https://img.shields.io/badge/version-1.0.2_beta-06d6a0)
[![made-with-flutter](https://img.shields.io/badge/Made%20with-Flutter-4361ee.svg)](https://flutter.dev/) [![GPLv3 License](https://img.shields.io/badge/License-GPL%20v3-e63946.svg)](https://opensource.org/licenses/) ![version](https://img.shields.io/badge/version-1.0_beta-06d6a0)
[<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 ([Annas Archive](https://annas-archive.org/)) . The App Has Built In Reader to Read Books.
##### As [Annas Archive](https://annas-archive.org/) Doesn't Have An API.The App Works By Sending Request To Annas Archive And Parses The Response To objects.The App Extracts The Mirrors From Response And Downloads The Book.
##### As [Annas Archive](https://annas-archive.org/) Doesn't Have An API.The App Works By Sending Request To Annas 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

View File

@ -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
}
}
}

View File

@ -1,33 +1,10 @@
<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
android:name=".MainActivity"
android:exported="true"
@ -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

View File

@ -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'>Annas Archive</a>). The App Has Built In Reader to Read Books.
</p>
<p>
As <i>Annas Archive</i> doesn't have an API, the app works by sending requests to <i>Annas 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'>Annas Archive</a>). The App Has Built In Reader to Read Books.</p><p>As <i>Annas Archive</i> doesn't have an API, the app works by sending requests to <i>Annas 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>

View File

@ -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,
),
),
);

View File

@ -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-[#00000011] 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);
}
}
}
}
@ -249,16 +210,12 @@ class AnnasArchieve {
Future<List<BookData>> searchBooks(
{required String searchQuery,
String content = "",
String sort = "",
String fileType = ""}) async {
String sort = ""}) async {
try {
final String encodedURL = content == ""
? '$baseUrl/search?index=&q=$searchQuery&ext=$fileType&sort=$sort'
: '$baseUrl/search?index=&q=$searchQuery&content=$content&ext=$fileType&sort=$sort';
final response = await dio.get(encodedURL,
options: Options(headers: defaultDioHeaders));
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";
@ -267,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 {

View File

@ -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 "";
}
}
}

View File

@ -1,9 +1,9 @@
import 'package:path_provider/path_provider.dart';
import 'package:dio/dio.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) {
@ -14,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);
}
}
@ -23,72 +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 {
CancelToken cancelToken = CancelToken();
try {
CancelToken cancelToken = CancelToken();
dio.download(
workingMirror,
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;
});
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;
});
mirrorStatus(true);
cancelDownlaod(cancelToken);
} catch (e) {
onDownlaodFailed();
}
} else {
cancelDownlaod(cancelToken);
} catch (e) {
onDownlaodFailed();
}
}

View File

@ -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 {

View File

@ -1,5 +1,4 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:openlib/services/database.dart';
import 'package:dio/dio.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) => "");
@ -75,30 +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));
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);
@ -164,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);

View File

@ -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,
)
],
),
),
);
}
}

View File

@ -1,7 +1,5 @@
import 'package:dio/dio.dart';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:openlib/services/database.dart';
@ -17,122 +15,44 @@ import 'package:openlib/state/state.dart'
getTotalFileSize,
getDownloadedFileSize,
cancelCurrentDownload,
mirrorStatusProvider,
dbProvider,
checkIdExists,
myLibraryProvider;
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(
@ -178,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,
)),
),
);
}
@ -244,9 +166,6 @@ Future<void> downloadFileWidget(
cancelDownlaod: (CancelToken downloadToken) {
ref.read(cancelCurrentDownload.notifier).state = downloadToken;
},
mirrorStatus: (val) {
ref.read(mirrorStatusProvider.notifier).state = val;
},
onDownlaodFailed: () {
Navigator.of(context).pop();
showSnackBar(
@ -271,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).cancel();
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'),
),
],
);

View File

@ -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,
),
),
],
),
],
),
),

View File

@ -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,

View File

@ -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'),
),
],
);

View File

@ -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!');
}
}

View File

@ -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,
),
),
);

View File

@ -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))),

View File

@ -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),

View File

@ -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,
));
}
});

View File

@ -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,
));
},
);

View File

@ -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,
));
},
)

View File

@ -23,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(
@ -46,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);
}));
},
))
@ -104,22 +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,
),
child: CircularProgressIndicator(
color: Theme.of(context).colorScheme.secondary,
))
],
),

View File

@ -7,33 +7,24 @@ import 'package:openlib/state/state.dart'
searchQueryProvider,
selectedTypeState,
selectedSortState,
selectedFileTypeState,
typeValues,
fileType,
sortValues;
import 'components/snack_bar_widget.dart';
class SearchPage extends ConsumerWidget {
const SearchPage({Key? key}) : super(key: key);
void onSubmit(BuildContext context, WidgetRef ref) {
if (ref.read(searchQueryProvider).isNotEmpty) {
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';
},
),
),
),
],
),
),

View File

@ -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,
),
),
),
),
);
}
}

View File

@ -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(),
),
);

View File

@ -17,133 +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: () {
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,
);
}, loading: () {
return const Center(
child: SizedBox(
width: 25,
height: 25,
child: CircularProgressIndicator(
// color: Theme.of(context).colorScheme.secondary,
),
));
});
));
});
}
}

View File

@ -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);
}
},
)),
),
),
);
}
}

View File

@ -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);

View File

@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
dynamic_color
url_launcher_linux
)

View File

@ -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"))

View File

@ -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:
@ -93,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:
@ -125,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:
@ -141,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:
@ -181,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:
@ -234,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:
@ -250,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:
@ -364,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:
@ -392,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:
@ -420,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:
@ -516,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:
@ -548,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:
@ -577,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:
@ -633,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:
@ -673,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:
@ -697,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:
@ -797,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:
@ -886,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"

View File

@ -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+5
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,10 +44,6 @@ 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: ^5.1.0
@ -61,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:

View File

@ -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"));
}

View File

@ -3,7 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
permission_handler_windows
dynamic_color
url_launcher_windows
)

View File

@ -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;
}