diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml index 910a9c5..7e343cb 100644 --- a/.github/workflows/dart.yml +++ b/.github/workflows/dart.yml @@ -69,9 +69,6 @@ jobs: with: files: build/windows/runner/Release/Release-${{github.ref_name}}-windows.zip - - - build-and-release-android: runs-on: ubuntu-latest @@ -89,7 +86,12 @@ jobs: channel: 'master' - name: Install dependencies - run: flutter pub get + run: flutter pub get + - name: Decode android/neumodore_key.jks + run: echo "${{ secrets.KEYSTORE }}" | base64 --decode > .key/keystore.jks + + - name: Decode android/key.properties + run: echo "${{ secrets.KEY_PROPERTIES }}" | base64 --decode > .key/key.properties - run: flutter build apk --release --split-debug-info='' - uses: actions/upload-artifact@v3 diff --git a/.gitignore b/.gitignore index 25b318f..8dbcafa 100755 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ .buildlog/ .history .svn/ +.key/ # IntelliJ related *.iml diff --git a/android/app/build.gradle b/android/app/build.gradle index 50bc3ff..6659f89 100755 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -25,6 +25,12 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" +def keystoreProperties = new Properties() +def keystorePropertiesFile = rootProject.file('../.key/key.properties') +if (keystorePropertiesFile.exists()) { + keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) +} + android { compileSdkVersion 33 @@ -40,14 +46,20 @@ android { versionCode flutterVersionCode.toInteger() versionName flutterVersionName } - - buildTypes { - release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug - } - } + + signingConfigs { + release { + keyAlias keystoreProperties['keyAlias'] + keyPassword keystoreProperties['keyPassword'] + storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null + storePassword keystoreProperties['storePassword'] + } + } + buildTypes { + release { + signingConfig signingConfigs.release + } + } } flutter { diff --git a/android/build.gradle b/android/build.gradle index c732d03..0072205 100755 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.6.10' + ext.kotlin_version = '1.8.21' repositories { google() jcenter() diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties old mode 100755 new mode 100644 index 8bd70f3..756067b --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Fri Jun 23 08:50:38 CEST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip +distributionUrl=https://services.gradle.org/distributions/gradle-7.6.1-all.zip diff --git a/lib/info_page.dart b/lib/info_page.dart deleted file mode 100644 index 51909a9..0000000 --- a/lib/info_page.dart +++ /dev/null @@ -1,253 +0,0 @@ -import 'package:anicross/media/providers/anime_providers.dart'; -import 'package:anicross/models/info_models.dart'; -import 'package:anicross/media/providers/manga_providers.dart'; -import 'package:expandable_text/expandable_text.dart'; -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; -import 'package:isar/isar.dart'; -import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; -import 'widgets/image.dart'; - -class InfoPage extends StatefulWidget { - final AniData data; - - const InfoPage({required this.data, super.key}); - - @override - State createState() => InfoPageState(); -} - -class InfoPageState extends State { - final List content = []; - final Isar isar = Isar.getInstance('later')!; - - @override - void initState() { - Future.microtask( - () async { - content.addAll( - switch (widget.data.type) { - 'anime' => await zoroList(widget.data.mediaId), - 'manga' => await dexReader(widget.data.mediaId), - _ => [], - }, - ); - if (content.isEmpty) { - content.addAll(await haniList(widget.data.title)); - } - if (mounted) { - setState(() {}); - } - }, - ); - super.initState(); - } - - @override - void dispose() { - super.dispose(); - } - - @override - Widget build(context) { - final expands = Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - const Divider(), - ExpandableText( - widget.data.description!, - expandText: "More", - collapseText: "Less", - maxLines: 8, - ), - const Divider( - height: 15, - ), - Wrap( - spacing: 3, - runSpacing: 7, - children: List.generate( - widget.data.tags!.length.clamp(0, 15), - (index) { - return ActionChip( - backgroundColor: Colors.blueGrey[800], - side: BorderSide.none, - padding: EdgeInsets.zero, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12)), - label: Text( - widget.data.tags![index], - ), - onPressed: () { - switch (widget.data.type) { - case 'anime': - { - context.go( - '/anime?tag=${widget.data.tags![index]}', - ); - } - case 'manga': - { - context.go( - '/manga?tag=${widget.data.tags![index]}', - ); - } - } - }, - ); - }, - ), - ), - ], - ); - return Scaffold( - body: SafeArea( - child: CustomScrollView( - slivers: [ - SliverAppBar( - floating: true, - title: Text(widget.data.title), - //leading: BackButton(), - ), - SliverPadding( - padding: const EdgeInsets.all(15), - sliver: SliverToBoxAdapter( - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Flexible( - flex: 2, - fit: FlexFit.tight, - child: AniImage( - image: widget.data.image, - ), - ), - const VerticalDivider( - width: 20, - ), - Flexible( - flex: 4, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - widget.data.title, - style: const TextStyle( - fontSize: 20, - ), - ), - const Divider( - height: 20, - ), - ActionChip( - onPressed: () => setState( - () { - QueryBuilder media = - isar.aniDatas.filter().mediaIdMatches( - widget.data.mediaId!, - ); - if (media.isEmptySync()) { - isar.writeTxnSync( - () => isar.aniDatas.putSync(widget.data), - ); - } else { - isar.writeTxnSync( - () => media.deleteAllSync(), - ); - } - }, - ), - avatar: (isar.aniDatas - .filter() - .mediaIdMatches(widget.data.mediaId!) - .isEmptySync()) - ? const Icon(MdiIcons.bookmarkOutline) - : const Icon(MdiIcons.bookmark), - label: const Text("Later"), - ), - if (MediaQuery.of(context).size.width / - MediaQuery.of(context).size.height > - 1.5) - expands, - ], - ), - ), - ], - ), - ), - ), - if (MediaQuery.of(context).size.width / - MediaQuery.of(context).size.height < - 1.5) - SliverPadding( - padding: const EdgeInsets.only( - left: 15, - right: 15, - ), - sliver: SliverToBoxAdapter( - child: expands, - ), - ), - if (content.isNotEmpty) - SliverPadding( - padding: const EdgeInsets.all(15), - sliver: SliverGrid( - gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( - mainAxisSpacing: 5, - crossAxisSpacing: 6, - maxCrossAxisExtent: 400, - mainAxisExtent: 100, - ), - delegate: SliverChildBuilderDelegate( - childCount: content.length, - (context, index) { - return GestureDetector( - onTap: () => context.push( - switch (widget.data.type) { - 'anime' => '/anime/info/viewer', - 'manga' => '/manga/info/viewer', - _ => '', - }, - extra: { - 'content': content[index], - 'contents': content, - }, - ), - child: Card( - elevation: 3, - child: Padding( - padding: const EdgeInsets.all(20), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - Flexible( - child: Text( - content[index].title, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ), - Flexible( - child: Text( - content[index].number, - ), - ), - ], - ), - ), - ), - ); - }, - ), - ), - ), - ], - ), - ), - ); - } -} diff --git a/lib/main.dart b/lib/main.dart index 6080f5d..c448909 100755 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,8 +1,8 @@ import 'dart:ui'; -import 'package:anicross/media/anime_videos.dart'; -import 'package:anicross/media/manga_reader.dart'; -import 'package:anicross/later_page.dart'; -import 'package:anicross/novel/novel_reader.dart'; +import '/media/anime_videos.dart'; +import '/media/manga_reader.dart'; +import 'pages/later_page.dart'; +import '/novel/novel_reader.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; @@ -13,18 +13,16 @@ import 'dart:io'; import 'media/media.dart'; import 'models/info_models.dart'; import 'novel/novel.dart'; -import 'info_page.dart'; +import 'pages/info_page.dart'; final GlobalKey _rootKey = GlobalKey(); -final GlobalKey _shellkey = - GlobalKey(debugLabel: 'shell'); +final GlobalKey _shellkey = GlobalKey(); void main() async { - int index = 0; WidgetsFlutterBinding.ensureInitialized(); MediaKit.ensureInitialized(); Isar.openSync( - [AniDataSchema, MediaProvSchema], + [AniDataSchema, MediaProvSchema, NovDataSchema], name: "later", directory: (await Directory( '${(await getApplicationDocumentsDirectory()).path}/.anicross') @@ -34,9 +32,6 @@ void main() async { runApp( MaterialApp.router( theme: ThemeData( - dividerTheme: const DividerThemeData( - color: Colors.transparent, - ), colorScheme: const ColorScheme.dark(), useMaterial3: true, chipTheme: const ChipThemeData( @@ -53,15 +48,9 @@ void main() async { navigatorKey: _rootKey, initialLocation: '/anime', routes: [ - ShellRoute( - navigatorKey: _shellkey, - builder: (context, state, child) => Scaffold( - body: child, - floatingActionButton: FloatingActionButton( - onPressed: () { - PrimaryScrollController.of(context).jumpTo(0); - }, - ), + StatefulShellRoute.indexedStack( + builder: (context, state, shell) => Scaffold( + body: shell, bottomNavigationBar: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -72,31 +61,8 @@ void main() async { elevation: 0, useLegacyColorScheme: false, showUnselectedLabels: false, - currentIndex: index, - onTap: (value) { - switch (value) { - case 0: - { - index = 0; - context.go('/anime'); - } - case 1: - { - index = 1; - context.go('/manga'); - } - case 2: - { - index = 2; - context.go('/novel'); - } - case 3: - { - index = 3; - context.go('/later'); - } - } - }, + currentIndex: shell.currentIndex, + onTap: (value) => shell.goBranch(value), items: const [ BottomNavigationBarItem( icon: Icon(MdiIcons.youtubeTv), @@ -120,93 +86,109 @@ void main() async { ], ), ), - routes: [ - GoRoute( - parentNavigatorKey: _shellkey, - name: 'anime', - path: '/anime', - builder: (context, state) { - index = 0; - return AniPage( - key: (state.queryParameters.isEmpty) - ? null - : Key(state.queryParameters['tag']!), - type: 'anime', - tag: state.queryParameters['tag'], - ); - }, + branches: [ + StatefulShellBranch( + navigatorKey: _shellkey, routes: [ GoRoute( - parentNavigatorKey: _rootKey, - path: 'info', - builder: (context, state) => InfoPage( - data: state.extra as AniData, - ), + name: 'anime', + path: '/anime', + builder: (context, state) { + return AniPage( + key: (state.queryParameters.isEmpty) + ? null + : Key(state.queryParameters['tag']!), + type: 'anime', + tag: state.queryParameters['tag'], + ); + }, routes: [ GoRoute( parentNavigatorKey: _rootKey, - path: 'viewer', - builder: (context, state) => AniViewer( - episodes: (state.extra as Map)['contents'], - episode: (state.extra as Map)['content'], + path: 'info', + builder: (context, state) => InfoPage( + data: state.extra as AniData, ), + routes: [ + GoRoute( + parentNavigatorKey: _rootKey, + path: 'viewer', + builder: (context, state) => AniViewer( + episodes: (state.extra as Map)['contents'], + episode: (state.extra as Map)['content'], + ), + ), + ], ), ], ), ], ), - GoRoute( - parentNavigatorKey: _shellkey, - name: 'manga', - path: '/manga', - builder: (context, state) { - index = 1; - return AniPage( - key: (state.queryParameters.isEmpty) - ? null - : Key(state.queryParameters['tag']!), - type: 'manga', - tag: state.queryParameters['tag'], - ); - }, + StatefulShellBranch( + //navigatorKey: _shellkey, routes: [ GoRoute( - parentNavigatorKey: _rootKey, - path: 'info', - builder: (context, state) => InfoPage( - data: state.extra as AniData, - ), + name: 'manga', + path: '/manga', + builder: (context, state) { + return AniPage( + key: (state.queryParameters.isEmpty) + ? null + : Key(state.queryParameters['tag']!), + type: 'manga', + tag: state.queryParameters['tag'], + ); + }, routes: [ GoRoute( parentNavigatorKey: _rootKey, - path: 'viewer', - builder: (context, state) => MangaReader( - chapter: (state.extra as Map)['content'], - chapters: (state.extra as Map)['contents'], + path: 'info', + builder: (context, state) => InfoPage( + data: state.extra as AniData, ), + routes: [ + GoRoute( + parentNavigatorKey: _rootKey, + path: 'viewer', + builder: (context, state) => MangaReader( + chapter: (state.extra as Map)['content'], + chapters: (state.extra as Map)['contents'], + ), + ), + ], ), ], ), ], ), - GoRoute( - name: 'novel', - path: '/novel', - builder: (context, state) => const NovelPage(), + StatefulShellBranch( + //navigatorKey: _shellkey, routes: [ GoRoute( - parentNavigatorKey: _rootKey, - path: 'viewer', - builder: (context, state) => NovelReader( - data: state.extra as AniData, - ), + name: 'novel', + path: '/novel', + builder: (context, state) => const NovelPage(), + routes: [ + GoRoute( + parentNavigatorKey: _rootKey, + path: 'viewer', + builder: (context, state) => NovelReader( + data: state.extra as NovData, + ), + ), + ], ), ], ), - GoRoute( - name: 'later', - path: '/later', - builder: (context, state) => const LaterPage(), + StatefulShellBranch( + //navigatorKey: _shellkey, + routes: [ + GoRoute( + name: 'later', + path: '/later', + builder: (context, state) => const LaterPage(), + ), + ], ), ], ), diff --git a/lib/media/anime_videos.dart b/lib/media/anime_videos.dart index a7b8aa1..456e4b5 100644 --- a/lib/media/anime_videos.dart +++ b/lib/media/anime_videos.dart @@ -1,5 +1,5 @@ import 'dart:async'; -import 'package:anicross/models/info_models.dart'; +import '/models/info_models.dart'; import 'package:audio_video_progress_bar/audio_video_progress_bar.dart'; import 'package:dio/dio.dart'; import 'package:path/path.dart' as p; diff --git a/lib/media/media.dart b/lib/media/media.dart index dd4fc6c..2a76bcf 100644 --- a/lib/media/media.dart +++ b/lib/media/media.dart @@ -1,3 +1,4 @@ +import 'dart:ui'; import '../models/info_models.dart'; import 'package:flutter/material.dart'; import 'package:graphql/client.dart'; @@ -61,7 +62,7 @@ const genresList = [ "Slice of Life", "Sports", "Supernatural", - "Thriller" + "Thriller", ]; class AniPage extends StatefulWidget { @@ -78,7 +79,7 @@ class AniPageState extends State { String? search; bool loading = false; List selectedGenres = []; - late final String? tag = widget.tag; + late String? tag = widget.tag; Map pageInfo = {}; List animeData = []; @@ -98,54 +99,36 @@ class AniPageState extends State { } Future queryData() async { - QueryResult query = await client.query( - QueryOptions( - fetchPolicy: FetchPolicy.networkOnly, - document: gql(base), - variables: { - "page": (pageInfo.isEmpty) ? 1 : pageInfo['currentPage'] + 1, - 'type': widget.type.toUpperCase(), - 'search': search, - "genre": (selectedGenres.isNotEmpty) ? selectedGenres : null, - "tag": tag, - }, - ), - ); - pageInfo = query.data!['Page']['pageInfo']; - animeData.addAll( - List.generate( - query.data!['Page']['media'].length, - (index) { - return AniData( - type: widget.type, - mediaId: query.data!['Page']['media'][index]['id'].toString(), - description: - (query.data!['Page']['media'][index]['description'] ?? "") - .replaceAll(RegExp(r'<[^>]*>|&[^;]+;'), ' '), - title: "${query.data!['Page']['media'][index]['title']['romaji']}", - image: query.data!['Page']['media'][index]['coverImage'] - ['extraLarge'], - count: (query.data!['Page']['media'][index]['episodes'] ?? "n/a") - .toString(), - score: - (query.data!['Page']['media'][index]['averageScore'] ?? "n/a") - .toString(), - tags: List.generate( - query.data!['Page']['media'][index]['tags'].length, - (tagIndex) { - return query.data!['Page']['media'][index]['tags'][tagIndex] - ['name']; - }, - ), - ); - }, - ), - ); - loading = false; - } - - Future updateData() async { - await queryData(); + try { + QueryResult query = await client.query( + QueryOptions( + fetchPolicy: FetchPolicy.networkOnly, + document: gql(base), + variables: { + "page": (pageInfo.isEmpty) ? 1 : pageInfo['currentPage'] + 1, + 'type': widget.type.toUpperCase(), + 'search': search, + "genre": (selectedGenres.isNotEmpty) ? selectedGenres : null, + "tag": tag, + }, + ), + ); + pageInfo = query.data!['Page']['pageInfo']; + animeData.addAll( + List.generate( + query.data!['Page']['media'].length, + (index) { + return AniData.fromJson( + query.data!['Page']['media'][index], + widget.type, + ); + }, + ), + ); + loading = false; + } catch (e) { + animeData.addAll([]); + } } Future searchData() async { @@ -155,6 +138,7 @@ class AniPageState extends State { selectedGenres = []; search = textController.text; } else { + tag = null; selectedGenres = []; search = null; } @@ -163,61 +147,57 @@ class AniPageState extends State { } Future updateGenre() async { - await showDialog( + await showModalBottomSheet( + constraints: BoxConstraints.tightFor( + width: clampDouble(MediaQuery.of(context).size.width, 0, 384)), context: context, builder: (context) { - return StatefulBuilder( - builder: (context, setState) => AlertDialog( - content: SizedBox( - width: MediaQuery.of(context).size.width / 2, - child: SingleChildScrollView( - child: Wrap( - spacing: 20, - runSpacing: 20, - children: List.generate( - genresList.length, - (index) { - return FilterChip( - labelPadding: const EdgeInsets.all(15), - padding: const EdgeInsets.all(2), - selected: selectedGenres.contains( - genresList[index], - ), - label: Text( - genresList[index], - ), - onSelected: (value) => setState( - () { - if (value) { - selectedGenres.add(genresList[index]); - } else { - selectedGenres.remove(genresList[index]); - } - }, - ), - ); - }, - ), - ), + return Padding( + padding: const EdgeInsets.all(15), + child: StatefulBuilder( + builder: (context, setState) => Wrap( + alignment: WrapAlignment.center, + spacing: 10, + runSpacing: 10, + children: List.generate( + genresList.length, + (index) { + return FilterChip( + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + visualDensity: VisualDensity.compact, + labelPadding: const EdgeInsets.all(0), + selected: selectedGenres.contains( + genresList[index], + ), + label: Text( + genresList[index], + ), + onSelected: (value) => setState( + () { + if (value) { + selectedGenres.add(genresList[index]); + } else { + selectedGenres.remove(genresList[index]); + } + }, + ), + ); + }, ), ), - actions: [ - TextButton( - onPressed: () => Navigator.pop(context), - child: const Text("Okay"), - ), - ], ), ); }, - ).then((value) async { - pageInfo = {}; - animeData = []; - await queryData(); - setState( - () {}, - ); - }); + ).then( + (value) async { + pageInfo = {}; + animeData = []; + await queryData(); + setState( + () {}, + ); + }, + ); } @override @@ -231,52 +211,61 @@ class AniPageState extends State { loading = true; Future.microtask( () async { - await updateData(); + await queryData(); setState(() {}); }, ); } - return true; + return false; }, - child: CustomScrollView( - primary: true, - slivers: [ - SliverToBoxAdapter( - child: Center( - child: SearchButton( - text: "Anilist", - controller: textController, - search: () async { - await searchData(); - setState(() {}); - }, + child: RefreshIndicator( + edgeOffset: 100, + onRefresh: () => Future.microtask( + () async { + await queryData(); + setState(() {}); + }, + ), + child: CustomScrollView( + primary: true, + slivers: [ + SliverToBoxAdapter( + child: Center( + child: SearchButton( + text: "Anilist", + controller: textController, + search: () async { + await searchData(); + setState(() {}); + }, + ), ), ), - ), - SliverToBoxAdapter( - child: Wrap( - alignment: WrapAlignment.spaceAround, - children: [ - TextButton( - onPressed: () => updateGenre(), - child: const Text( - "Filter by genre", - style: TextStyle(color: Colors.white), + SliverToBoxAdapter( + child: Wrap( + alignment: WrapAlignment.spaceAround, + children: [ + TextButton( + onPressed: () => updateGenre(), + child: const Text( + "Filter by genre", + style: TextStyle(color: Colors.white), + ), ), - ), - ], + ], + ), ), - ), - (animeData.isNotEmpty) - ? Grid( - data: animeData, - ) - : const SliverToBoxAdapter( - child: Center( - child: CircularProgressIndicator(), + (animeData.isNotEmpty) + ? Grid( + data: animeData, + ) + : const SliverToBoxAdapter( + child: Center( + child: CircularProgressIndicator(), + ), ), - ), - ], + ], + ), ), ); } diff --git a/lib/media/providers/anime_providers.dart b/lib/media/providers/anime_providers.dart index 9579177..3fc955a 100644 --- a/lib/media/providers/anime_providers.dart +++ b/lib/media/providers/anime_providers.dart @@ -1,7 +1,6 @@ import 'dart:convert'; -import 'dart:math'; import 'dart:typed_data'; -import 'package:anicross/models/info_models.dart'; +import '/models/info_models.dart'; import 'package:html/parser.dart'; import 'package:html/dom.dart'; import 'package:dio/dio.dart'; @@ -26,7 +25,7 @@ Future> zoroList(id) async { ), ); final Document episodeList = parse(jsonDecode(html.data)['html']); - List episodes = []; + final List episodes = []; for (Element i in episodeList.getElementsByClassName('ssl-item ep-item')) { episodes.add( MediaProv( @@ -40,7 +39,6 @@ Future> zoroList(id) async { } return episodes; } catch (e) { - print(e); return []; } } @@ -113,9 +111,9 @@ Future> haniList(String name) async { ); if (json.data['nbHits'] > 0) { final List results = jsonDecode(json.data['hits']); - List videos = []; + final List videos = []; for (Map i in results) { - Response v = await Dio().get( + final Response v = await Dio().get( "https://hanime.tv/api/v8/video?id=${i['id']}", ); if (i['name'].toString().similarityTo(name) > 0.2) { @@ -148,32 +146,6 @@ Future> haniList(String name) async { } //End hanime -// Response json = await Dio().get( -// "https://api.consumet.org/meta/anilist/info/$id?provider=zoro", -// ); -// return (json.data['episodes'].isEmpty) -// ? null -// : List.generate( -// json.data['episodes'].length, -// (index) { -// return { -// "id": json.data['episodes'][index]['id'], -// "title": json.data['episodes'][index]['title'], -// "number": "Episode: ${json.data['episodes'][index]['number']}", -// "description": json.data['episodes'][index]['description'] -// }; -// }, -// ); - -// try { -// Response json = await Dio().get( -// "https://api.consumet.org/meta/anilist/watch/$id?provider=zoro", -// ); -// return json.data; -// } catch (e) { -// return null; -// } - String decryptAESCryptoJS(final String encrypted, final String passphrase) { try { Uint8List encryptedBytesWithSalt = base64.decode(encrypted); @@ -194,7 +166,7 @@ String decryptAESCryptoJS(final String encrypted, final String passphrase) { } List deriveKeyAndIV(final String passphrase, final Uint8List salt) { - var password = createUint8ListFromString(passphrase); + Uint8List password = Uint8List.fromList(passphrase.codeUnits); Uint8List concatenatedHashes = Uint8List(0); Uint8List currentHash = Uint8List(0); Uint8List preHash = Uint8List(0); @@ -213,21 +185,3 @@ List deriveKeyAndIV(final String passphrase, final Uint8List salt) { concatenatedHashes.sublist(32, 48), ]; } - -Uint8List createUint8ListFromString(final String s) { - Uint8List ret = Uint8List(s.length); - for (var i = 0; i < s.length; i++) { - ret[i] = s.codeUnitAt(i); - } - return ret; -} - -Uint8List genRandomWithNonZero(final int seedLength) { - final Random random = Random.secure(); - const int randomMax = 245; - final Uint8List uint8list = Uint8List(seedLength); - for (int i = 0; i < seedLength; i++) { - uint8list[i] = random.nextInt(randomMax) + 1; - } - return uint8list; -} diff --git a/lib/media/providers/manga_providers.dart b/lib/media/providers/manga_providers.dart index feec554..a413647 100644 --- a/lib/media/providers/manga_providers.dart +++ b/lib/media/providers/manga_providers.dart @@ -17,11 +17,11 @@ Future> dexReader(id) async { json['data'].length, (index) { return MediaProv( - provider: 'mangadex', - provId: json['data'][index]['id'], - title: json['data'][index]['attributes']['title'] ?? "", - number: - "${(json['data'][index]['attributes']['volume'] != null) ? "Vol: ${json['data'][index]['attributes']['volume']}" : ""} Ch: ${json['data'][index]['attributes']['chapter']}"); + provider: 'mangadex', + provId: json['data'][index]['id'], + title: json['data'][index]['attributes']['title'] ?? "", + number: json['data'][index]['attributes']['chapter'], + ); }, ); } diff --git a/lib/models/info_models.dart b/lib/models/info_models.dart index 0190441..50610d2 100644 --- a/lib/models/info_models.dart +++ b/lib/models/info_models.dart @@ -6,24 +6,55 @@ part 'info_models.g.dart'; class AniData { final Id id = Isar.autoIncrement; final String type; - final String? mediaId; - final String? malid; + final String mediaId; + final int? malid; final String title; - final String? description; + final String description; final String image; - final String? score; - final String? count; - final List? tags; + final String score; + final String count; + final List tags; AniData({ required this.type, - this.mediaId, - this.malid, + required this.mediaId, + required this.malid, required this.title, - this.description, + required this.description, required this.image, - this.count, - this.score, - this.tags, + required this.count, + required this.score, + required this.tags, + }); + + AniData.fromJson(Map json, this.type) + : malid = json['idMal'], + mediaId = json['id'].toString(), + description = (json['description'] ?? "") + .replaceAll(RegExp(r'<[^>]*>|&[^;]+;'), ' '), + title = "${json['title']['romaji']}", + image = json['coverImage']['extraLarge'], + count = (json['episodes'] ?? "n/a").toString(), + score = (json['averageScore'] ?? "n/a").toString(), + tags = List.generate( + json['tags'].length, + (tagIndex) { + return json['tags'][tagIndex]['name']; + }, + ); +} + +@collection +class NovData { + final Id id = Isar.autoIncrement; + final String type; + final String title; + final String image; + final String path; + NovData({ + required this.type, + required this.title, + required this.image, + required this.path, }); } diff --git a/lib/models/info_models.g.dart b/lib/models/info_models.g.dart index 3c7cc84..fed82b4 100644 --- a/lib/models/info_models.g.dart +++ b/lib/models/info_models.g.dart @@ -35,7 +35,7 @@ const AniDataSchema = CollectionSchema( r'malid': PropertySchema( id: 3, name: r'malid', - type: IsarType.string, + type: IsarType.long, ), r'mediaId': PropertySchema( id: 4, @@ -83,47 +83,16 @@ int _aniDataEstimateSize( Map> allOffsets, ) { var bytesCount = offsets.last; - { - final value = object.count; - if (value != null) { - bytesCount += 3 + value.length * 3; - } - } - { - final value = object.description; - if (value != null) { - bytesCount += 3 + value.length * 3; - } - } + bytesCount += 3 + object.count.length * 3; + bytesCount += 3 + object.description.length * 3; bytesCount += 3 + object.image.length * 3; + bytesCount += 3 + object.mediaId.length * 3; + bytesCount += 3 + object.score.length * 3; + bytesCount += 3 + object.tags.length * 3; { - final value = object.malid; - if (value != null) { - bytesCount += 3 + value.length * 3; - } - } - { - final value = object.mediaId; - if (value != null) { - bytesCount += 3 + value.length * 3; - } - } - { - final value = object.score; - if (value != null) { - bytesCount += 3 + value.length * 3; - } - } - { - final list = object.tags; - if (list != null) { - bytesCount += 3 + list.length * 3; - { - for (var i = 0; i < list.length; i++) { - final value = list[i]; - bytesCount += value.length * 3; - } - } + for (var i = 0; i < object.tags.length; i++) { + final value = object.tags[i]; + bytesCount += value.length * 3; } } bytesCount += 3 + object.title.length * 3; @@ -140,7 +109,7 @@ void _aniDataSerialize( writer.writeString(offsets[0], object.count); writer.writeString(offsets[1], object.description); writer.writeString(offsets[2], object.image); - writer.writeString(offsets[3], object.malid); + writer.writeLong(offsets[3], object.malid); writer.writeString(offsets[4], object.mediaId); writer.writeString(offsets[5], object.score); writer.writeStringList(offsets[6], object.tags); @@ -155,13 +124,13 @@ AniData _aniDataDeserialize( Map> allOffsets, ) { final object = AniData( - count: reader.readStringOrNull(offsets[0]), - description: reader.readStringOrNull(offsets[1]), + count: reader.readString(offsets[0]), + description: reader.readString(offsets[1]), image: reader.readString(offsets[2]), - malid: reader.readStringOrNull(offsets[3]), - mediaId: reader.readStringOrNull(offsets[4]), - score: reader.readStringOrNull(offsets[5]), - tags: reader.readStringList(offsets[6]), + malid: reader.readLongOrNull(offsets[3]), + mediaId: reader.readString(offsets[4]), + score: reader.readString(offsets[5]), + tags: reader.readStringList(offsets[6]) ?? [], title: reader.readString(offsets[7]), type: reader.readString(offsets[8]), ); @@ -176,19 +145,19 @@ P _aniDataDeserializeProp

( ) { switch (propertyId) { case 0: - return (reader.readStringOrNull(offset)) as P; + return (reader.readString(offset)) as P; case 1: - return (reader.readStringOrNull(offset)) as P; + return (reader.readString(offset)) as P; case 2: return (reader.readString(offset)) as P; case 3: - return (reader.readStringOrNull(offset)) as P; + return (reader.readLongOrNull(offset)) as P; case 4: - return (reader.readStringOrNull(offset)) as P; + return (reader.readString(offset)) as P; case 5: - return (reader.readStringOrNull(offset)) as P; + return (reader.readString(offset)) as P; case 6: - return (reader.readStringList(offset)) as P; + return (reader.readStringList(offset) ?? []) as P; case 7: return (reader.readString(offset)) as P; case 8: @@ -285,24 +254,8 @@ extension AniDataQueryWhere on QueryBuilder { extension AniDataQueryFilter on QueryBuilder { - QueryBuilder countIsNull() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'count', - )); - }); - } - - QueryBuilder countIsNotNull() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'count', - )); - }); - } - QueryBuilder countEqualTo( - String? value, { + String value, { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { @@ -315,7 +268,7 @@ extension AniDataQueryFilter } QueryBuilder countGreaterThan( - String? value, { + String value, { bool include = false, bool caseSensitive = true, }) { @@ -330,7 +283,7 @@ extension AniDataQueryFilter } QueryBuilder countLessThan( - String? value, { + String value, { bool include = false, bool caseSensitive = true, }) { @@ -345,8 +298,8 @@ extension AniDataQueryFilter } QueryBuilder countBetween( - String? lower, - String? upper, { + String lower, + String upper, { bool includeLower = true, bool includeUpper = true, bool caseSensitive = true, @@ -431,24 +384,8 @@ extension AniDataQueryFilter }); } - QueryBuilder descriptionIsNull() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'description', - )); - }); - } - - QueryBuilder descriptionIsNotNull() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'description', - )); - }); - } - QueryBuilder descriptionEqualTo( - String? value, { + String value, { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { @@ -461,7 +398,7 @@ extension AniDataQueryFilter } QueryBuilder descriptionGreaterThan( - String? value, { + String value, { bool include = false, bool caseSensitive = true, }) { @@ -476,7 +413,7 @@ extension AniDataQueryFilter } QueryBuilder descriptionLessThan( - String? value, { + String value, { bool include = false, bool caseSensitive = true, }) { @@ -491,8 +428,8 @@ extension AniDataQueryFilter } QueryBuilder descriptionBetween( - String? lower, - String? upper, { + String lower, + String upper, { bool includeLower = true, bool includeUpper = true, bool caseSensitive = true, @@ -777,54 +714,46 @@ extension AniDataQueryFilter } QueryBuilder malidEqualTo( - String? value, { - bool caseSensitive = true, - }) { + int? value) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.equalTo( property: r'malid', value: value, - caseSensitive: caseSensitive, )); }); } QueryBuilder malidGreaterThan( - String? value, { + int? value, { bool include = false, - bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.greaterThan( include: include, property: r'malid', value: value, - caseSensitive: caseSensitive, )); }); } QueryBuilder malidLessThan( - String? value, { + int? value, { bool include = false, - bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.lessThan( include: include, property: r'malid', value: value, - caseSensitive: caseSensitive, )); }); } QueryBuilder malidBetween( - String? lower, - String? upper, { + int? lower, + int? upper, { bool includeLower = true, bool includeUpper = true, - bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.between( @@ -833,97 +762,12 @@ extension AniDataQueryFilter includeLower: includeLower, upper: upper, includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder malidStartsWith( - String value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'malid', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder malidEndsWith( - String value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'malid', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder malidContains( - String value, - {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'malid', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder malidMatches( - String pattern, - {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'malid', - wildcard: pattern, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder malidIsEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'malid', - value: '', - )); - }); - } - - QueryBuilder malidIsNotEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'malid', - value: '', - )); - }); - } - - QueryBuilder mediaIdIsNull() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'mediaId', - )); - }); - } - - QueryBuilder mediaIdIsNotNull() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'mediaId', )); }); } QueryBuilder mediaIdEqualTo( - String? value, { + String value, { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { @@ -936,7 +780,7 @@ extension AniDataQueryFilter } QueryBuilder mediaIdGreaterThan( - String? value, { + String value, { bool include = false, bool caseSensitive = true, }) { @@ -951,7 +795,7 @@ extension AniDataQueryFilter } QueryBuilder mediaIdLessThan( - String? value, { + String value, { bool include = false, bool caseSensitive = true, }) { @@ -966,8 +810,8 @@ extension AniDataQueryFilter } QueryBuilder mediaIdBetween( - String? lower, - String? upper, { + String lower, + String upper, { bool includeLower = true, bool includeUpper = true, bool caseSensitive = true, @@ -1052,24 +896,8 @@ extension AniDataQueryFilter }); } - QueryBuilder scoreIsNull() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'score', - )); - }); - } - - QueryBuilder scoreIsNotNull() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'score', - )); - }); - } - QueryBuilder scoreEqualTo( - String? value, { + String value, { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { @@ -1082,7 +910,7 @@ extension AniDataQueryFilter } QueryBuilder scoreGreaterThan( - String? value, { + String value, { bool include = false, bool caseSensitive = true, }) { @@ -1097,7 +925,7 @@ extension AniDataQueryFilter } QueryBuilder scoreLessThan( - String? value, { + String value, { bool include = false, bool caseSensitive = true, }) { @@ -1112,8 +940,8 @@ extension AniDataQueryFilter } QueryBuilder scoreBetween( - String? lower, - String? upper, { + String lower, + String upper, { bool includeLower = true, bool includeUpper = true, bool caseSensitive = true, @@ -1198,22 +1026,6 @@ extension AniDataQueryFilter }); } - QueryBuilder tagsIsNull() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'tags', - )); - }); - } - - QueryBuilder tagsIsNotNull() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'tags', - )); - }); - } - QueryBuilder tagsElementEqualTo( String value, { bool caseSensitive = true, @@ -1928,10 +1740,9 @@ extension AniDataQueryWhereDistinct }); } - QueryBuilder distinctByMalid( - {bool caseSensitive = true}) { + QueryBuilder distinctByMalid() { return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'malid', caseSensitive: caseSensitive); + return query.addDistinctBy(r'malid'); }); } @@ -1978,13 +1789,13 @@ extension AniDataQueryProperty }); } - QueryBuilder countProperty() { + QueryBuilder countProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'count'); }); } - QueryBuilder descriptionProperty() { + QueryBuilder descriptionProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'description'); }); @@ -1996,25 +1807,25 @@ extension AniDataQueryProperty }); } - QueryBuilder malidProperty() { + QueryBuilder malidProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'malid'); }); } - QueryBuilder mediaIdProperty() { + QueryBuilder mediaIdProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'mediaId'); }); } - QueryBuilder scoreProperty() { + QueryBuilder scoreProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'score'); }); } - QueryBuilder?, QQueryOperations> tagsProperty() { + QueryBuilder, QQueryOperations> tagsProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'tags'); }); @@ -2036,6 +1847,955 @@ extension AniDataQueryProperty // coverage:ignore-file // ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types +extension GetNovDataCollection on Isar { + IsarCollection get novDatas => this.collection(); +} + +const NovDataSchema = CollectionSchema( + name: r'NovData', + id: -1560610905487464081, + properties: { + r'image': PropertySchema( + id: 0, + name: r'image', + type: IsarType.string, + ), + r'path': PropertySchema( + id: 1, + name: r'path', + type: IsarType.string, + ), + r'title': PropertySchema( + id: 2, + name: r'title', + type: IsarType.string, + ), + r'type': PropertySchema( + id: 3, + name: r'type', + type: IsarType.string, + ) + }, + estimateSize: _novDataEstimateSize, + serialize: _novDataSerialize, + deserialize: _novDataDeserialize, + deserializeProp: _novDataDeserializeProp, + idName: r'id', + indexes: {}, + links: {}, + embeddedSchemas: {}, + getId: _novDataGetId, + getLinks: _novDataGetLinks, + attach: _novDataAttach, + version: '3.1.0+1', +); + +int _novDataEstimateSize( + NovData object, + List offsets, + Map> allOffsets, +) { + var bytesCount = offsets.last; + bytesCount += 3 + object.image.length * 3; + bytesCount += 3 + object.path.length * 3; + bytesCount += 3 + object.title.length * 3; + bytesCount += 3 + object.type.length * 3; + return bytesCount; +} + +void _novDataSerialize( + NovData object, + IsarWriter writer, + List offsets, + Map> allOffsets, +) { + writer.writeString(offsets[0], object.image); + writer.writeString(offsets[1], object.path); + writer.writeString(offsets[2], object.title); + writer.writeString(offsets[3], object.type); +} + +NovData _novDataDeserialize( + Id id, + IsarReader reader, + List offsets, + Map> allOffsets, +) { + final object = NovData( + image: reader.readString(offsets[0]), + path: reader.readString(offsets[1]), + title: reader.readString(offsets[2]), + type: reader.readString(offsets[3]), + ); + return object; +} + +P _novDataDeserializeProp

( + IsarReader reader, + int propertyId, + int offset, + Map> allOffsets, +) { + switch (propertyId) { + case 0: + return (reader.readString(offset)) as P; + case 1: + return (reader.readString(offset)) as P; + case 2: + return (reader.readString(offset)) as P; + case 3: + return (reader.readString(offset)) as P; + default: + throw IsarError('Unknown property with id $propertyId'); + } +} + +Id _novDataGetId(NovData object) { + return object.id; +} + +List> _novDataGetLinks(NovData object) { + return []; +} + +void _novDataAttach(IsarCollection col, Id id, NovData object) {} + +extension NovDataQueryWhereSort on QueryBuilder { + QueryBuilder anyId() { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(const IdWhereClause.any()); + }); + } +} + +extension NovDataQueryWhere on QueryBuilder { + QueryBuilder idEqualTo(Id id) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IdWhereClause.between( + lower: id, + upper: id, + )); + }); + } + + QueryBuilder idNotEqualTo(Id id) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: false), + ) + .addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: false), + ); + } else { + return query + .addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: false), + ) + .addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: false), + ); + } + }); + } + + QueryBuilder idGreaterThan(Id id, + {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: include), + ); + }); + } + + QueryBuilder idLessThan(Id id, + {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: include), + ); + }); + } + + QueryBuilder idBetween( + Id lowerId, + Id upperId, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + )); + }); + } +} + +extension NovDataQueryFilter + on QueryBuilder { + QueryBuilder idEqualTo(Id value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'id', + value: value, + )); + }); + } + + QueryBuilder idGreaterThan( + Id value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + )); + }); + } + + QueryBuilder idLessThan( + Id value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + )); + }); + } + + QueryBuilder idBetween( + Id lower, + Id upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder imageEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'image', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder imageGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'image', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder imageLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'image', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder imageBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'image', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder imageStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'image', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder imageEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'image', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder imageContains( + String value, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'image', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder imageMatches( + String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'image', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder imageIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'image', + value: '', + )); + }); + } + + QueryBuilder imageIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'image', + value: '', + )); + }); + } + + QueryBuilder pathEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'path', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder pathGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'path', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder pathLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'path', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder pathBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'path', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder pathStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'path', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder pathEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'path', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder pathContains( + String value, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'path', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder pathMatches( + String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'path', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder pathIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'path', + value: '', + )); + }); + } + + QueryBuilder pathIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'path', + value: '', + )); + }); + } + + QueryBuilder titleEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'title', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder titleGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'title', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder titleLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'title', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder titleBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'title', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder titleStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'title', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder titleEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'title', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder titleContains( + String value, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'title', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder titleMatches( + String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'title', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder titleIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'title', + value: '', + )); + }); + } + + QueryBuilder titleIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'title', + value: '', + )); + }); + } + + QueryBuilder typeEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'type', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder typeGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'type', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder typeLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'type', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder typeBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'type', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder typeStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'type', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder typeEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'type', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder typeContains( + String value, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'type', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder typeMatches( + String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'type', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder typeIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'type', + value: '', + )); + }); + } + + QueryBuilder typeIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'type', + value: '', + )); + }); + } +} + +extension NovDataQueryObject + on QueryBuilder {} + +extension NovDataQueryLinks + on QueryBuilder {} + +extension NovDataQuerySortBy on QueryBuilder { + QueryBuilder sortByImage() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'image', Sort.asc); + }); + } + + QueryBuilder sortByImageDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'image', Sort.desc); + }); + } + + QueryBuilder sortByPath() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'path', Sort.asc); + }); + } + + QueryBuilder sortByPathDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'path', Sort.desc); + }); + } + + QueryBuilder sortByTitle() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'title', Sort.asc); + }); + } + + QueryBuilder sortByTitleDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'title', Sort.desc); + }); + } + + QueryBuilder sortByType() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'type', Sort.asc); + }); + } + + QueryBuilder sortByTypeDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'type', Sort.desc); + }); + } +} + +extension NovDataQuerySortThenBy + on QueryBuilder { + QueryBuilder thenById() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'id', Sort.asc); + }); + } + + QueryBuilder thenByIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'id', Sort.desc); + }); + } + + QueryBuilder thenByImage() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'image', Sort.asc); + }); + } + + QueryBuilder thenByImageDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'image', Sort.desc); + }); + } + + QueryBuilder thenByPath() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'path', Sort.asc); + }); + } + + QueryBuilder thenByPathDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'path', Sort.desc); + }); + } + + QueryBuilder thenByTitle() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'title', Sort.asc); + }); + } + + QueryBuilder thenByTitleDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'title', Sort.desc); + }); + } + + QueryBuilder thenByType() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'type', Sort.asc); + }); + } + + QueryBuilder thenByTypeDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'type', Sort.desc); + }); + } +} + +extension NovDataQueryWhereDistinct + on QueryBuilder { + QueryBuilder distinctByImage( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'image', caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByPath( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'path', caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByTitle( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'title', caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByType( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'type', caseSensitive: caseSensitive); + }); + } +} + +extension NovDataQueryProperty + on QueryBuilder { + QueryBuilder idProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'id'); + }); + } + + QueryBuilder imageProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'image'); + }); + } + + QueryBuilder pathProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'path'); + }); + } + + QueryBuilder titleProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'title'); + }); + } + + QueryBuilder typeProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'type'); + }); + } +} + +// coverage:ignore-file +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types + extension GetMediaProvCollection on Isar { IsarCollection get mediaProvs => this.collection(); } diff --git a/lib/novel/novel.dart b/lib/novel/novel.dart index 9703597..077ad68 100755 --- a/lib/novel/novel.dart +++ b/lib/novel/novel.dart @@ -1,7 +1,7 @@ import 'dart:io'; -import 'package:anicross/novel/novel_parser.dart'; -import 'package:anicross/widgets/grid.dart'; -import 'package:anicross/models/info_models.dart'; +import '/novel/novel_parser.dart'; +import '/widgets/grid.dart'; +import '/models/info_models.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -18,12 +18,12 @@ class NovelPage extends StatefulWidget { class NovelPageState extends State { Isar isar = Isar.getInstance('later')!; - List novels = []; + List novels = []; @override void initState() { super.initState(); - novels = isar.aniDatas.filter().typeEqualTo("novel").findAllSync(); + novels = isar.novDatas.filter().typeEqualTo("novel").findAllSync(); } importNovel() async { @@ -48,7 +48,7 @@ class NovelPageState extends State { ..retainWhere( (element) => element.path.endsWith(".epub"), ); - List data = []; + List data = []; for (File i in epubs) { print(i); final Novel novel = Novel(path: i.path); @@ -56,11 +56,11 @@ class NovelPageState extends State { await novel.writeCover(); novel.close(); data.add( - AniData( + NovData( type: "novel", title: novel.title!, image: novel.cover!, - mediaId: i.path, + path: i.path, ), ); } @@ -79,7 +79,7 @@ class NovelPageState extends State { void updateIsar() { isar.writeTxnSync( - () => isar.aniDatas.putAllSync(novels), + () => isar.novDatas.putAllSync(novels), ); } diff --git a/lib/novel/novel_reader.dart b/lib/novel/novel_reader.dart index f6d188c..11a4af0 100644 --- a/lib/novel/novel_reader.dart +++ b/lib/novel/novel_reader.dart @@ -1,11 +1,11 @@ import 'dart:io'; -import 'package:anicross/models/info_models.dart'; -import 'package:anicross/novel/novel_parser.dart'; +import '/models/info_models.dart'; +import '/novel/novel_parser.dart'; import 'package:flutter/material.dart'; import 'package:path_provider/path_provider.dart'; class NovelReader extends StatefulWidget { - final AniData data; + final NovData data; const NovelReader({required this.data, super.key}); @override @@ -13,7 +13,7 @@ class NovelReader extends StatefulWidget { } class NovelReaderState extends State { - late final Novel novel = Novel(path: widget.data.mediaId!); + late final Novel novel = Novel(path: widget.data.path); late final String extract; final List chapters = []; diff --git a/lib/pages/info_page.dart b/lib/pages/info_page.dart new file mode 100644 index 0000000..e173bb1 --- /dev/null +++ b/lib/pages/info_page.dart @@ -0,0 +1,280 @@ +import 'dart:ui'; +import 'package:cached_network_image/cached_network_image.dart'; +import '/media/providers/anime_providers.dart'; +import '/models/info_models.dart'; +import '/media/providers/manga_providers.dart'; +import 'package:expandable_text/expandable_text.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:isar/isar.dart'; +import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; +import '../widgets/image.dart'; + +class InfoPage extends StatefulWidget { + final AniData data; + + const InfoPage({required this.data, super.key}); + + @override + State createState() => InfoPageState(); +} + +class InfoPageState extends State { + final List content = []; + final Isar isar = Isar.getInstance('later')!; + + @override + void initState() { + Future.microtask( + () async { + content.addAll( + switch (widget.data.type) { + 'anime' => await zoroList(widget.data.mediaId), + 'manga' => await dexReader(widget.data.mediaId), + _ => [], + }, + ); + if (content.isEmpty) { + content.addAll(await haniList(widget.data.title)); + } + if (mounted) { + setState(() {}); + } + }, + ); + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(context) { + final Widget expands = Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + ActionChip( + onPressed: () => setState( + () { + QueryBuilder media = + isar.aniDatas.filter().mediaIdMatches( + widget.data.mediaId, + ); + if (media.isEmptySync()) { + isar.writeTxnSync( + () => isar.aniDatas.putSync(widget.data), + ); + } else { + isar.writeTxnSync( + () => media.deleteAllSync(), + ); + } + }, + ), + avatar: (isar.aniDatas + .filter() + .mediaIdMatches(widget.data.mediaId) + .isEmptySync()) + ? const Icon(MdiIcons.bookmarkOutline) + : const Icon(MdiIcons.bookmark), + label: const Text("Later"), + ), + const SizedBox( + height: 10, + ), + ExpandableText( + widget.data.description, + expandText: "More", + collapseText: "Less", + maxLines: 4, + ), + const SizedBox( + height: 15, + ), + Wrap( + spacing: 3, + runSpacing: 7, + children: List.generate( + widget.data.tags.length.clamp(0, 15), + (index) { + return ActionChip( + visualDensity: VisualDensity.compact, + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + backgroundColor: Colors.blueGrey[800], + side: BorderSide.none, + padding: EdgeInsets.zero, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12)), + label: Text( + widget.data.tags[index], + ), + onPressed: () { + switch (widget.data.type) { + case 'anime': + { + context.go( + '/anime?tag=${widget.data.tags[index]}', + ); + } + case 'manga': + { + context.go( + '/manga?tag=${widget.data.tags[index]}', + ); + } + } + }, + ); + }, + ), + ), + ], + ); + return Container( + decoration: BoxDecoration( + gradient: const LinearGradient( + colors: [ + Color.fromARGB(100, 0, 0, 0), + Color.fromARGB(120, 0, 0, 0), + Color.fromARGB(255, 0, 0, 0) + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + image: DecorationImage( + fit: BoxFit.cover, + image: CachedNetworkImageProvider(widget.data.image), + ), + ), + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), + child: Scaffold( + backgroundColor: Colors.transparent, + body: SafeArea( + child: CustomScrollView( + slivers: [ + SliverAppBar( + backgroundColor: Colors.transparent, + floating: true, + title: Text(widget.data.title), + ), + SliverPadding( + padding: const EdgeInsets.all(15), + sliver: SliverToBoxAdapter( + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Flexible( + flex: 2, + fit: FlexFit.loose, + child: AniImage( + image: widget.data.image, + ), + ), + const SizedBox( + width: 20, + ), + Flexible( + flex: 4, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + widget.data.title, + style: const TextStyle( + fontSize: 20, + ), + ), + const SizedBox( + height: 20, + ), + if (MediaQuery.of(context).size.width / + MediaQuery.of(context).size.height > + 1.2) + expands, + ], + ), + ), + ], + ), + ), + ), + if (MediaQuery.of(context).size.width / + MediaQuery.of(context).size.height < + 1.2) + SliverPadding( + padding: const EdgeInsets.only( + left: 15, + right: 15, + ), + sliver: SliverToBoxAdapter( + child: expands, + ), + ), + if (content.isNotEmpty) + SliverPadding( + padding: const EdgeInsets.all(15), + sliver: SliverGrid( + gridDelegate: + const SliverGridDelegateWithMaxCrossAxisExtent( + mainAxisSpacing: 5, + crossAxisSpacing: 6, + maxCrossAxisExtent: 400, + mainAxisExtent: 100, + ), + delegate: SliverChildBuilderDelegate( + childCount: content.length, + (context, index) { + return GestureDetector( + onTap: () => context.push( + switch (widget.data.type) { + 'anime' => '/anime/info/viewer', + 'manga' => '/manga/info/viewer', + _ => '', + }, + extra: { + 'content': content[index], + 'contents': content, + }, + ), + child: Card( + elevation: 3, + child: Padding( + padding: const EdgeInsets.all(20), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Flexible( + child: Text( + content[index].title, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + Flexible( + child: Text( + '${(widget.data.type == 'anime') ? 'Episode:' : 'Chapter:'} ${content[index].number}', + ), + ), + ], + ), + ), + ), + ); + }, + ), + ), + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/later_page.dart b/lib/pages/later_page.dart similarity index 53% rename from lib/later_page.dart rename to lib/pages/later_page.dart index d98269d..4fea504 100644 --- a/lib/later_page.dart +++ b/lib/pages/later_page.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'dart:io'; -import 'package:anicross/models/info_models.dart'; -import 'package:anicross/widgets/grid.dart'; +import '/models/info_models.dart'; +import '/widgets/grid.dart'; import 'package:flutter/material.dart'; import 'package:isar/isar.dart'; import 'package:path_provider/path_provider.dart'; @@ -42,11 +42,51 @@ class LaterPageState extends State { return SafeArea( child: CustomScrollView( slivers: [ + const SliverToBoxAdapter( + child: Row( + children: [ + Expanded( + flex: 2, + child: Divider(), + ), + Spacer(), + Text( + 'Anime', + textScaleFactor: 1.2, + ), + Spacer(), + Expanded( + flex: 200, + child: Divider(), + ), + ], + ), + ), Grid( data: animeData, keep: false, length: animeData.length, ), + const SliverToBoxAdapter( + child: Row( + children: [ + Expanded( + flex: 2, + child: Divider(), + ), + Spacer(), + Text( + 'Manga', + textScaleFactor: 1.2, + ), + Spacer(), + Expanded( + flex: 200, + child: Divider(), + ), + ], + ), + ), Grid( data: mangaData, keep: false, diff --git a/lib/widgets/block.dart b/lib/widgets/block.dart index b86d586..4260c2d 100644 --- a/lib/widgets/block.dart +++ b/lib/widgets/block.dart @@ -1,15 +1,15 @@ -import 'package:anicross/models/info_models.dart'; -import 'package:anicross/widgets/image.dart'; +import '/widgets/image.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; class Block extends StatelessWidget { - final AniData data; + final dynamic data; const Block({ Key? key, required this.data, }) : super(key: key); + @override Widget build(context) { return GestureDetector( @@ -62,12 +62,13 @@ class Block extends StatelessWidget { overflow: TextOverflow.ellipsis, ), ), - if (data.score != null) + if (data.type != 'novel') Positioned( right: 10, top: 10, child: Container( - padding: const EdgeInsets.all(8.0), + padding: + const EdgeInsets.only(left: 7, right: 7, top: 5, bottom: 5), decoration: const BoxDecoration( color: Colors.black, //Yes borderRadius: BorderRadius.all( @@ -82,35 +83,37 @@ class Block extends StatelessWidget { ], ), child: Text( - "★ ${data.score ?? "n/a"}", + "★ ${data.score}", ), ), ), - Positioned( - left: 10, - top: 10, - child: Container( - padding: const EdgeInsets.all(8.0), - decoration: const BoxDecoration( - color: Colors.white, // Ill swear in russian for real lol :D - borderRadius: BorderRadius.all( - Radius.circular(30), - ), - boxShadow: [ - BoxShadow( - color: Color.fromARGB(94, 0, 0, 0), - spreadRadius: 3, - blurRadius: 3, - offset: Offset(0, 3), + if (data.type != 'novel') + Positioned( + left: 10, + top: 10, + child: Container( + padding: + const EdgeInsets.only(left: 7, right: 7, top: 5, bottom: 5), + decoration: const BoxDecoration( + color: Colors.white, // Ill swear in russian for real lol :D + borderRadius: BorderRadius.all( + Radius.circular(30), ), - ], - ), - child: Text( - "# ${data.count ?? "n/a"}", - style: const TextStyle(color: Colors.black), + boxShadow: [ + BoxShadow( + color: Color.fromARGB(94, 0, 0, 0), + spreadRadius: 3, + blurRadius: 3, + offset: Offset(0, 3), + ), + ], + ), + child: Text( + "# ${data.count}", + style: const TextStyle(color: Colors.black), + ), ), ), - ), ], ), ); diff --git a/lib/widgets/grid.dart b/lib/widgets/grid.dart index e969f95..ee9d855 100644 --- a/lib/widgets/grid.dart +++ b/lib/widgets/grid.dart @@ -1,9 +1,8 @@ import 'package:flutter/material.dart'; -import '../models/info_models.dart'; import 'block.dart'; class Grid extends StatefulWidget { - final List data; + final List data; final bool keep; final int? length; const Grid({ diff --git a/pubspec.lock b/pubspec.lock index c9e980f..5a2788e 100755 --- a/pubspec.lock +++ b/pubspec.lock @@ -109,10 +109,10 @@ packages: dependency: transitive description: name: build_runner_core - sha256: "30859c90e9ddaccc484f56303931f477b1f1ba2bab74aa32ed5d6ce15870f8cf" + sha256: "88a57f2ac99849362e73878334caa9f06ee25f31d2adced882b8337838c84e1e" url: "https://pub.dev" source: hosted - version: "7.2.8" + version: "7.2.9" built_collection: dependency: transitive description: @@ -125,10 +125,10 @@ packages: dependency: transitive description: name: built_value - sha256: "2f17434bd5d52a26762043d6b43bb53b3acd029b4d9071a329f46d67ef297e6d" + sha256: "7dd62d9faf105c434f3d829bbe9c4be02ec67f5ed94832222116122df67c5452" url: "https://pub.dev" source: hosted - version: "8.5.0" + version: "8.6.0" cached_network_image: dependency: "direct main" description: @@ -213,10 +213,10 @@ packages: dependency: transitive description: name: csslib - sha256: b36c7f7e24c0bdf1bf9a3da461c837d1de64b9f8beb190c9011d8c72a3dfd745 + sha256: "831883fb353c8bdc1d71979e5b342c7d88acfbc643113c14ae51e2442ea0f20f" url: "https://pub.dev" source: hosted - version: "0.17.2" + version: "0.17.3" dart_style: dependency: transitive description: @@ -294,6 +294,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_adaptive_scaffold: + dependency: "direct main" + description: + name: flutter_adaptive_scaffold + sha256: fbaa2e18a2d371d2092d05968d2aefc62c97bd588f25a56569b7b097f2d7442a + url: "https://pub.dev" + source: hosted + version: "0.1.4" flutter_blurhash: dependency: transitive description: @@ -359,10 +367,10 @@ packages: dependency: "direct main" description: name: go_router - sha256: b185cf91b5a6861f4c2a92ddaa65f8919909416ee033e00751f7c67ebee1588d + sha256: "00d1b67d6e9fa443331da229084dd3eb04407f5a2dff22940bd7bba6af5722c3" url: "https://pub.dev" source: hosted - version: "7.0.2" + version: "7.1.1" gql: dependency: transitive description: @@ -431,10 +439,10 @@ packages: dependency: transitive description: name: graphs - sha256: "772db3d53d23361d4ffcf5a9bb091cf3ee9b22f2be52cd107cd7a2683a89ba0e" + sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.3.1" hive: dependency: transitive description: @@ -551,10 +559,10 @@ packages: dependency: transitive description: name: logging - sha256: "04094f2eb032cbb06c6f6e8d3607edcfcb0455e2bb6cbc010cb01171dcb64e6d" + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.2.0" matcher: dependency: transitive description: @@ -584,25 +592,25 @@ packages: description: path: media_kit ref: main - resolved-ref: a84f5da8ebe4c2d865c5f1c1be15ed98173348df + resolved-ref: "29a367555c14f9460961e6c4876370f49c48f73a" url: "https://github.com/alexmercerind/media_kit.git" source: git - version: "0.0.8" + version: "0.0.9+1" media_kit_libs_android_video: dependency: "direct main" description: path: media_kit_libs_android_video ref: main - resolved-ref: a84f5da8ebe4c2d865c5f1c1be15ed98173348df + resolved-ref: "29a367555c14f9460961e6c4876370f49c48f73a" url: "https://github.com/alexmercerind/media_kit" source: git - version: "1.0.4" + version: "1.0.5" media_kit_libs_ios_video: dependency: "direct main" description: path: media_kit_libs_ios_video ref: main - resolved-ref: a84f5da8ebe4c2d865c5f1c1be15ed98173348df + resolved-ref: "29a367555c14f9460961e6c4876370f49c48f73a" url: "https://github.com/alexmercerind/media_kit" source: git version: "1.0.4" @@ -611,7 +619,7 @@ packages: description: path: media_kit_libs_linux ref: main - resolved-ref: a84f5da8ebe4c2d865c5f1c1be15ed98173348df + resolved-ref: "29a367555c14f9460961e6c4876370f49c48f73a" url: "https://github.com/alexmercerind/media_kit" source: git version: "1.0.2" @@ -620,7 +628,7 @@ packages: description: path: media_kit_libs_macos_video ref: main - resolved-ref: a84f5da8ebe4c2d865c5f1c1be15ed98173348df + resolved-ref: "29a367555c14f9460961e6c4876370f49c48f73a" url: "https://github.com/alexmercerind/media_kit" source: git version: "1.0.5" @@ -629,7 +637,7 @@ packages: description: path: media_kit_libs_windows_video ref: main - resolved-ref: a84f5da8ebe4c2d865c5f1c1be15ed98173348df + resolved-ref: "29a367555c14f9460961e6c4876370f49c48f73a" url: "https://github.com/alexmercerind/media_kit" source: git version: "1.0.2" @@ -638,10 +646,10 @@ packages: description: path: media_kit_video ref: main - resolved-ref: a84f5da8ebe4c2d865c5f1c1be15ed98173348df + resolved-ref: "29a367555c14f9460961e6c4876370f49c48f73a" url: "https://github.com/alexmercerind/media_kit.git" source: git - version: "0.0.10" + version: "0.0.12" meta: dependency: transitive description: @@ -718,10 +726,10 @@ packages: dependency: transitive description: name: path_provider_linux - sha256: "2ae08f2216225427e64ad224a24354221c2c7907e448e6e0e8b57b1eb9f10ad1" + sha256: ffbb8cc9ed2c9ec0e4b7a541e56fd79b138e8f47d2fb86815f15358a349b3b57 url: "https://pub.dev" source: hosted - version: "2.1.10" + version: "2.1.11" path_provider_platform_interface: dependency: transitive description: @@ -1147,10 +1155,10 @@ packages: dependency: "direct main" description: name: xml2json - sha256: c39bf22898b4458ccca91d5087ba86ebda078f0e2291bd73de80372a37d83e52 + sha256: "12ebc382f8684cafcc1e3ae5c982de82b5a52007a5d0ac79b22745d34f5ba975" url: "https://pub.dev" source: hosted - version: "6.0.0" + version: "6.1.0" xxh3: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 3a505a8..a23b25c 100755 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,4 +1,4 @@ -name: anicross +name: tokuwari description: Something I guess # The following line prevents the package from being accidentally published to @@ -18,6 +18,7 @@ dependencies: image: path: cached_network_image: + flutter_adaptive_scaffold: dio: file_picker: photo_view: