From 0119b2704c0ba9bc23fd90da6bab6374719ac800 Mon Sep 17 00:00:00 2001 From: hanami <1021210354@qq.com> Date: Thu, 5 Sep 2024 14:26:10 +0800 Subject: [PATCH] feat: hive db --- .github/workflows/flutter-ci.yml | 2 +- README.md | 36 +- lib/components/MdCardItemView2.dart | 4 +- lib/components/SettingModalView.dart | 186 ++++++++++ lib/components/SkillModalView.dart | 214 +++++------ lib/demo/generic1/main.dart | 28 -- lib/extension/Future.dart | 1 - lib/gen/assets.gen.dart | 11 + lib/hive/MyHive.dart | 11 +- lib/hive/db/ArticleHiveDb.dart | 62 ++++ lib/hive/db/BanListChangeHiveDb.dart | 23 +- lib/hive/db/CardHiveDb.dart | 17 +- lib/hive/db/DarkModeHiveDb.dart | 27 +- lib/hive/db/DeckTypeDetailHiveDb.dart | 23 +- lib/hive/db/NavHiveDb.dart | 23 +- lib/hive/db/PackHiveDb.dart | 14 +- lib/hive/db/PacksHiveDb.dart | 22 +- lib/hive/db/PowerRankingsHiveDb.dart | 58 +++ lib/hive/db/SkillHiveDb.dart | 20 +- lib/hive/db/TierListHiveDb.dart | 49 +++ lib/hive/db/TopDeckHiveDb.dart | 61 ++++ lib/http/ArticleApi.dart | 31 +- lib/http/TierListApi.dart | 18 +- lib/pages/articles/index.dart | 155 +++++--- .../components/BanListChangeView.dart | 54 ++- .../components/BanStatusCardView.dart | 169 ++++----- .../components/BanStatusCardView_2.dart | 168 --------- .../components/BanStatusCardView_3.dart | 196 ----------- lib/pages/ban_list_change/index.dart | 1 - lib/pages/cards_viewpager/CardView.dart | 7 +- .../deck_detail/components/DeckInfo.dart | 4 +- lib/pages/deck_type_detail/index.dart | 31 +- .../components/EventListView.dart | 216 ++++++++---- lib/pages/farming_and_event/index.dart | 7 +- lib/pages/home/components/NavItemCard.dart | 12 + lib/pages/home/index.dart | 193 ++-------- lib/pages/main/index.dart | 35 +- lib/pages/open_source_licenses/index.dart | 16 +- lib/pages/pack_detail/index.dart | 8 +- lib/pages/packs/components/PackListView.dart | 4 + lib/pages/packs/index.dart | 15 +- lib/pages/splash/BanStatusCardHiveDb.dart | 5 + lib/pages/splash/index.dart | 73 +--- .../tier_list/components/TierListView.dart | 133 +++---- .../tier_list/components/TierListView2.dart | 332 ------------------ lib/pages/tier_list/index.dart | 3 +- .../top_decks/components/TopDeckListView.dart | 212 +++++------ lib/pages/top_decks/index.dart | 54 +-- lib/store/AppStore.dart | 28 +- lib/store/BanCardStore.dart | 73 +++- lib/type/Article.dart | 22 +- lib/type/Article.g.dart | 61 ++++ lib/type/NavTab.dart | 9 +- lib/type/listViewData.dart | 14 +- pubspec.lock | 8 + pubspec.yaml | 1 + 56 files changed, 1488 insertions(+), 1772 deletions(-) create mode 100644 lib/components/SettingModalView.dart delete mode 100644 lib/demo/generic1/main.dart create mode 100644 lib/hive/db/ArticleHiveDb.dart create mode 100644 lib/hive/db/PowerRankingsHiveDb.dart create mode 100644 lib/hive/db/TierListHiveDb.dart create mode 100644 lib/hive/db/TopDeckHiveDb.dart delete mode 100644 lib/pages/ban_list_change/components/BanStatusCardView_2.dart delete mode 100644 lib/pages/ban_list_change/components/BanStatusCardView_3.dart delete mode 100644 lib/pages/tier_list/components/TierListView2.dart diff --git a/.github/workflows/flutter-ci.yml b/.github/workflows/flutter-ci.yml index a192495..e4c830a 100644 --- a/.github/workflows/flutter-ci.yml +++ b/.github/workflows/flutter-ci.yml @@ -33,4 +33,4 @@ jobs: with: allowUpdates: true artifacts: "releases/*-release.apk" - tag: 1.0 \ No newline at end of file + tag: ${{ github.event.inputs.TAG }} \ No newline at end of file diff --git a/README.md b/README.md index f6260b7..4dd0d28 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,32 @@ -# duel_links_meta +# Duel links meta app -A new Flutter project. +## Futures -## Getting Started +- Tier List Page +- Deck Type Detail Page +- Top Deck page +- Deck detail page +- Ban list change page +- Ban list cards page +- Article list page +- Pack set list page +- Pack set detail page +- Data local cache for fast viewing and reduce the api fetching -This project is a starting point for a Flutter application. +## Download 下载 -A few resources to get you started if this is your first Flutter project: +[从github下载](https://github.com/sukinosuki/duel-links-meta-flutter-app/releases) -- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) +## Stars -For help getting started with Flutter development, view the -[online documentation](https://docs.flutter.dev/), which offers tutorials, -samples, guidance on mobile development, and a full API reference. +If this project is helpful to you, please consider giving a star for support. + +如果本项目对你有帮助的话, 可以`star`支持一下. + +## Data source + +The app utilizes the following resources from [Duel Links Meta](https://www.duellinksmeta.com): + +- **APIs**: The app integrates with Duel Links Meta's APIs to fetch deck lists, card details, and other game-related data. +- **Images**: Card images and other graphics used in the app are sourced directly from Duel Links Meta. + \ No newline at end of file diff --git a/lib/components/MdCardItemView2.dart b/lib/components/MdCardItemView2.dart index 4bd7124..25cc075 100644 --- a/lib/components/MdCardItemView2.dart +++ b/lib/components/MdCardItemView2.dart @@ -39,14 +39,14 @@ class _MdCardItemViewState extends State { Future init() async { if (widget.mdCard != null && widget.mdCard?.type != '') return; - var card = await CardHiveDb.get(cardId); + var card = await CardHiveDb().get(cardId); if (card == null) { final (err, res) = await CardApi().getById(cardId).toCatch; if (err != null || res == null) return; card = res; - await CardHiveDb.setCard(card); + await CardHiveDb().set(card); } setState(() { diff --git a/lib/components/SettingModalView.dart b/lib/components/SettingModalView.dart new file mode 100644 index 0000000..cbf6d4c --- /dev/null +++ b/lib/components/SettingModalView.dart @@ -0,0 +1,186 @@ +import 'package:duel_links_meta/hive/MyHive.dart'; +import 'package:duel_links_meta/hive/db/DarkModeHiveDb.dart'; +import 'package:duel_links_meta/pages/open_source_licenses/index.dart'; +import 'package:duel_links_meta/store/AppStore.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:package_info_plus/package_info_plus.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class SettingModalView extends StatefulWidget { + const SettingModalView({super.key}); + + @override + State createState() => _SettingModalViewState(); +} + +class _SettingModalViewState extends State { + AppStore appStore = Get.put(AppStore()); + + PackageInfo? get packageInfo => appStore.packageInfo; + + String repos = 'https://github.com/sukinosuki/duel-links-meta-flutter-app'; + String duelLinksMetaUrl = 'https://www.duellinksmeta.com'; + + String githubHash = ''; + + void init() { + githubHash = const String.fromEnvironment('GITHUB_HASH'); + } + + @override + void initState() { + super.initState(); + + init(); + } + + @override + Widget build(BuildContext context) { + return ClipRRect( + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(24), + topRight: Radius.circular(24), + ), + child: Container( + padding: const EdgeInsets.only(top: 30), + color: Theme.of(context).colorScheme.onPrimary, + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Padding( + padding: EdgeInsets.symmetric(horizontal: 12), + child: Text( + 'Setting', + style: TextStyle(fontSize: 24), + ), + ), + Container( + padding: const EdgeInsets.symmetric(horizontal: 6), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Padding( + padding: EdgeInsets.only(left: 6), + child: Text('Theme mode'), + ), + Row( + children: [ + IconButton( + isSelected: !Get.isDarkMode, + onPressed: () { + Get.changeThemeMode(ThemeMode.light); + DarkModeHiveDb().set(ThemeMode.light); + }, + + icon: const Icon(Icons.sunny), + ), + IconButton( + isSelected: Get.isDarkMode, + onPressed: () { + Get.changeThemeMode(ThemeMode.dark); + DarkModeHiveDb().set(ThemeMode.dark); + }, + icon: const Icon(Icons.nightlight_rounded), + ), + ], + ), + ], + ), + ), + const Padding( + padding: EdgeInsets.symmetric(vertical: 12, horizontal: 12), + child: Text( + 'About', + style: TextStyle(fontSize: 24), + ), + ), + Container( + height: 50, + padding: const EdgeInsets.symmetric(horizontal: 12), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text('App version'), + Text(packageInfo?.version ?? ''), + ], + ), + ), + Container( + height: 50, + padding: const EdgeInsets.symmetric(horizontal: 12), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text('Commit ID'), + Text(githubHash), + ], + ), + ), + Material( + color: Colors.transparent, + child: InkWell( + onTap: () { + final uri = Uri.parse(repos); + launchUrl(uri).ignore(); + }, + child: Container( + height: 50, + padding: const EdgeInsets.symmetric(horizontal: 12), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text('Github'), + Icon(Icons.arrow_forward, color: Theme.of(context).iconTheme.color?.withOpacity(0.6)), + ], + ), + ), + ), + ), + Material( + color: Colors.transparent, + child: InkWell( + onTap: () { + Navigator.push(context, MaterialPageRoute(builder: (context) => const OpenSourceLicensePage())); + }, + child: Container( + height: 50, + padding: const EdgeInsets.symmetric(horizontal: 12), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text('Open source licenses'), + Icon(Icons.arrow_forward, color: Theme.of(context).iconTheme.color?.withOpacity(0.6),), + ], + ), + ), + ), + ), + Material( + color: Colors.transparent, + child: InkWell( + onTap: () { + final uri = Uri.parse(duelLinksMetaUrl); + launchUrl(uri).ignore(); + }, + child: Container( + height: 50, + padding: const EdgeInsets.symmetric(horizontal: 12), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text('Duel Link Meta'), + Icon(Icons.arrow_forward, color: Theme.of(context).iconTheme.color?.withOpacity(0.6),), + ], + ), + ), + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/components/SkillModalView.dart b/lib/components/SkillModalView.dart index 05694a1..b64c93c 100644 --- a/lib/components/SkillModalView.dart +++ b/lib/components/SkillModalView.dart @@ -1,5 +1,3 @@ -import 'dart:developer'; - import 'package:cached_network_image/cached_network_image.dart'; import 'package:duel_links_meta/extension/Future.dart'; import 'package:duel_links_meta/gen/assets.gen.dart'; @@ -7,7 +5,6 @@ import 'package:duel_links_meta/hive/db/SkillHiveDb.dart'; import 'package:duel_links_meta/http/SkillApi.dart'; import 'package:duel_links_meta/type/enum/PageStatus.dart'; import 'package:duel_links_meta/type/skill/Skill.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; class SkillModalView extends StatefulWidget { @@ -28,10 +25,8 @@ class _SkillModalViewState extends State { // Future fetchData({bool force = false}) async { - // await Future.delayed(Duration(seconds: 1)); - // return false; - var skill = await SkillHiveDb.get(_name); - final expireTime = await SkillHiveDb.getExpireTime(_name); + var skill = await SkillHiveDb().get(_name); + final expireTime = await SkillHiveDb().getExpireTime(_name); Exception? err; var shouldRefresh = false; @@ -44,8 +39,8 @@ class _SkillModalViewState extends State { return false; } - SkillHiveDb.set(skill).ignore(); - SkillHiveDb.setExpireTime(skill.name, DateTime.now().add(const Duration(days: 1))).ignore(); + SkillHiveDb().set(skill).ignore(); + SkillHiveDb().setExpireTime(skill.name, DateTime.now().add(const Duration(days: 1))).ignore(); } else { shouldRefresh = expireTime == null || expireTime.isBefore(DateTime.now()); } @@ -82,139 +77,100 @@ class _SkillModalViewState extends State { @override Widget build(BuildContext context) { - return Center( - child: ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(14)), - child: Container( - // constraints: const BoxConstraints(maxHeight: 400), - width: MediaQuery.of(context).size.width * 0.9, - decoration: BoxDecoration( - // color: Colors.white, - image: DecorationImage(image: Assets.images.modalBg.image().image, fit: BoxFit.fitWidth), - color: Theme.of(context).cardColor, - ), - padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 12), + return Container( + constraints: const BoxConstraints(maxHeight: 300), + child: Scrollbar( + child: SingleChildScrollView( + physics: const AlwaysScrollableScrollPhysics(), child: Stack( children: [ - Container( - constraints: const BoxConstraints(maxHeight: 300), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: const EdgeInsets.symmetric(vertical: 8), - child: Center( - child: Text( - widget.name, - style: const TextStyle(fontSize: 24, fontWeight: FontWeight.w500), - ), + Column( + children: [ + Text(_skill.description, style: const TextStyle(fontSize: 12)), + const SizedBox(height: 4), + if (_skill.relatedCards.isNotEmpty) + SizedBox( + height: 60, + child: ListView.builder( + scrollDirection: Axis.horizontal, + itemCount: _skill.relatedCards.length, + itemBuilder: (context, index) { + return Row( + children: [ + Container( + color: Colors.white38, + height: 60, + width: 60 / 1.4, + child: CachedNetworkImage( + fit: BoxFit.cover, + imageUrl: 'https://s3.duellinksmeta.com/cards/${_skill.relatedCards[index].oid}_w100.webp', + ), + ), + const SizedBox(width: 4), + ], + ); + }, ), ), - Expanded( - child: SingleChildScrollView( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(_skill.description, style: const TextStyle(fontSize: 12)), - const SizedBox(height: 4), - if (_skill.relatedCards.isNotEmpty) - SizedBox( - height: 60, - child: ListView.builder( - scrollDirection: Axis.horizontal, - itemCount: _skill.relatedCards.length, - itemBuilder: (context, index) { - return Row( - children: [ - Container( - color: Colors.white38, - height: 60, - width: 60 / 1.4, - child: CachedNetworkImage( - fit: BoxFit.cover, - imageUrl: 'https://s3.duellinksmeta.com/cards/${_skill.relatedCards[index].oid}_w100.webp', - ), - ), - const SizedBox(width: 4), - ], - ); - }, - ), - ) - ], - ), - const SizedBox(height: 5), - if (_skill.characters.isNotEmpty) - Column( - crossAxisAlignment: CrossAxisAlignment.start, + const SizedBox(height: 5), + if (_skill.characters.isNotEmpty) + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text('Characters', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500)), + ListView.builder( + padding: EdgeInsets.zero, + // scrollDirection: Axis.horizontal, + itemCount: _skill.characters.length, + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemBuilder: (context, index) { + return Container( + padding: EdgeInsets.only(bottom: 4), + child: Row( children: [ - const Text('Characters', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500)), - Container( + SizedBox( + width: 36, height: 42, - child: Scrollbar( - child: ListView.builder( - padding: EdgeInsets.zero, - scrollDirection: Axis.horizontal, - itemCount: _skill.characters.length, - itemBuilder: (context, index) { - return Row( - children: [ - Container( - width: 36, - height: 42, - child: CachedNetworkImage( - fit: BoxFit.cover, - imageUrl: - 'https://s3.duellinksmeta.com${_skill.characters[index].character.thumbnailImage}', - ), - ), - const SizedBox(width: 4), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(_skill.characters[index].character.name, - style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500)), - Text( - _skill.characters[index].how, - style: const TextStyle(fontSize: 12), - ), - ], - ), - const SizedBox(width: 4), - ], - ); - }, - ), + child: CachedNetworkImage( + fit: BoxFit.cover, + imageUrl: + 'https://s3.duellinksmeta.com${_skill.characters[index].character.thumbnailImage}', ), ), + const SizedBox(width: 4), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(_skill.characters[index].character.name, + style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500)), + Text( + _skill.characters[index].how, + style: const TextStyle(fontSize: 12), + ), + ], + ), ], ), - if (_skill.source != '') - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text('Source', style: TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.w500)), - Text(_skill.source, style: const TextStyle(color: Colors.white, fontSize: 12)), - ], - ) - ], + ); + }, ), - ), + ], ), - ], - ), + if (_skill.source != '') + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text('Source', style: TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.w500)), + Text(_skill.source, style: const TextStyle(color: Colors.white, fontSize: 12)), + ], + ) + ], ), - if (_pageStatus == PageStatus.loading) - const Center( - child: CircularProgressIndicator( - strokeWidth: 3, - color: Colors.white, - ), - ), + + if (_pageStatus == PageStatus.loading) Positioned(child: Center( + child: CircularProgressIndicator(), + )) ], ), ), diff --git a/lib/demo/generic1/main.dart b/lib/demo/generic1/main.dart deleted file mode 100644 index b82ac82..0000000 --- a/lib/demo/generic1/main.dart +++ /dev/null @@ -1,28 +0,0 @@ -void main() { - var box1 = Box('111'); - - print('box1.content: ${box1.content}, type: ${box1.content.runtimeType}'); - - var box2 = box1.rebox((value) => int.parse(value) + 233); - - print('box2.content: ${box2.content}, ${box2.content.runtimeType}'); - - var list1 = [1, 2, 3]; - var list2 = [4, 5, 6]; - - var list3 = [7, 8, ...list1, ...list2]; - - print('list3: ${list3}'); - -} - -class Box { - T content; - - Box(this.content); - - // Box rebox(R callback(T value)) { - Box rebox(R Function(T value) callback) { - return Box(callback(this.content)); - } -} diff --git a/lib/extension/Future.dart b/lib/extension/Future.dart index aff2311..f9a6c4e 100644 --- a/lib/extension/Future.dart +++ b/lib/extension/Future.dart @@ -2,7 +2,6 @@ import 'dart:developer'; import 'dart:io'; import 'package:duel_links_meta/extension/String.dart'; -import 'package:duel_links_meta/util/index.dart'; import 'package:get/get_connect/connect.dart'; extension FutureEx on Future> { diff --git a/lib/gen/assets.gen.dart b/lib/gen/assets.gen.dart index 9769ef1..579513a 100644 --- a/lib/gen/assets.gen.dart +++ b/lib/gen/assets.gen.dart @@ -260,10 +260,21 @@ class $AssetsImagesGen { ]; } +class $AssetsJsonGen { + const $AssetsJsonGen(); + + /// File path: assets/json/open_source_licenses.json + String get openSourceLicenses => 'assets/json/open_source_licenses.json'; + + /// List of all assets + List get values => [openSourceLicenses]; +} + class Assets { Assets._(); static const $AssetsImagesGen images = $AssetsImagesGen(); + static const $AssetsJsonGen json = $AssetsJsonGen(); } class AssetGenImage { diff --git a/lib/hive/MyHive.dart b/lib/hive/MyHive.dart index dcd9af2..d962e91 100644 --- a/lib/hive/MyHive.dart +++ b/lib/hive/MyHive.dart @@ -1,3 +1,4 @@ +import 'package:duel_links_meta/type/Article.dart'; import 'package:duel_links_meta/type/MdCard.dart'; import 'package:duel_links_meta/type/NavTab.dart'; import 'package:duel_links_meta/type/ban_list_change/BanListChange.dart'; @@ -12,7 +13,7 @@ import 'package:duel_links_meta/type/tier_list_top_tier/TierList_TopTier_Expire. import 'package:duel_links_meta/type/top_deck/TopDeck.dart'; import 'package:hive_flutter/adapters.dart'; -const boxName = 'todo_box'; +// const boxName = 'todo_box'; const boxName2 = 'todo_box2'; class MyHive { @@ -41,11 +42,12 @@ class MyHive { static const int skill_related_card = 23; static const int skill_related_character = 24; static const int skill_related_character_character = 25; + static const int article = 26; // static const int expire_data = 10; // static late Box> box; - static late Box box; + // static late Box box; static late LazyBox box2; MyHive._(); @@ -78,10 +80,11 @@ class MyHive { ..registerAdapter(SkillAdapter()) ..registerAdapter(SkillRelatedCardAdapter()) ..registerAdapter(SkillCharacterAdapter()) - ..registerAdapter(SkillCharacterCharacterAdapter()); + ..registerAdapter(SkillCharacterCharacterAdapter()) + ..registerAdapter(ArticleAdapter()); // Hive.registerAdapter(TLoginFormAdapter()); - box = await Hive.openBox(boxName); + // box = await Hive.openBox(boxName); box2 = await Hive.openLazyBox(boxName2); } } diff --git a/lib/hive/db/ArticleHiveDb.dart b/lib/hive/db/ArticleHiveDb.dart new file mode 100644 index 0000000..7aedf38 --- /dev/null +++ b/lib/hive/db/ArticleHiveDb.dart @@ -0,0 +1,62 @@ +import 'dart:developer'; + +import 'package:duel_links_meta/hive/MyHive.dart'; +import 'package:duel_links_meta/type/Article.dart'; + +class ArticleHiveDb { + factory ArticleHiveDb() { + return _instance; + } + + ArticleHiveDb._constructor(); + + static final _instance = ArticleHiveDb._constructor(); + + String _getKey(String category) { + return 'article:$category'; + } + + String _getExpireTimeKey(String category) { + return 'article_expire:$category'; + } + + Future?> get(String category) async { + final key = _getKey(category); + List
? articles; + + try { + final data = await MyHive.box2.get(key) as List?; + articles = data?.map((e) => e as Article).toList(); + } catch (e) { + log('转换失败: $e'); + return null; + } + + return articles; + } + + Future? getExpireTime(String category) async { + final key = _getExpireTimeKey(category); + DateTime? time; + try { + time = await MyHive.box2.get(key) as DateTime?; + } catch (e) { + log('转换失败: $e'); + return null; + } + + return time; + } + + Future set(List
articles, String category) { + final key = _getKey(category); + + return MyHive.box2.put(key, articles); + } + + Future setExpireTime(DateTime? time, String category) { + final key = _getExpireTimeKey(category); + + return MyHive.box2.put(key, time); + } +} diff --git a/lib/hive/db/BanListChangeHiveDb.dart b/lib/hive/db/BanListChangeHiveDb.dart index eda055d..0722fa9 100644 --- a/lib/hive/db/BanListChangeHiveDb.dart +++ b/lib/hive/db/BanListChangeHiveDb.dart @@ -4,36 +4,47 @@ import 'package:duel_links_meta/hive/MyHive.dart'; import 'package:duel_links_meta/type/ban_list_change/BanListChange.dart'; class BanListChangeHiveDb { - static const String _key = 'ban_list_change:list'; - static const String _expireKey = 'ban_list_change:fetch_date'; + factory BanListChangeHiveDb() { + return _instance; + } + + BanListChangeHiveDb._constructor(); + + static final BanListChangeHiveDb _instance = BanListChangeHiveDb._constructor(); - static Future?> get() async { + final String _key = 'ban_list_change:list'; + final String _expireKey = 'ban_list_change:fetch_date'; + + Future?> get() async { List? data; + try { final list = await MyHive.box2.get(_key) as List?; data = list?.map((e) => e as BanListChange).toList(); } catch (e) { + log('转换失败 $e'); return null; } return data; } - static Future getExpireTime() async { + Future getExpireTime() async { DateTime? expireTime; try { expireTime = await MyHive.box2.get(_expireKey) as DateTime?; } catch (e) { + log('转换失败 $e'); return null; } return expireTime; } - static Future set(List data) { + Future set(List data) { return MyHive.box2.put(_key, data); } - static Future setExpireTime(DateTime time) { + Future setExpireTime(DateTime time) { return MyHive.box2.put(_expireKey, time); } } diff --git a/lib/hive/db/CardHiveDb.dart b/lib/hive/db/CardHiveDb.dart index 016431d..dc24076 100644 --- a/lib/hive/db/CardHiveDb.dart +++ b/lib/hive/db/CardHiveDb.dart @@ -1,25 +1,36 @@ +import 'dart:developer'; + import 'package:duel_links_meta/hive/MyHive.dart'; import 'package:duel_links_meta/type/MdCard.dart'; class CardHiveDb { - static String _getKey(String id) { + factory CardHiveDb() { + return _instance; + } + + CardHiveDb._constructor(); + + static final CardHiveDb _instance = CardHiveDb._constructor(); + + String _getKey(String id) { return 'card:$id'; } - static Future get(String id) async { + Future get(String id) async { final key = _getKey(id); MdCard? card; try { card = await MyHive.box2.get(key) as MdCard?; } catch (e) { + log('转换失败'); return null; } return card; } - static Future setCard(MdCard card) async { + Future set(MdCard card) async { final key = _getKey(card.oid); return MyHive.box2.put(key, card); diff --git a/lib/hive/db/DarkModeHiveDb.dart b/lib/hive/db/DarkModeHiveDb.dart index 607aa68..56b4b1c 100644 --- a/lib/hive/db/DarkModeHiveDb.dart +++ b/lib/hive/db/DarkModeHiveDb.dart @@ -1,22 +1,31 @@ +import 'package:duel_links_meta/hive/MyHive.dart'; import 'package:flutter/material.dart'; -import '../MyHive.dart'; - class DarkModeHiveDb { - static const String _key = 'dark_mode'; - static void set() { - return MyHive.box2.put(_key, 'light').ignore(); + factory DarkModeHiveDb() { + return _instance; + } + + DarkModeHiveDb._constructor(); + + static final DarkModeHiveDb _instance = DarkModeHiveDb._constructor(); + + final String _key = 'dark_mode'; + + void set(ThemeMode mode) { + + return MyHive.box2.put(_key, mode.name).ignore(); } - static Future get() async { - final mode = await MyHive.box2.get('dark_mode'); + Future get() async { + final mode = await MyHive.box2.get(_key); - if (mode == 'dark') { + if (mode == ThemeMode.dark.name) { return ThemeMode.dark; } - if (mode == 'system') { + if (mode == ThemeMode.system.name) { return ThemeMode.system; } diff --git a/lib/hive/db/DeckTypeDetailHiveDb.dart b/lib/hive/db/DeckTypeDetailHiveDb.dart index 6b5e368..1e8c9ac 100644 --- a/lib/hive/db/DeckTypeDetailHiveDb.dart +++ b/lib/hive/db/DeckTypeDetailHiveDb.dart @@ -1,41 +1,52 @@ +import 'dart:developer'; + import 'package:duel_links_meta/hive/MyHive.dart'; import 'package:duel_links_meta/type/deck_type/DeckType.dart'; class DeckTypeDetailHiveDb { - static String _getKey(String deckTypeName) { + factory DeckTypeDetailHiveDb() { + return _instance; + } + + DeckTypeDetailHiveDb._constructor(); + + static final DeckTypeDetailHiveDb _instance = DeckTypeDetailHiveDb._constructor(); + + String _getKey(String deckTypeName) { return 'deck_type:$deckTypeName'; } - static String _getExpireTimeKey(String deckTypeName) { + String _getExpireTimeKey(String deckTypeName) { return 'deck_type_fetch_date:$deckTypeName'; } - static Future getDetail(String deckTypeName) async { + Future get(String deckTypeName) async { final key = _getKey(deckTypeName); DeckType? deckType; try { deckType = await MyHive.box2.get(key) as DeckType?; } catch (e) { + log('转换失败 $e'); return null; } return deckType; } - static Future getDetailExpireDate(String deckTypeName) async { + Future getExpireTime(String deckTypeName) async { final key = _getExpireTimeKey(deckTypeName); final date = await MyHive.box2.get(key) as DateTime?; return date; } - static Future setDeckType(DeckType deckType) { + Future set(DeckType deckType) { final key = _getKey(deckType.name); return MyHive.box2.put(key, deckType); } - static Future setDeckTypeExpireDate(DeckType deckType, DateTime date) { + Future setExpireTime(DeckType deckType, DateTime date) { final key = _getExpireTimeKey(deckType.name); return MyHive.box2.put(key, date); } diff --git a/lib/hive/db/NavHiveDb.dart b/lib/hive/db/NavHiveDb.dart index ad2a75b..32e4582 100644 --- a/lib/hive/db/NavHiveDb.dart +++ b/lib/hive/db/NavHiveDb.dart @@ -2,12 +2,17 @@ import 'package:duel_links_meta/hive/MyHive.dart'; import 'package:duel_links_meta/type/NavTab.dart'; class HomeHiveDb { - List? navTabList; + factory HomeHiveDb(){ + return _instance; + } - static const _navTabKey = 'nav_tab:list'; - static const _navTabFetchDateKey = 'nav_tab:fetch_date'; + HomeHiveDb._constructor(); + static final HomeHiveDb _instance = HomeHiveDb._constructor(); - static Future?> getNavTabList() async { + final String _navTabKey = 'nav_tab:list'; + final String _navTabFetchDateKey = 'nav_tab:fetch_date'; + + Future?> getNavTabList() async { final hiveData = await MyHive.box2.get(_navTabKey) as List?; if (hiveData == null) return null; @@ -19,25 +24,25 @@ class HomeHiveDb { } } - static Future deleteNavTabList() { + Future deleteNavTabList() { return MyHive.box2.delete(_navTabKey); } - static Future deleteNavTabListExpireTime() { + Future deleteNavTabListExpireTime() { return MyHive.box2.delete(_navTabFetchDateKey); } - static Future getNavTabListExpireTime() async { + Future getNavTabListExpireTime() async { final expireTime = await MyHive.box2.get(_navTabFetchDateKey) as DateTime?; return expireTime; } - static Future setNavTabList(List? data) { + Future setNavTabList(List? data) { return MyHive.box2.put(_navTabKey, data); } - static Future setNavTabListExpireTime(DateTime? time) { + Future setNavTabListExpireTime(DateTime? time) { return MyHive.box2.put(_navTabFetchDateKey, time); } } diff --git a/lib/hive/db/PackHiveDb.dart b/lib/hive/db/PackHiveDb.dart index 81dc601..386965c 100644 --- a/lib/hive/db/PackHiveDb.dart +++ b/lib/hive/db/PackHiveDb.dart @@ -1,11 +1,19 @@ import 'package:duel_links_meta/hive/MyHive.dart'; class PackHiveDb { - static String _getKey(String packId) { + factory PackHiveDb() { + return _instance; + } + + PackHiveDb._constructor(); + + static final _instance = PackHiveDb._constructor(); + + String _getKey(String packId) { return 'pack_card_ids:$packId'; } - static Future?>? getIds(String packId) async { + Future?>? getIds(String packId) async { final key = _getKey(packId); List? ids; @@ -18,7 +26,7 @@ class PackHiveDb { return ids; } - static Future setIds(String packId, List ids) { + Future setIds(String packId, List ids) { final key = _getKey(packId); return MyHive.box2.put(key, ids); diff --git a/lib/hive/db/PacksHiveDb.dart b/lib/hive/db/PacksHiveDb.dart index 5f8433e..fd5aefa 100644 --- a/lib/hive/db/PacksHiveDb.dart +++ b/lib/hive/db/PacksHiveDb.dart @@ -3,15 +3,23 @@ import 'dart:developer'; import 'package:duel_links_meta/hive/MyHive.dart'; import 'package:duel_links_meta/type/pack_set/PackSet.dart'; -class PackHiveDb { - static const String _key = 'pack_set:list'; - static const String _expireTimeKey = 'pack_set:list_last_fetch_date'; +class PacksHiveDb { + factory PacksHiveDb() { + return _instance; + } + + PacksHiveDb._constructor(); + + static final _instance = PacksHiveDb._constructor(); + + final String _key = 'pack_set:list'; + final String _expireTimeKey = 'pack_set:list_last_fetch_date'; - static Future set(List data) { + Future set(List data) { return MyHive.box2.put(_key, data); } - static Future?>? get() async { + Future?>? get() async { List? list; try { final data = await MyHive.box2.get(_key) as List?; @@ -23,11 +31,11 @@ class PackHiveDb { return list; } - static Future setExpireTime(DateTime date) { + Future setExpireTime(DateTime date) { return MyHive.box2.put(_expireTimeKey, date); } - static Future? getExpireTime() async { + Future? getExpireTime() async { DateTime? time; try { time = await MyHive.box2.get(_expireTimeKey) as DateTime?; diff --git a/lib/hive/db/PowerRankingsHiveDb.dart b/lib/hive/db/PowerRankingsHiveDb.dart new file mode 100644 index 0000000..903bb89 --- /dev/null +++ b/lib/hive/db/PowerRankingsHiveDb.dart @@ -0,0 +1,58 @@ +import 'dart:developer'; + +import 'package:duel_links_meta/hive/MyHive.dart'; +import 'package:duel_links_meta/type/deck_type/TierList_PowerRanking.dart'; + +class PowerRankingsHiveDb { + + factory PowerRankingsHiveDb() => _instance; + + PowerRankingsHiveDb._constructor(); + + static final _instance = PowerRankingsHiveDb._constructor(); + + String _getKey(bool isRush) { + return 'tier_list:power_rankings:${isRush ? 'rush' :''}'; + } + + String _getExpireTimeKey(bool isRush) { + return 'tier_list_expire_time:power_rankings:${isRush ? 'rush' : ''}'; + } + + Future?>? get({required bool isRush}) async{ + final key = _getKey(isRush); + List? list; + try { + final data = await MyHive.box2.get(key) as List?; + list = data?.map((e) => e as TierList_PowerRanking).toList(); + } catch(e){ + log('转换失败 $e'); + } + + return list; + } + + Future set(List list, {required bool isRush}) { + final key = _getKey(isRush); + return MyHive.box2.put(key, list); + } + + Future? getExpireTime({required bool isRush}) async{ + final key = _getExpireTimeKey(isRush); + DateTime? time; + try { + time = await MyHive.box2.get(key) as DateTime?; + } catch(e){ + log('转换失败 $e'); + } + + return time; + } + + Future setExpireTime(DateTime time, {required bool isRush}) { + final key = _getExpireTimeKey(isRush); + + return MyHive.box2.put(key, time); + } + +} \ No newline at end of file diff --git a/lib/hive/db/SkillHiveDb.dart b/lib/hive/db/SkillHiveDb.dart index 39bcfa4..2c56979 100644 --- a/lib/hive/db/SkillHiveDb.dart +++ b/lib/hive/db/SkillHiveDb.dart @@ -2,18 +2,24 @@ import 'dart:developer'; import 'package:duel_links_meta/hive/MyHive.dart'; import 'package:duel_links_meta/type/skill/Skill.dart'; -import 'package:get/get.dart'; class SkillHiveDb { - static String _getKey(String skillName) { + factory SkillHiveDb() { + return _instance; + } + SkillHiveDb._constructor(); + + static final _instance = SkillHiveDb._constructor() ; + + String _getKey(String skillName) { return 'skill:$skillName'; } - static String _getExpireTimeKey(String skillName) { + String _getExpireTimeKey(String skillName) { return 'skill_expire_time:$skillName'; } - static Future? get(String skillName) async { + Future? get(String skillName) async { final key = _getKey(skillName); Skill? skill; @@ -27,7 +33,7 @@ class SkillHiveDb { return skill; } - static Future? getExpireTime(String skillName) async { + Future? getExpireTime(String skillName) async { final key = _getExpireTimeKey(skillName); DateTime? time; try { @@ -40,12 +46,12 @@ class SkillHiveDb { return time; } - static Future set(Skill skill) { + Future set(Skill skill) { final key = _getKey(skill.name); return MyHive.box2.put(key, skill); } - static Future setExpireTime(String skillName, DateTime time) { + Future setExpireTime(String skillName, DateTime time) { final key = _getExpireTimeKey(skillName); return MyHive.box2.put(key, time); diff --git a/lib/hive/db/TierListHiveDb.dart b/lib/hive/db/TierListHiveDb.dart new file mode 100644 index 0000000..d61c1c9 --- /dev/null +++ b/lib/hive/db/TierListHiveDb.dart @@ -0,0 +1,49 @@ +import 'dart:developer'; + +import 'package:duel_links_meta/hive/MyHive.dart'; +import 'package:duel_links_meta/type/tier_list_top_tier/TierList_TopTier.dart'; + +class TierListHiveDb { + factory TierListHiveDb() => _instance; + + TierListHiveDb._constructor(); + + static final _instance = TierListHiveDb._constructor(); + + final _key = 'tier_list:top_tier'; + + final _expireTimeKey = 'tier_list_expire_time:top_tier'; + + Future?> get() async { + List? list; + + try { + final data = await MyHive.box2.get(_key) as List?; + list = data?.map((e) => e as TierList_TopTier).toList(); + } catch (e) { + log('转换失败 $e'); + } + + return list; + } + + Future getExpireTime() async { + DateTime? time; + + try { + time = await MyHive.box2.get(_expireTimeKey) as DateTime?; + } catch (e) { + log('转换失败 $e'); + } + + return time; + } + + Future set(List list) { + return MyHive.box2.put(_key, list); + } + + Future setExpireTime(DateTime time) { + return MyHive.box2.put(_expireTimeKey, time); + } +} diff --git a/lib/hive/db/TopDeckHiveDb.dart b/lib/hive/db/TopDeckHiveDb.dart new file mode 100644 index 0000000..dcc1da0 --- /dev/null +++ b/lib/hive/db/TopDeckHiveDb.dart @@ -0,0 +1,61 @@ +import 'dart:developer'; + +import 'package:duel_links_meta/hive/MyHive.dart'; +import 'package:duel_links_meta/type/top_deck/TopDeck.dart'; + +class TopDeckHiveDb { + + factory TopDeckHiveDb() { + return _instance; + } + TopDeckHiveDb._controller(); + + static final _instance = TopDeckHiveDb._controller(); + + + String _getKey(bool isRush){ + return 'top_deck:list:${isRush ? 'rush' : 'speed'}'; + } + + String _getExpireTimeKey(bool isRush) { + return 'top_deck:list:${isRush ? 'rush' : 'speed'}:refresh'; + } + + Future?> get({required bool isRush})async { + final key = _getKey(isRush); + + List? list; + try { + final data = await MyHive.box2.get(key) as List?; + list = data?.map((e) => e as TopDeck).toList(); + }catch(e) { + log('转换失败 $e'); + } + + return list; + } + + Future getExpireTime({required bool isRush}) async{ + final key = _getExpireTimeKey(isRush); + DateTime? time; + try { + time = await MyHive.box2.get(key) as DateTime?; + } catch(e) { + log('转换失败 $e'); + } + + return time; + } + + Future set(List list, {required bool isRush}) { + final key = _getKey(isRush); + + return MyHive.box2.put(key, list); + } + + Future setExpireTime(DateTime time, {required bool isRush}) { + final key = _getExpireTimeKey(isRush); + + return MyHive.box2.put(key, time); + } +} \ No newline at end of file diff --git a/lib/http/ArticleApi.dart b/lib/http/ArticleApi.dart index 21a0225..4a17d8f 100644 --- a/lib/http/ArticleApi.dart +++ b/lib/http/ArticleApi.dart @@ -3,28 +3,37 @@ import 'package:duel_links_meta/type/Article.dart'; import 'package:get/get.dart'; class ArticleApi extends Net { - Future>> articleList(Map _params) { - var params = {}; + Future>> articleList(Map _params) { + final params = {}; params['limit'] = _params['limit'] ?? '8'; params['page'] = _params['page']; params['field'] = '-markdown'; params['sort'] = '-featured,-date'; - params['hidden[\$ne]'] = 'true'; - params['category[\$ne]'] = 'quick-news'; + params[r'hidden[$ne]'] = 'true'; + params[r'category[$ne]'] = 'quick-news'; - return httpClient.get('/api/v1/articles', query: params); + return httpClient.get('/api/v1/articles', query: params, decoder: (data) => (data as List).map(Article.fromJson).toList()); } - Future>> activeEventLst(DateTime countdownDate) { - return httpClient.get('/api/v1/articles?category=event&countdownDate[\$gte]=$countdownDate&sort=-date&hidden[\$ne]=true&limit=10'); + Future>> activeEventLst(DateTime countdownDate) { + return httpClient.get( + '/api/v1/articles?category=event&countdownDate[\$gte]=$countdownDate&sort=-date&hidden[\$ne]=true&limit=10', + decoder: (data) => (data as List).map(Article.fromJson).toList(), + ); } - Future>> pastEventLst(DateTime countdownDate, int page) { - return httpClient.get('/api/v1/articles?category=event&countdownDate[\$lt]=$countdownDate&sort=-date&hidden[\$ne]=true&limit=10&page=$page'); + Future>> pastEventLst(DateTime countdownDate, int page) { + return httpClient.get( + '/api/v1/articles?category=event&countdownDate[\$lt]=$countdownDate&sort=-date&hidden[\$ne]=true&limit=10&page=$page', + decoder: (data) => (data as List).map(Article.fromJson).toList(), + ); } - Future>> pinnedFarmingEvent() { - return httpClient.get('/api/v1/articles?category=farming&subCategory=pinned&sort=-date&hidden[\$ne]=true'); + Future>> pinnedFarmingEvent() { + return httpClient.get( + r'/api/v1/articles?category=farming&subCategory=pinned&sort=-date&hidden[$ne]=true', + decoder: (data) => (data as List).map(Article.fromJson).toList(), + ); } } diff --git a/lib/http/TierListApi.dart b/lib/http/TierListApi.dart index 6c7bb27..c924a54 100644 --- a/lib/http/TierListApi.dart +++ b/lib/http/TierListApi.dart @@ -1,14 +1,18 @@ import 'package:duel_links_meta/http/http.dart'; +import 'package:duel_links_meta/type/deck_type/TierList_PowerRanking.dart'; +import 'package:duel_links_meta/type/tier_list_top_tier/TierList_TopTier.dart'; import 'package:get/get_connect/http/src/response/response.dart'; class TierListApi extends Net { + Future>> getTopTiers() => + httpClient.get('/api/v1/deck-types?tier[\$in]=0,1,2,3,4&limit=0&sort=name&fields=name,tier', + decoder: (data) => (data as List).map(TierList_TopTier.fromJson).toList()); - Future>> getTopTiers() => - httpClient.get('/api/v1/deck-types?tier[\$in]=0,1,2,3,4&limit=0&sort=name&fields=name,tier'); + Future>> getPowerRankings() => httpClient.get( + '/api/v1/deck-types?rush[\$ne]=true&tournamentPower[\$gte]=6&limit=0&sort=-tournamentPower&fields=name,tournamentPower,tournamentPowerTrend', + decoder: (data) => (data as List).map(TierList_PowerRanking.fromJson).toList()); - Future> getPowerRankings() => httpClient.get( - '/api/v1/deck-types?rush[\$ne]=true&tournamentPower[\$gte]=6&limit=0&sort=-tournamentPower&fields=name,tournamentPower,tournamentPowerTrend'); - - Future> getRushRankings() => httpClient.get( - '/api/v1/deck-types?rush=true&tournamentPower[\$gte]=6&limit=0&sort=-tournamentPower&fields=name,tournamentPower,tournamentPowerTrend,rush'); + Future>> getRushRankings() => httpClient.get( + '/api/v1/deck-types?rush=true&tournamentPower[\$gte]=6&limit=0&sort=-tournamentPower&fields=name,tournamentPower,tournamentPowerTrend,rush', + decoder: (data) => (data as List).map(TierList_PowerRanking.fromJson).toList()); } diff --git a/lib/pages/articles/index.dart b/lib/pages/articles/index.dart index a79dc3c..e7f0768 100644 --- a/lib/pages/articles/index.dart +++ b/lib/pages/articles/index.dart @@ -1,8 +1,8 @@ import 'dart:developer'; import 'package:duel_links_meta/components/ListFooter.dart'; -import 'package:duel_links_meta/components/Loading.dart'; import 'package:duel_links_meta/extension/Future.dart'; +import 'package:duel_links_meta/hive/db/ArticleHiveDb.dart'; import 'package:duel_links_meta/http/ArticleApi.dart'; import 'package:duel_links_meta/pages/articles/components/ArticleItem.dart'; import 'package:duel_links_meta/pages/webview/index.dart'; @@ -10,6 +10,7 @@ import 'package:duel_links_meta/type/Article.dart'; import 'package:duel_links_meta/type/enum/PageStatus.dart'; import 'package:duel_links_meta/type/listViewData.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; class ArticlesPage extends StatefulWidget { const ArticlesPage({super.key}); @@ -19,11 +20,12 @@ class ArticlesPage extends StatefulWidget { } class _ArticlesPageState extends State with AutomaticKeepAliveClientMixin { - final ScrollController _scrollController = ScrollController(); final _listViewData = ListViewData
(); + final _refreshIndicator = GlobalKey(); + List
get _articles => _listViewData.data; void handleTapArticleItem(Article article) { @@ -35,12 +37,24 @@ class _ArticlesPageState extends State with AutomaticKeepAliveClie // Future fetchData({bool isLoadMore = false}) async { - final params = {}; - params['limit'] = _listViewData.size.toString(); - params['page'] = _listViewData.page.toString(); + if (!isLoadMore) { + final list = await ArticleHiveDb().get(''); + if (list != null) { + setState(() { + _listViewData + ..data = list + ..pageStatus = PageStatus.success; + }); + } + } + + final params = { + 'limit': _listViewData.size.toString(), + 'page': _listViewData.page.toString(), + }; - final (err, res) = await ArticleApi().articleList(params).toCatch; - if (err != null) { + final (err, list) = await ArticleApi().articleList(params).toCatch; + if (err != null || list == null) { if (isLoadMore) { setState(() { _listViewData.loadMoreStatus = PageStatus.fail; @@ -50,53 +64,74 @@ class _ArticlesPageState extends State with AutomaticKeepAliveClie _listViewData.pageStatus = PageStatus.fail; }); } + return; } - final list = res!.map(Article.fromJson).toList(); _listViewData.page += 1; + if (isLoadMore) { + setState(() { + _listViewData.data.addAll(list); + }); + } else { + setState(() { + _listViewData.data = list; + }); + + ArticleHiveDb().set(list, '').ignore(); + } + setState(() { - _listViewData.data.addAll(list); - _listViewData..pageStatus = PageStatus.success - ..hasMore = list.length == _listViewData.size - ..loadMoreStatus = PageStatus.success; + _listViewData + ..pageStatus = PageStatus.success + ..hasMore = list.length == _listViewData.size + ..loadMoreStatus = PageStatus.success; }); } bool isReachBottom() { - log('bottom: ${_scrollController.position.maxScrollExtent - _scrollController.position.pixels}'); return _scrollController.position.maxScrollExtent - _scrollController.position.pixels <= 200; } void initScrollReachBottomListener() { _scrollController.addListener(() { - log('滚动中。。。'); if (_listViewData.pageStatus != PageStatus.success) { - log('不是加载成功,return'); return; } - if (_listViewData.hasMore && isReachBottom()) { - log('hasMore && 到达底部'); - if (_listViewData.loadMoreStatus == PageStatus.loading) { - log('到达底部,是加载更多中,不可执行 _loadMoreStatus ${_listViewData.loadMoreStatus}, ${PageStatus.loading}'); - return; - } - setState(() { - _listViewData.loadMoreStatus = PageStatus.loading; - }); + if (!_listViewData.hasMore || !isReachBottom()) { + return; + } - fetchData(isLoadMore: true); - } else{ - log('没有更多或者没到达底部'); + if (_listViewData.loadMoreStatus == PageStatus.loading) { + return; } + + setState(() { + _listViewData.loadMoreStatus = PageStatus.loading; + }); + + fetchData(isLoadMore: true); + }); + } + + Future _handleRefresh() async { + _listViewData.page = 1; + await fetchData(); + } + + void init() { + initScrollReachBottomListener(); + + SchedulerBinding.instance.addPostFrameCallback((timeStamp) { + _refreshIndicator.currentState?.show(); }); } @override void initState() { super.initState(); - fetchData(); - initScrollReachBottomListener(); + + init(); } @override @@ -110,34 +145,48 @@ class _ArticlesPageState extends State with AutomaticKeepAliveClie appBar: AppBar( title: const Text('Articles'), ), - body: Stack( - children: [ - AnimatedOpacity( - opacity: _listViewData.pageStatus == PageStatus.success ? 1 : 0, - duration: const Duration(milliseconds: 300), - child: ListView.separated( - controller: _scrollController, - padding: const EdgeInsets.only(top: 8), - itemCount: _articles.length >= _listViewData.size ? _articles.length + 1 : _articles.length, - itemBuilder: (context, index) { - return Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Padding( + body: RefreshIndicator( + onRefresh: _handleRefresh, + key: _refreshIndicator, + child: Stack( + fit: StackFit.expand, + children: [ + AnimatedOpacity( + opacity: _listViewData.pageStatus == PageStatus.success ? 1 : 0, + duration: const Duration(milliseconds: 300), + child: ListView.separated( + controller: _scrollController, + padding: const EdgeInsets.only(top: 8), + itemCount: _articles.length >= _listViewData.size ? _articles.length + 1 : _articles.length, + itemBuilder: (context, index) { + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Padding( padding: const EdgeInsets.symmetric(horizontal: 5), child: _articles.length >= _listViewData.size && index == _articles.length ? ListFooter(loadMoreStatus: _listViewData.loadMoreStatus, hasMore: _listViewData.hasMore) - : ArticleItem(article: _articles[index], onTap: handleTapArticleItem)), - ], - ); - }, - separatorBuilder: (BuildContext context, int index) { - return const SizedBox(height: 8); - }, + : ArticleItem(article: _articles[index], onTap: handleTapArticleItem), + ), + ], + ); + }, + separatorBuilder: (BuildContext context, int index) { + return const SizedBox(height: 8); + }, + ), ), - ), - if (_listViewData.pageStatus != PageStatus.success) const Positioned.fill(child: Center(child: Loading())), - ], + + if (_listViewData.pageStatus == PageStatus.fail) + const SingleChildScrollView( + physics: AlwaysScrollableScrollPhysics(), + ), + if (_listViewData.pageStatus == PageStatus.fail) + const Center( + child: Text('Loading failed'), + ), + ], + ), ), ); } diff --git a/lib/pages/ban_list_change/components/BanListChangeView.dart b/lib/pages/ban_list_change/components/BanListChangeView.dart index b01fd76..aa46cbe 100644 --- a/lib/pages/ban_list_change/components/BanListChangeView.dart +++ b/lib/pages/ban_list_change/components/BanListChangeView.dart @@ -25,26 +25,20 @@ class BanListChangeView extends StatefulWidget { class _BanListChangeViewState extends State with AutomaticKeepAliveClientMixin { var _pageStatus = PageStatus.loading; - final _refreshIndicatorKey = GlobalKey(); - - List> banListChangeGroup = []; - + List> _banListChangeGroup = []; BanListChange? currentBanListChange; + final formatter = DateFormat('MM-dd'); + bool _isInit = false; // - Future fetchBanListChanges({bool force = false}) async { + Future fetchData({bool force = false}) async { var reRefreshFlag = false; - var list = await BanListChangeHiveDb.get(); - final expireTime = await BanListChangeHiveDb.getExpireTime(); + var list = await BanListChangeHiveDb().get(); + final expireTime = await BanListChangeHiveDb().getExpireTime(); if (list == null || force) { - if (list == null) { - log('本地没数据'); - } else { - log('强制刷新'); - } final params = { r'rush[$ne]': 'true', 'sort': '-date,-announced', @@ -62,19 +56,12 @@ class _BanListChangeViewState extends State with AutomaticKee } list = res; - BanListChangeHiveDb.set(list).ignore(); - BanListChangeHiveDb.setExpireTime(DateTime.now().add(const Duration(days: 1))).ignore(); - log('本地保存数据'); + BanListChangeHiveDb().set(list).ignore(); + BanListChangeHiveDb().setExpireTime(DateTime.now().add(const Duration(days: 1))).ignore(); } else { - log('本地获取到数据'); reRefreshFlag = expireTime == null || expireTime.isBefore(DateTime.now()); - if (reRefreshFlag) { - log('本地数据已过期'); - } } - final formatter = DateFormat('MM-dd'); - final dataGroupList = >[]; list.forEach((item) { @@ -95,7 +82,7 @@ class _BanListChangeViewState extends State with AutomaticKee }); setState(() { - banListChangeGroup = dataGroupList; + _banListChangeGroup = dataGroupList; currentBanListChange = dataGroupList[0].items[0]; _pageStatus = PageStatus.success; }); @@ -109,7 +96,7 @@ class _BanListChangeViewState extends State with AutomaticKee for (var i = 0; i < currentBanListChange!.changes.length; i++) { final item = currentBanListChange!.changes[i]; - var card = await CardHiveDb.get(item.card!.oid); + var card = await CardHiveDb().get(item.card!.oid); card ??= MdCard() ..oid = item.card!.oid ..name = item.card!.name; @@ -117,7 +104,6 @@ class _BanListChangeViewState extends State with AutomaticKee cards.add(card); } - // TODO await showDialog( context: context, builder: (context) => Dialog.fullscreen( @@ -128,7 +114,7 @@ class _BanListChangeViewState extends State with AutomaticKee } void handlePickerConfirm(int yearIndex, int itemIndex) { - final banListChange = banListChangeGroup[yearIndex].items[itemIndex]; + final banListChange = _banListChangeGroup[yearIndex].items[itemIndex]; setState(() { currentBanListChange = banListChange; @@ -141,23 +127,20 @@ class _BanListChangeViewState extends State with AutomaticKee void showUpdatesDatePicker() { showModalBottomSheet( context: context, - // backgroundColor: Colors.black12, builder: (context) => BanListChangePicker( - data: banListChangeGroup, + data: _banListChangeGroup, onConfirm: handlePickerConfirm, ), ); } - bool isInit = false; Future _handleRefresh() async { - final shouldRefresh = await fetchBanListChanges(); - isInit = true; + final shouldRefresh = await fetchData(); + _isInit = true; if (shouldRefresh) { - await fetchBanListChanges(force: true); + await fetchData(force: true); } - log('_handleRefresh'); } @override @@ -175,6 +158,7 @@ class _BanListChangeViewState extends State with AutomaticKee @override Widget build(BuildContext context) { super.build(context); + return RefreshIndicator( key: _refreshIndicatorKey, onRefresh: _handleRefresh, @@ -201,7 +185,7 @@ class _BanListChangeViewState extends State with AutomaticKee const Icon(Icons.keyboard_arrow_down, size: 16), ], ), - ) + ), ], ), ), @@ -220,6 +204,10 @@ class _BanListChangeViewState extends State with AutomaticKee ], ), ), + + if (_pageStatus == PageStatus.fail) const Center( + child: Text('Loading failed'), + ) ], ), ); diff --git a/lib/pages/ban_list_change/components/BanStatusCardView.dart b/lib/pages/ban_list_change/components/BanStatusCardView.dart index 3342a42..6265071 100644 --- a/lib/pages/ban_list_change/components/BanStatusCardView.dart +++ b/lib/pages/ban_list_change/components/BanStatusCardView.dart @@ -9,7 +9,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:get/get.dart'; - class BanStatusCardView extends StatefulWidget { const BanStatusCardView({super.key}); @@ -27,16 +26,10 @@ class _BanStatusCardViewState extends State with AutomaticKee Group(key: 'Limited 3', data: banCardsStore.group.value['Limited 3'] ?? []) ]; - // List> get groups2 => [ - // Group(key: 'Limited 2', data: banCardsStore.group.value['Limited 2'] ?? []), - // Group(key: 'Limited 3', data: banCardsStore.group.value['Limited 3'] ?? []) - // ]; - var _initFlag = false; // void handleTapCardItem(List cards, int index) { - // TODO showDialog( context: context, builder: (context) => Dialog.fullscreen( @@ -47,12 +40,16 @@ class _BanStatusCardViewState extends State with AutomaticKee } Future init() async { - await Future.delayed(const Duration(milliseconds: 300)); + await Future.delayed(const Duration(milliseconds: 200)); setState(() { _initFlag = true; }); } + Future _handleRefresh() async { + await banCardsStore.fetchData(); + } + @override void initState() { super.initState(); @@ -63,85 +60,99 @@ class _BanStatusCardViewState extends State with AutomaticKee Widget build(BuildContext context) { super.build(context); - return Stack( - children: [ - Obx( - () => AnimatedOpacity( - opacity: (banCardsStore.pageStatus.value == PageStatus.success && _initFlag) ? 1 : 0, - duration: const Duration(milliseconds: 300), - child: SingleChildScrollView( - padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 4), - child: Column( - children: groups1 - .map( - (group) => Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 4), - child: Row( - children: [ - ClipRRect( - borderRadius: BorderRadius.circular(20), - child: SvgPicture.asset( - 'assets/images/icon_${group.key.toLowerCase()}.svg', - width: 20, - height: 20, + return RefreshIndicator( + onRefresh: _handleRefresh, + child: Stack( + fit: StackFit.expand, + children: [ + Obx( + () => AnimatedOpacity( + opacity: (banCardsStore.pageStatus.value == PageStatus.success && _initFlag) ? 1 : 0, + duration: const Duration(milliseconds: 300), + child: SingleChildScrollView( + padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 4), + child: Column( + children: groups1 + .map( + (group) => Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 4), + child: Row( + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(20), + child: SvgPicture.asset( + 'assets/images/icon_${group.key.toLowerCase()}.svg', + width: 20, + height: 20, + ), ), - ), - const SizedBox(width: 4), - Text( - group.key, - style: const TextStyle(fontSize: 20), - ), - ], - ), - ), - Card( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(6), + const SizedBox(width: 4), + Text( + group.key, + style: const TextStyle(fontSize: 20), + ), + ], + ), ), - child: Container( - padding: const EdgeInsets.only(left: 8, right: 8, top: 8), - child: GridView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: group.data.length, - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 5, - childAspectRatio: 0.57, - crossAxisSpacing: 6, + Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6), + ), + child: Container( + padding: const EdgeInsets.only(left: 8, right: 8, top: 8), + child: GridView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: group.data.length, + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 5, + childAspectRatio: 0.57, + crossAxisSpacing: 6, + ), + itemBuilder: (context, cardIndex) { + return MdCardItemView2( + id: group.data[cardIndex].oid, + mdCard: group.data[cardIndex], + onTap: (card) => handleTapCardItem(group.data, cardIndex), + ); + }, ), - itemBuilder: (context, _index) { - return MdCardItemView2( - id: group.data[_index].oid, - mdCard: group.data[_index], - onTap: (card) => handleTapCardItem(group.data, _index), - ); - }, ), ), - ), - ], - ), - ) - .toList(), + ], + ), + ) + .toList(), + ), ), ), ), - ), - Obx( - () { - return banCardsStore.pageStatus.value == PageStatus.loading - ? const Positioned.fill( - child: Center( - child: Loading(), - ), - ) - : const SizedBox(); - }, - ) - ], + Obx( + () { + return banCardsStore.pageStatus.value == PageStatus.loading + ? const Positioned.fill( + child: Center( + child: Loading(), + ), + ) + : const SizedBox(); + }, + ), + Obx(() => banCardsStore.pageStatus.value == PageStatus.fail + ? const SingleChildScrollView( + physics: AlwaysScrollableScrollPhysics(), + ) + : const SizedBox()), + Obx(() => banCardsStore.pageStatus.value == PageStatus.fail + ? const Center( + child: Text('Loading failed'), + ) + : const SizedBox(),), + ], + ), ); } diff --git a/lib/pages/ban_list_change/components/BanStatusCardView_2.dart b/lib/pages/ban_list_change/components/BanStatusCardView_2.dart deleted file mode 100644 index 86d66c6..0000000 --- a/lib/pages/ban_list_change/components/BanStatusCardView_2.dart +++ /dev/null @@ -1,168 +0,0 @@ -import 'dart:developer'; - -import 'package:duel_links_meta/components/Loading.dart'; -import 'package:duel_links_meta/components/MdCardItemView.dart'; -import 'package:duel_links_meta/components/MdCardItemView2.dart'; -import 'package:duel_links_meta/extension/Future.dart'; -import 'package:duel_links_meta/hive/MyHive.dart'; -import 'package:duel_links_meta/http/CardApi.dart'; -import 'package:duel_links_meta/pages/cards_viewpager/index.dart'; -import 'package:duel_links_meta/type/MdCard.dart'; -import 'package:duel_links_meta/type/ban_list_change/BanStatusCard.dart'; -import 'package:duel_links_meta/type/enum/PageStatus.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_svg/svg.dart'; - -class BanStatusCardView extends StatefulWidget { - const BanStatusCardView({super.key}); - - @override - State createState() => _BanStatusCardViewState(); -} - -class _BanStatusCardViewState extends State with AutomaticKeepAliveClientMixin { - Map> banStatus2CardsGroup = {}; - var _pageStatus = PageStatus.loading; - - void handleTapCardItem(List cards, int index) { - showDialog( - context: context, - builder: (context) => Dialog.fullscreen( - backgroundColor: Colors.black87.withOpacity(0.3), - child: CardsViewpagerPage(cards: cards, index: index), - ), - ); - } - - Future fetchCards({bool force = false}) async { - var banStatusCardIdsKey = 'ban_status:card_ids'; - var banStatusCardIdsFetchDateKey = 'ban_status:card_ids'; - - var cardIds = await MyHive.box2.get(banStatusCardIdsKey); - - var refreshFlag = false; - - var list = []; - - if (cardIds == null) { - final params = { - 'limit': '0', - r'banStatus[$exists]': 'true', - r'alternateArt[$ne]': 'true', - r'rush[$ne]': 'true', - // 'fields': 'oid,banStatus' - }; - - final (err, res) = await CardApi().list(params).toCatch; - if (err != null) { - setState(() { - _pageStatus = PageStatus.fail; - }); - return false; - } - list = res!.map(BanStatusCard.fromJson).toList(); - MyHive.box2.put(banStatusCardIdsKey, list); - } else { - log('本地有数据'); - await Future.delayed(Duration(milliseconds: 300)); - - try { - list = (cardIds as List).map((e) => e as BanStatusCard).toList(); - - list.forEach((element) { - // TODO - var card = MyHive.box2.get('card:${element.oid}'); - }); - } catch (e) { - return true; - } - } - - final group = >{ - 'Forbidden': [], - 'Limited 1': [], - 'Limited 2': [], - 'Limited 3': [], - }; - - list.forEach((item) { - group[item.banStatus]?.add(item.oid); - }); - - setState(() { - banStatus2CardsGroup = group; - _pageStatus = PageStatus.success; - }); - - return refreshFlag; - } - - @override - void initState() { - super.initState(); - fetchCards(); - } - - @override - Widget build(BuildContext context) { - super.build(context); - - return Stack( - children: [ - AnimatedOpacity( - opacity: _pageStatus == PageStatus.success ? 1 : 0, - duration: const Duration(milliseconds: 400), - child: ListView.builder( - itemCount: banStatus2CardsGroup.keys.length, - shrinkWrap: true, - itemBuilder: (context, index) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.symmetric(vertical: 10), - child: Row( - children: [ - ClipRRect( - borderRadius: BorderRadius.circular(20), - child: SvgPicture.asset('assets/images/icon_${banStatus2CardsGroup.keys.elementAt(index).toLowerCase()}.svg', - width: 20, height: 20), - ), - const SizedBox(width: 4), - Text(banStatus2CardsGroup.keys.elementAt(index), style: const TextStyle(fontSize: 20)), - ], - ), - ), - Card( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(6), - ), - child: Container( - padding: const EdgeInsets.only(left: 8, right: 8, top: 8), - child: GridView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: banStatus2CardsGroup[banStatus2CardsGroup.keys.elementAt(index)]!.length, - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 5, childAspectRatio: 0.57, crossAxisSpacing: 6), - itemBuilder: (context, _index) { - return MdCardItemView2( - id: banStatus2CardsGroup[banStatus2CardsGroup.keys.elementAt(index)]![_index], - // mdCard: banStatus2CardsGroup[key]![index], - // onTap: (card) => handleTapCardItem(banStatus2CardsGroup[key]!, index), - ); - }), - ), - ), - ], - ); - }), - ), - if (_pageStatus == PageStatus.loading) const Positioned.fill(child: Center(child: Loading())) - ], - ); - } - - @override - bool get wantKeepAlive => true; -} diff --git a/lib/pages/ban_list_change/components/BanStatusCardView_3.dart b/lib/pages/ban_list_change/components/BanStatusCardView_3.dart deleted file mode 100644 index 1afddde..0000000 --- a/lib/pages/ban_list_change/components/BanStatusCardView_3.dart +++ /dev/null @@ -1,196 +0,0 @@ -import 'dart:developer'; - -import 'package:duel_links_meta/components/Loading.dart'; -import 'package:duel_links_meta/components/MdCardItemView.dart'; -import 'package:duel_links_meta/components/MdCardItemView2.dart'; -import 'package:duel_links_meta/extension/Future.dart'; -import 'package:duel_links_meta/hive/MyHive.dart'; -import 'package:duel_links_meta/http/CardApi.dart'; -import 'package:duel_links_meta/pages/cards_viewpager/index.dart'; -import 'package:duel_links_meta/store/BanCardStore.dart'; -import 'package:duel_links_meta/type/MdCard.dart'; -import 'package:duel_links_meta/type/ban_list_change/BanStatusCard.dart'; -import 'package:duel_links_meta/type/enum/PageStatus.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_svg/svg.dart'; -import 'package:get/get.dart'; - -class BanStatusCardView extends StatefulWidget { - const BanStatusCardView({super.key}); - - @override - State createState() => _BanStatusCardViewState(); -} - -class _BanStatusCardViewState extends State with AutomaticKeepAliveClientMixin { - // Map> banStatus2CardsGroup = {}; - - // var _pageStatus = PageStatus.loading; - - var banCardsStore = Get.put(BanCardStore()); - - // - void handleTapCardItem(List cards, int index) { - // TODO - showDialog( - context: context, - builder: (context) => Dialog.fullscreen( - backgroundColor: Colors.black87.withOpacity(0.3), - child: CardsViewpagerPage(cards: cards, index: index), - ), - ); - } - - // - // Future fetchCards({bool force = false}) async { - // const banStatusCardIdsKey = 'ban_status:card_ids'; - // var banStatusCardIdsFetchDateKey = 'ban_status:card_ids'; - // - // var cardIds = await MyHive.box2.get(banStatusCardIdsKey) as List?; - // - // var refreshFlag = false; - // - // var list = []; - // - // if (cardIds == null || force) { - // final params = { - // 'limit': '0', - // r'banStatus[$exists]': 'true', - // r'alternateArt[$ne]': 'true', - // r'rush[$ne]': 'true', - // // 'fields': 'oid,banStatus' - // }; - // - // final (err, res) = await CardApi().list(params).toCatch; - // if (err != null) { - // setState(() { - // _pageStatus = PageStatus.fail; - // }); - // return false; - // } - // list = res!.map(MdCard.fromJson).toList(); - // - // // 保存ids - // MyHive.box2.put(banStatusCardIdsKey, list.map((e) => e.oid).toList()); - // // 保存card - // list.forEach((element) { - // MyHive.box2.put('card:${element.oid}', element); - // }); - // } else { - // log('本地有数据'); - // // await Future.delayed(Duration(milliseconds: 300)); - // - // try { - // // list = (cardIds as List).map((e) => (MyHive.box2.get('card:$e') ?? MdCard()..oid = e) as MdCard).toList(); - // // list = cardIds.map((e) => (MyHive.box2.get('card:$e') ?? MdCard()..oid = e) as MdCard).toList(); - // - // var start = DateTime.now(); - // for (var i=0; i < cardIds.length;i++) { - // final card = await MyHive.box2.get('card:${cardIds[i]}') as MdCard?; - // - // list.add(card??MdCard()..oid = cardIds[i]); - // } - // log('读取本地数据消耗时间: ${DateTime.now().difference(start).inMilliseconds}'); - // } catch (e) { - // MyHive.box2.delete(banStatusCardIdsKey); - // - // log('转换失败 $e'); - // - // return true; - // } - // } - // - // final group = >{ - // 'Forbidden': [], - // 'Limited 1': [], - // 'Limited 2': [], - // 'Limited 3': [], - // }; - // - // list.forEach((item) { - // group[item.banStatus]?.add(item); - // }); - // - // setState(() { - // banStatus2CardsGroup = group; - // _pageStatus = PageStatus.success; - // }); - // - // return refreshFlag; - // } - - @override - void initState() { - super.initState(); - // fetchCards(); - } - - @override - Widget build(BuildContext context) { - super.build(context); - - return Stack( - children: [ - Obx(() => - AnimatedOpacity( - opacity: banCardsStore.pageStatus.value == PageStatus.success ? 1 : 0, - duration: const Duration(milliseconds: 300), - child: SingleChildScrollView( - padding: const EdgeInsets.symmetric(horizontal: 4), - child: Column( - children: banCardsStore.group.value.keys - .map((key) => Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 4), - child: Row( - children: [ - ClipRRect( - borderRadius: BorderRadius.circular(20), - child: SvgPicture.asset('assets/images/icon_${key.toLowerCase()}.svg', width: 20, height: 20), - ), - const SizedBox(width: 4), - Text(key, style: const TextStyle(fontSize: 20)), - ], - ), - ), - Card( - // margin: EdgeInsets.all(0), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(6), - ), - child: Container( - padding: const EdgeInsets.only(left: 8, right: 8, top: 8), - child: GridView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: banCardsStore.group?.value[key]!.length, - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 5, childAspectRatio: 0.57, crossAxisSpacing: 6), - itemBuilder: (context, _index) { - return MdCardItemView2( - id: banCardsStore.group.value[key]![_index].oid, - mdCard: banCardsStore.group.value[key]![_index], - onTap: (card) => handleTapCardItem(banCardsStore.group.value[key]!, _index), - ); - }), - ), - ), - ], - )) - .toList(), - ), - ), - ) - ), - Obx( - () => banCardsStore.pageStatus.value == PageStatus.loading ? const Positioned.fill(child: Center(child: Loading())) : SizedBox(), - ) - ], - ); - } - - @override - bool get wantKeepAlive => true; -} diff --git a/lib/pages/ban_list_change/index.dart b/lib/pages/ban_list_change/index.dart index 62c6bbe..6e9ad06 100644 --- a/lib/pages/ban_list_change/index.dart +++ b/lib/pages/ban_list_change/index.dart @@ -24,7 +24,6 @@ class _BanListChangePageState extends State with SingleTicker child: Scaffold( appBar: AppBar( title: const TabBar( - // center tab tabAlignment: TabAlignment.start, isScrollable: true, dividerHeight: 0, diff --git a/lib/pages/cards_viewpager/CardView.dart b/lib/pages/cards_viewpager/CardView.dart index 62e48a5..fa0c16c 100644 --- a/lib/pages/cards_viewpager/CardView.dart +++ b/lib/pages/cards_viewpager/CardView.dart @@ -1,16 +1,13 @@ import 'dart:developer'; import 'package:cached_network_image/cached_network_image.dart'; -import 'package:duel_links_meta/constant/colors.dart'; import 'package:duel_links_meta/extension/Future.dart'; import 'package:duel_links_meta/gen/assets.gen.dart'; -import 'package:duel_links_meta/hive/MyHive.dart'; import 'package:duel_links_meta/hive/db/CardHiveDb.dart'; import 'package:duel_links_meta/http/CardApi.dart'; import 'package:duel_links_meta/store/BanCardStore.dart'; import 'package:duel_links_meta/type/MdCard.dart'; import 'package:duel_links_meta/util/time_util.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; @@ -62,7 +59,7 @@ class _CardViewState extends State { return; } - final card = await CardHiveDb.get(widget.card.oid); + final card = await CardHiveDb().get(widget.card.oid); if (card != null) { log('本地获取到card111'); @@ -79,7 +76,7 @@ class _CardViewState extends State { _card = card; }); - CardHiveDb.setCard(card).ignore(); + CardHiveDb().set(card).ignore(); } } diff --git a/lib/pages/deck_detail/components/DeckInfo.dart b/lib/pages/deck_detail/components/DeckInfo.dart index c3b1cb6..3575056 100644 --- a/lib/pages/deck_detail/components/DeckInfo.dart +++ b/lib/pages/deck_detail/components/DeckInfo.dart @@ -118,7 +118,7 @@ class _DeckInfoState extends State { final cards = []; for (var i = 0; i < mainCardIds.length; i++) { - final hiveData = await CardHiveDb.get(mainCardIds[i]); + final hiveData = await CardHiveDb().get(mainCardIds[i]); if (hiveData == null) { log('hiveData为null, ${mainCardIds[i]}'); needFetchCardIds.add(mainCardIds[i]); @@ -133,7 +133,7 @@ class _DeckInfoState extends State { if (cardsRes != null) { cards.addAll(cardsRes); cardsRes.forEach((element) { - CardHiveDb.setCard(element).ignore(); + CardHiveDb().set(element).ignore(); }); } } diff --git a/lib/pages/deck_type_detail/index.dart b/lib/pages/deck_type_detail/index.dart index 78c97f7..8569e2b 100644 --- a/lib/pages/deck_type_detail/index.dart +++ b/lib/pages/deck_type_detail/index.dart @@ -62,23 +62,13 @@ class _DeckTypeDetailPageState extends State { } Future handleTapSkill(DeckType_DeckBreakdown_Skill skill) async { - // await showModal( - // context: context, - // builder: (context) { - // return Padding(padding: EdgeInsets.symmetric(horizontal: 12), child: SkillModalView(name: skill.name)); - // }, - // // configuration: FadeScaleTransitionConfiguration(), - // ); await showDialog( context: context, - builder: (context) => SkillModalView(name: skill.name), - // builder: (context) => Dialog.fullscreen( - // backgroundColor: Colors.black.withOpacity(0.3), - // child: Container( - // padding: const EdgeInsets.symmetric(horizontal: 30), - // child: SkillModalView(name: skill.name), - // ), - // ), + builder: (context) => AlertDialog( + title: Text(skill.name), + content: SkillModalView(name: skill.name), + surfaceTintColor: Colors.transparent, + ), ); // await showGeneralDialog( @@ -104,15 +94,13 @@ class _DeckTypeDetailPageState extends State { // Future fetchDeckType({bool force = false}) async { - var deckType = await DeckTypeDetailHiveDb.getDetail(_deckTypeName); - final hiveDataExpire = await DeckTypeDetailHiveDb.getDetailExpireDate(_deckTypeName); + var deckType = await DeckTypeDetailHiveDb().get(_deckTypeName); + final hiveDataExpire = await DeckTypeDetailHiveDb().getExpireTime(_deckTypeName); Exception? err; var refreshFlag = false; if (deckType == null || force) { - log('需要请求获取数据, 本地数据为空: ${deckType == null}, 是否强制刷新: $force'); - (err, deckType) = await DeckTypeApi().getDetailByName(_deckTypeName).toCatch; if (err != null || deckType == null) { @@ -122,10 +110,9 @@ class _DeckTypeDetailPageState extends State { return false; } - await DeckTypeDetailHiveDb.setDeckType(deckType); - await DeckTypeDetailHiveDb.setDeckTypeExpireDate(deckType, DateTime.now().add(const Duration(days: 1))); + await DeckTypeDetailHiveDb().set(deckType); + await DeckTypeDetailHiveDb().setExpireTime(deckType, DateTime.now().add(const Duration(days: 1))); } else { - log('从本地获取数据 $hiveDataExpire'); refreshFlag = hiveDataExpire == null || hiveDataExpire.isBefore(DateTime.now()); } diff --git a/lib/pages/farming_and_event/components/EventListView.dart b/lib/pages/farming_and_event/components/EventListView.dart index 8d9ec63..7c51912 100644 --- a/lib/pages/farming_and_event/components/EventListView.dart +++ b/lib/pages/farming_and_event/components/EventListView.dart @@ -1,19 +1,20 @@ -import 'package:duel_links_meta/components/Loading.dart'; +import 'dart:developer'; + +import 'package:duel_links_meta/components/ListFooter.dart'; import 'package:duel_links_meta/extension/Future.dart'; +import 'package:duel_links_meta/hive/db/ArticleHiveDb.dart'; import 'package:duel_links_meta/http/ArticleApi.dart'; import 'package:duel_links_meta/pages/articles/components/ArticleItem.dart'; import 'package:duel_links_meta/pages/farming_and_event/type/TabType.dart'; +import 'package:duel_links_meta/pages/webview/index.dart'; import 'package:duel_links_meta/type/Article.dart'; import 'package:duel_links_meta/type/enum/PageStatus.dart'; import 'package:duel_links_meta/type/listViewData.dart'; import 'package:flutter/material.dart'; - -import '../../../components/ListFooter.dart'; -import '../../../util/index.dart'; -import '../../webview/index.dart'; +import 'package:flutter/scheduler.dart'; class EventListView extends StatefulWidget { - const EventListView({super.key, required this.type}); + const EventListView({required this.type, super.key}); final TabType type; @@ -22,61 +23,95 @@ class EventListView extends StatefulWidget { } class _EventListViewState extends State with AutomaticKeepAliveClientMixin { - - TabType get type => widget.type; - + TabType get _type => widget.type; final ScrollController _scrollController = ScrollController(); - final _listViewData = ListViewData
(); - List
get _articles => _listViewData.data; + final _listViewData = ListViewData
(); + var _isInit = false; + final _refreshIndicator = GlobalKey(); // - handleTapArticleItem(Article article) { - var title = article.title; - var url = 'https://www.duellinksmeta.com/articles${article.url}'; + void handleTapArticleItem(Article article) { + final title = article.title; + final url = 'https://www.duellinksmeta.com/articles${article.url}'; - Navigator.push(context, MaterialPageRoute(builder: (context) => WebviewPage(title: title, url: url))); + Navigator.push( + context, + MaterialPageRoute(builder: (context) => WebviewPage(title: title, url: url)), + ); } - // - fetchData({bool isLoadMore = false}) async { + Future loadMore() async { + List
? articles; Exception? err; - List? res; - if (type == TabType.active) { - (err, res) = await ArticleApi().activeEventLst(DateTime.now()).toCatch; + if (_type == TabType.active) { + (err, articles) = await ArticleApi().activeEventLst(DateTime.now()).toCatch; + } else if (_type == TabType.past) { + (err, articles) = await ArticleApi().pastEventLst(DateTime.now(), _listViewData.page).toCatch; + } else if (_type == TabType.general) { + (err, articles) = await ArticleApi().pinnedFarmingEvent().toCatch; } - if (type == TabType.past) { - (err, res) = await ArticleApi().pastEventLst(DateTime.now(), _listViewData.page).toCatch; + + if (err != null || articles == null) { + setState(() { + _listViewData.loadMoreStatus = PageStatus.fail; + }); + + return; } - if (type == TabType.general) { - (err, res) = await ArticleApi().pinnedFarmingEvent().toCatch; + + setState(() { + _listViewData.data.addAll(articles!); + _listViewData.loadMoreStatus = PageStatus.success; + _listViewData.page++; + _listViewData.hasMore = articles.length == _listViewData.size; + }); + } + + // + Future fetchData({bool force = false}) async { + var articles = await ArticleHiveDb().get(_type.toString()); + final expireTime = await ArticleHiveDb().getExpireTime(_type.toString()); + + Exception? err; + + if (articles != null) { + setState(() { + _listViewData + ..data = articles! + ..pageStatus = PageStatus.success; + }); } - if (err != null) { - if (isLoadMore) { - setState(() { - _listViewData.loadMoreStatus = PageStatus.fail; - }); - } else { + if (force || expireTime == null || expireTime.isBefore(DateTime.now())) { + if (_type == TabType.active) { + (err, articles) = await ArticleApi().activeEventLst(DateTime.now()).toCatch; + } else if (_type == TabType.past) { + (err, articles) = await ArticleApi().pastEventLst(DateTime.now(), _listViewData.page).toCatch; + } else if (_type == TabType.general) { + (err, articles) = await ArticleApi().pinnedFarmingEvent().toCatch; + } + if (err != null || articles == null) { setState(() { _listViewData.pageStatus = PageStatus.fail; }); + return; } - return; - } - var list = res!.map((e) => Article.fromJson(e)).toList(); + await ArticleHiveDb().set(articles, _type.toString()); + await ArticleHiveDb().setExpireTime(DateTime.now().add(const Duration(days: 1)), _type.toString()); + } _listViewData.page += 1; setState(() { - _listViewData.data.addAll(list); - _listViewData.hasMore = list.length == _listViewData.size; - _listViewData.loadMoreStatus = PageStatus.success; - _listViewData.pageStatus = PageStatus.success; + _listViewData + ..data = articles! + ..pageStatus = PageStatus.success + ..hasMore = articles.length == _listViewData.size; }); } @@ -86,25 +121,39 @@ class _EventListViewState extends State with AutomaticKeepAliveCl _scrollController.dispose(); } - initScrollReachBottomListener() { + bool isReachBottom() { + return _scrollController.position.maxScrollExtent - _scrollController.position.pixels <= 200; + } + + void initScrollReachBottomListener() { _scrollController.addListener(() { if (_listViewData.pageStatus != PageStatus.success) { - print('页面未加载完成不可以加载更多'); + return; + } + if (!_listViewData.hasMore || _listViewData.loadMoreStatus == PageStatus.loading || isReachBottom()) { return; } - if (_listViewData.hasMore && Util.isReachBottom(_scrollController)) { - if (_listViewData.loadMoreStatus == PageStatus.loading) { - print('到达底部,是加载更多中,不可执行 _loadMoreStatus ${_listViewData.loadMoreStatus}, ${PageStatus.loading}'); - return; - } - setState(() { - _listViewData.loadMoreStatus = PageStatus.loading; - }); + setState(() { + _listViewData.loadMoreStatus = PageStatus.loading; + }); - fetchData(isLoadMore: true); - } + loadMore(); + }); + } + + Future _handleRefresh() async { + _listViewData.page = 1; + await fetchData(force: _isInit); + _isInit = true; + } + + void init() { + initScrollReachBottomListener(); + + SchedulerBinding.instance.addPostFrameCallback((timeStamp) { + _refreshIndicator.currentState?.show(); }); } @@ -112,8 +161,7 @@ class _EventListViewState extends State with AutomaticKeepAliveCl void initState() { super.initState(); - initScrollReachBottomListener(); - fetchData(); + init(); } @override @@ -121,35 +169,47 @@ class _EventListViewState extends State with AutomaticKeepAliveCl @override Widget build(BuildContext context) { - return Stack( - children: [ - AnimatedOpacity( - opacity: _listViewData.pageStatus == PageStatus.success ? 1 : 0, - duration: const Duration(milliseconds: 400), - child: ListView.separated( - controller: _scrollController, - padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 8), - itemCount: _articles.length >= _listViewData.size ? _articles.length + 1 : _articles.length, - itemBuilder: (context, index) { - return _articles.length >= _listViewData.size && index == _articles.length - ? ListFooter(loadMoreStatus: _listViewData.loadMoreStatus, hasMore: _listViewData.hasMore) - : ArticleItem( - article: _articles[index], - onTap: handleTapArticleItem, - ); - }, - separatorBuilder: (BuildContext context, int index) { - return const SizedBox(height: 10); - }, - ), - ), - if (_listViewData.pageStatus == PageStatus.loading) - const Positioned.fill( - child: Center( - child: Loading(), + super.build(context); + + return RefreshIndicator( + onRefresh: _handleRefresh, + key: _refreshIndicator, + child: Stack( + fit: StackFit.expand, + children: [ + AnimatedOpacity( + opacity: _listViewData.pageStatus == PageStatus.success ? 1 : 0, + duration: const Duration(milliseconds: 300), + child: ListView.separated( + controller: _scrollController, + padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 10), + itemCount: _articles.length >= _listViewData.size ? _articles.length + 1 : _articles.length, + itemBuilder: (context, index) { + return _articles.length >= _listViewData.size && index == _articles.length + ? ListFooter( + loadMoreStatus: _listViewData.loadMoreStatus, + hasMore: _listViewData.hasMore, + ) + : ArticleItem( + article: _articles[index], + onTap: handleTapArticleItem, + ); + }, + separatorBuilder: (BuildContext context, int index) { + return const SizedBox(height: 10); + }, ), ), - ], + if (_listViewData.pageStatus == PageStatus.fail) + const SingleChildScrollView( + physics: AlwaysScrollableScrollPhysics(), + ), + if (_listViewData.pageStatus == PageStatus.fail) + const Center( + child: Text('Loading failed'), + ), + ], + ), ); } } diff --git a/lib/pages/farming_and_event/index.dart b/lib/pages/farming_and_event/index.dart index 31f6f08..6b0dd78 100644 --- a/lib/pages/farming_and_event/index.dart +++ b/lib/pages/farming_and_event/index.dart @@ -13,19 +13,20 @@ class _FarmingAndEventPageState extends State { @override Widget build(BuildContext context) { - List _tabs = [ + final tabs = [ Tab(text: TabType.active.value), Tab(text: TabType.general.value), Tab(text: TabType.past.value), ]; return DefaultTabController( - length: _tabs.length, + length: tabs.length, child: Scaffold( appBar: AppBar( title: const Text('Farming & Event'), bottom: TabBar( - tabs: _tabs, + dividerHeight: 0, + tabs: tabs, ), ), body: const TabBarView( diff --git a/lib/pages/home/components/NavItemCard.dart b/lib/pages/home/components/NavItemCard.dart index b726b76..fea62e6 100644 --- a/lib/pages/home/components/NavItemCard.dart +++ b/lib/pages/home/components/NavItemCard.dart @@ -16,6 +16,8 @@ class NavItemCard extends StatelessWidget { shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(6), ), + shadowColor: Colors.transparent, + // elevation: 0, clipBehavior: Clip.hardEdge, child: Stack( fit: StackFit.expand, @@ -54,6 +56,16 @@ class NavItemCard extends StatelessWidget { ), ), ), + // Positioned( + // right: 0, + // top: 0, + // child: Container( + // decoration: BoxDecoration( + // borderRadius: BorderRadius.circular(6), + // // color: Theme.of(context).colorScheme.onPrimary + // ), + // child: Icon(Icons.eject, color: Colors.white, size: 12,), + // )) ], ), ); diff --git a/lib/pages/home/index.dart b/lib/pages/home/index.dart index de19167..0f09d33 100644 --- a/lib/pages/home/index.dart +++ b/lib/pages/home/index.dart @@ -1,25 +1,19 @@ -import 'dart:developer'; - +import 'package:duel_links_meta/components/SettingModalView.dart'; import 'package:duel_links_meta/extension/Future.dart'; import 'package:duel_links_meta/hive/MyHive.dart'; +import 'package:duel_links_meta/hive/db/NavHiveDb.dart'; import 'package:duel_links_meta/http/NavTabApi.dart'; -import 'package:duel_links_meta/pages/about/index.dart'; import 'package:duel_links_meta/pages/farming_and_event/index.dart'; -import 'package:duel_links_meta/hive/db/NavHiveDb.dart'; import 'package:duel_links_meta/pages/home/components/NavItemCard.dart'; import 'package:duel_links_meta/pages/home/type/NavTabType.dart'; -import 'package:duel_links_meta/pages/open_source_licenses/index.dart'; import 'package:duel_links_meta/pages/tier_list/index.dart'; import 'package:duel_links_meta/pages/top_decks/index.dart'; import 'package:duel_links_meta/pages/webview/index.dart'; import 'package:duel_links_meta/store/AppStore.dart'; import 'package:duel_links_meta/type/NavTab.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; -import 'package:flutter/widgets.dart'; import 'package:get/get.dart'; -import 'package:package_info_plus/package_info_plus.dart'; class HomePage extends StatefulWidget { const HomePage({super.key}); @@ -40,12 +34,12 @@ class _HomePageState extends State with AutomaticKeepAliveClientMixin NavTab(id: NavTabType.topDecksSpeed.value, title: 'TOP DECK: SPEED'), NavTab(id: NavTabType.topDecksRush.value, title: 'TOP DECKS: RUSH'), NavTab(id: NavTabType.farmingAndEvents.value, title: 'FARMING & EVENTS'), - NavTab(id: NavTabType.leaksAndUpdates.value, title: 'LEAKS & UPDATES'), + NavTab(id: NavTabType.leaksAndUpdates.value, title: 'LEAKS & UPDATES', url: 'https://www.duellinksmeta.com/leaks-and-updates'), NavTab(id: NavTabType.genGuide.value, title: 'GEM GUIDE'), - NavTab(id: NavTabType.deckBuilder.value, title: 'DECK BUILDER'), - NavTab(id: NavTabType.tournaments.value, title: 'TOURNAMENTS'), - NavTab(id: NavTabType.duelAssist.value, title: 'DUEL ASSIST'), - NavTab(id: NavTabType.packOpener.value, title: 'PACK OPENER'), + NavTab(id: NavTabType.deckBuilder.value, title: 'DECK BUILDER', url: 'https://www.duellinksmeta.com/deck-tester/'), + NavTab(id: NavTabType.tournaments.value, title: 'TOURNAMENTS', url: 'https://www.duellinksmeta.com/tournaments'), + NavTab(id: NavTabType.duelAssist.value, title: 'DUEL ASSIST', url: 'https://www.duellinksmeta.com/duel-assist'), + NavTab(id: NavTabType.packOpener.value, title: 'PACK OPENER', url: 'https://www.duellinksmeta.com/pack-opening-simulator'), ]; // @@ -70,13 +64,11 @@ class _HomePageState extends State with AutomaticKeepAliveClientMixin return; } - if (nav.id == NavTabType.leaksAndUpdates.value) { - const url = 'https://www.duellinksmeta.com/leaks-and-updates'; - + if (nav.url != null) { Navigator.push( context, MaterialPageRoute( - builder: (context) => const WebviewPage(url: url, title: 'Leaks & Updates'), + builder: (context) => WebviewPage(url: nav.url!, title: nav.title!), ), ); return; @@ -85,22 +77,19 @@ class _HomePageState extends State with AutomaticKeepAliveClientMixin // Future toggleDarkMode() async { - // TODO if (Get.isDarkMode) { Get.changeThemeMode(ThemeMode.light); - // appStore.changeThemeMode(ThemeMode.light); MyHive.box2.put('dark_mode', 'light').ignore(); } else { Get.changeThemeMode(ThemeMode.dark); - // appStore.changeThemeMode(ThemeMode.dark); MyHive.box2.put('dark_mode', 'dark').ignore(); } } // Future fetchData({bool force = false}) async { - var navTabList = await HomeHiveDb.getNavTabList(); - final navTabListExpireTime = await HomeHiveDb.getNavTabListExpireTime(); + var navTabList = await HomeHiveDb().getNavTabList(); + final navTabListExpireTime = await HomeHiveDb().getNavTabListExpireTime(); var shouldRefresh = false; if (navTabList == null || force) { @@ -109,9 +98,8 @@ class _HomePageState extends State with AutomaticKeepAliveClientMixin if (err != null || res == null) return false; navTabList = res; - await HomeHiveDb.setNavTabList(navTabList); - // set 1 day expire - await HomeHiveDb.setNavTabListExpireTime(DateTime.now().add(const Duration(days: 1))); + await HomeHiveDb().setNavTabList(navTabList); + await HomeHiveDb().setNavTabListExpireTime(DateTime.now().add(const Duration(days: 1))); } else { if (navTabListExpireTime == null || navTabListExpireTime.isBefore(DateTime.now())) { shouldRefresh = true; @@ -145,155 +133,18 @@ class _HomePageState extends State with AutomaticKeepAliveClientMixin } void init() { - // trigger refresh indicator when page initialized SchedulerBinding.instance.addPostFrameCallback((_) async { await _refreshIndicatorKey.currentState?.show(); }); } - void _handleOpenSettingDialog() async { - PackageInfo packageInfo = await PackageInfo.fromPlatform(); - log("packageInfo ${packageInfo.toString()}"); - + Future handleOpenSettingDialog() async { await showModalBottomSheet( - context: context, - // backgroundColor: Theme.of(context).colorScheme.surface, - // backgroundColor: Colors.orange, - builder: (context) { - return ClipRRect( - borderRadius: const BorderRadius.only(topLeft: Radius.circular(24), topRight: Radius.circular(24)), - child: Container( - // height: 300, - padding: const EdgeInsets.only(top: 30), - color: Theme.of(context).colorScheme.onPrimary, - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // const SizedBox(height: 40), - const Padding( - padding: EdgeInsets.symmetric(horizontal: 12), - child: Text( - 'Setting', - style: TextStyle(fontSize: 24), - ), - ), - Container( - padding: const EdgeInsets.symmetric(horizontal: 6), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Padding( - padding: EdgeInsets.only(left: 6), - child: Text('Theme mode'), - ), - Row( - children: [ - IconButton( - isSelected: !Get.isDarkMode, - onPressed: () { - Get.changeThemeMode(ThemeMode.light); - // TODO - MyHive.box2.put('dark_mode', 'light').ignore(); - }, - icon: const Icon(Icons.sunny), - ), - IconButton( - isSelected: Get.isDarkMode, - onPressed: () { - Get.changeThemeMode(ThemeMode.dark); - MyHive.box2.put('dark_mode', 'dark').ignore(); - }, - icon: const Icon(Icons.nightlight_rounded), - ), - ], - ), - ], - ), - ), - const Padding( - padding: EdgeInsets.symmetric(vertical: 12, horizontal: 12), - child: Text( - 'About', - style: TextStyle(fontSize: 24), - ), - ), - // Material( - // color: Colors.transparent, - // child: InkWell( - // onTap: () { - // Navigator.push(context, MaterialPageRoute(builder: (context) => const AboutPage())); - // }, - // child: Container( - // height: 50, - // padding: const EdgeInsets.symmetric(horizontal: 12), - // child: const Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - // children: [ - // Text( - // 'About this project', - // overflow: TextOverflow.ellipsis, - // ), - // Icon(Icons.arrow_forward), - // ], - // ), - // ), - // ), - // ), - Container( - height: 50, - padding: const EdgeInsets.symmetric(horizontal: 12), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [const Text('App version'), Text(packageInfo.version)], - ), - ), - Container( - height: 50, - padding: const EdgeInsets.symmetric(horizontal: 12), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [const Text('Build signature'), Text(packageInfo.buildSignature.substring(0, 6))], - ), - ), - Material( - color: Colors.transparent, - child: InkWell( - onTap: () { - Navigator.push(context, MaterialPageRoute(builder: (context) => const OpenSourceLicensePage())); - }, - child: Container( - height: 50, - padding: const EdgeInsets.symmetric(horizontal: 12), - child: const Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [Text('Github'), Icon(Icons.arrow_forward)], - ), - ), - ), - ), - Material( - color: Colors.transparent, - child: InkWell( - onTap: () { - Navigator.push(context, MaterialPageRoute(builder: (context) => const OpenSourceLicensePage())); - }, - child: Container( - height: 50, - padding: const EdgeInsets.symmetric(horizontal: 12), - child: const Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [Text('Open source licenses'), Icon(Icons.arrow_forward)], - ), - ), - ), - ), - ], - ), - ), - ), - ); - }); + context: context, + builder: (context) { + return const SettingModalView(); + }, + ); } @override @@ -315,11 +166,7 @@ class _HomePageState extends State with AutomaticKeepAliveClientMixin automaticallyImplyLeading: false, title: const Text('Duel Links Meta'), actions: [ - // IconButton( - // onPressed: toggleDarkMode, - // icon: Obx(() => Icon(appStore.themeMode.value == ThemeMode.dark ? Icons.nightlight : Icons.sunny)), - // ), - IconButton(onPressed: _handleOpenSettingDialog, icon: const Icon(Icons.settings)) + IconButton(onPressed: handleOpenSettingDialog, icon: const Icon(Icons.settings_rounded)), ], ), body: RefreshIndicator( diff --git a/lib/pages/main/index.dart b/lib/pages/main/index.dart index c370222..b08a40e 100644 --- a/lib/pages/main/index.dart +++ b/lib/pages/main/index.dart @@ -42,40 +42,19 @@ class _MainPageState extends State { ), bottomNavigationBar: NavigationBar( destinations: const [ - NavigationDestination(icon: Icon(Icons.home), label: 'Home'), - NavigationDestination(icon: Icon(Icons.business), label: 'Packs'), - NavigationDestination(icon: Icon(Icons.school), label: 'Articles'), - NavigationDestination(icon: Icon(Icons.school), label: 'Ban list'), + NavigationDestination(icon: Icon(Icons.home_rounded), label: 'Home'), + NavigationDestination(icon: Icon(Icons.card_giftcard), label: 'Packs'), + NavigationDestination(icon: Icon(Icons.article), label: 'Articles'), + NavigationDestination(icon: Icon(Icons.sync_disabled), label: 'Ban list'), ], - // labelBehavior: NavigationDestinationLabelBehavior.onlyShowSelected, selectedIndex: _selectedIndex, onDestinationSelected: _onItemTapped, height: 70, - // overlayColor: Colors.white, - // indicatorColor: Colors.teal, - // surfaceTintColor: Colors.tealAccent, // 背景 + indicatorColor: Theme.of(context).colorScheme.secondaryContainer.withOpacity(0.3), + // surfaceTintColor: Colors.tealAccent, + // surfaceTintColor: Colors.transparent, // backgroundColor: Theme.of(context).colorScheme.primary, ), - // bottomNavigationBar: BottomNavigationBar( - // currentIndex: _selectedIndex, - // selectedFontSize: 12, - // // useLegacyColorScheme: false, - // // enableFeedback: true, - // items: const [ - // BottomNavigationBarItem(icon: Icon(Icons.home), backgroundColor: Colors.transparent, label: 'Home'), - // BottomNavigationBarItem(icon: Icon(Icons.business), label: 'Packs'), - // BottomNavigationBarItem(icon: Icon(Icons.school), label: 'Articles'), - // BottomNavigationBarItem(icon: Icon(Icons.school), label: 'Ban list'), - // ], - // - // type: BottomNavigationBarType.fixed, - // // backgroundColor: BaColors.main, - // // selectedIconTheme: const IconThemeData(color: Colors.white), - // // selectedItemColor: Theme.of(context).colorScheme.secondary, - // // selectedLabelStyle: const TextStyle(fontWeight: FontWeight.bold), - // // unselectedItemColor: Colors.grey, - // onTap: _onItemTapped, - // ), ); } } diff --git a/lib/pages/open_source_licenses/index.dart b/lib/pages/open_source_licenses/index.dart index 68e81ef..0ff3727 100644 --- a/lib/pages/open_source_licenses/index.dart +++ b/lib/pages/open_source_licenses/index.dart @@ -1,11 +1,8 @@ import 'dart:convert'; import 'package:animations/animations.dart'; -import 'package:duel_links_meta/pages/webview/index.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; import 'package:url_launcher/url_launcher.dart'; class OpenSourceLicense { @@ -50,7 +47,7 @@ class _OpenSourceLicensePageState extends State { launchUrl(uri).ignore(); Navigator.pop(context); }, - child: const Text('Confirm'), + child: const Text('Open'), ), ), ], @@ -111,14 +108,11 @@ class _OpenSourceLicensePageState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( - children: [ - Text( - licenses[index].name, - style: const TextStyle(fontSize: 22), - ), - ], + Text( + licenses[index].name, + style: const TextStyle(fontSize: 22), ), + const SizedBox(height: 10), Text( licenses[index].repos, style: const TextStyle(fontSize: 12), diff --git a/lib/pages/pack_detail/index.dart b/lib/pages/pack_detail/index.dart index 5c38f20..f5ca9e9 100644 --- a/lib/pages/pack_detail/index.dart +++ b/lib/pages/pack_detail/index.dart @@ -42,12 +42,12 @@ class _PackDetailPageState extends State { // Future fetchData({bool force = false}) async { - final cardsIds = await PackHiveDb.getIds(pack.oid); + final cardsIds = await PackHiveDb().getIds(pack.oid); var cards = []; if (cardsIds != null) { for (var i = 0; i < cardsIds.length; i++) { - final card = await CardHiveDb.get(cardsIds[i]); + final card = await CardHiveDb().get(cardsIds[i]); cards.add(card ?? MdCard() ..oid = cardsIds[i]); @@ -62,14 +62,14 @@ class _PackDetailPageState extends State { } cards = list; - PackHiveDb.setIds(pack.oid, list.map((e) => e.oid).toList()).ignore(); + PackHiveDb().setIds(pack.oid, list.map((e) => e.oid).toList()).ignore(); } final rarityGroup = >{}; cards.forEach((element) { if (cardsIds == null && element.rarity != '') { - CardHiveDb.setCard(element); + CardHiveDb().set(element); } if (rarityGroup[element.rarity] == null) { diff --git a/lib/pages/packs/components/PackListView.dart b/lib/pages/packs/components/PackListView.dart index c84202c..9ca8364 100644 --- a/lib/pages/packs/components/PackListView.dart +++ b/lib/pages/packs/components/PackListView.dart @@ -41,12 +41,16 @@ class _PackListViewState extends State with AutomaticKeepAliveClie SizedBox( height: 110, child: OpenContainer( + openColor: Colors.transparent, closedColor: Colors.transparent, + openElevation: 0, + closedElevation: 0, closedBuilder: (BuildContext context, void Function() action) { return Card( margin: EdgeInsets.zero, shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(6))), clipBehavior: Clip.hardEdge, + shadowColor: Colors.transparent, child: Stack( fit: StackFit.expand, children: [ diff --git a/lib/pages/packs/index.dart b/lib/pages/packs/index.dart index af7a2ca..6d622a2 100644 --- a/lib/pages/packs/index.dart +++ b/lib/pages/packs/index.dart @@ -27,16 +27,10 @@ class _PacksPageState extends State with SingleTickerProviderStateMix Future fetchData({bool force = false}) async { var reRefreshFlag = false; - var packs = await PackHiveDb.get(); - final expireTime = await PackHiveDb.getExpireTime(); + var packs = await PacksHiveDb().get(); + final expireTime = await PacksHiveDb().getExpireTime(); if (packs == null || force) { - if (packs == null) { - log('无本地数据'); - } - if (force) { - log('强制刷新'); - } final (err, res) = await PackSetApi().list().toCatch; if (err != null || res == null) { @@ -46,10 +40,9 @@ class _PacksPageState extends State with SingleTickerProviderStateMix return false; } packs = res; - PackHiveDb.set(packs).ignore(); - PackHiveDb.setExpireTime(DateTime.now().add(const Duration(days: 1))).ignore(); + PacksHiveDb().set(packs).ignore(); + PacksHiveDb().setExpireTime(DateTime.now().add(const Duration(days: 1))).ignore(); } else { - log('本地获取到数据'); reRefreshFlag = expireTime == null || expireTime.isBefore(DateTime.now()); } diff --git a/lib/pages/splash/BanStatusCardHiveDb.dart b/lib/pages/splash/BanStatusCardHiveDb.dart index 1c69346..d230152 100644 --- a/lib/pages/splash/BanStatusCardHiveDb.dart +++ b/lib/pages/splash/BanStatusCardHiveDb.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:duel_links_meta/hive/MyHive.dart'; class BanStatusCardHiveDb { @@ -9,6 +11,8 @@ class BanStatusCardHiveDb { try { cardIds = await MyHive.box2.get(_banStatusCardIdsKey) as List?; } catch (e) { + log('转换失败 $e'); + return null; } @@ -20,6 +24,7 @@ class BanStatusCardHiveDb { try { expireTime = await MyHive.box2.get(_banStatusCardIdsFetchDateKey) as DateTime?; } catch (e) { + log('转换失败 $e'); return null; } diff --git a/lib/pages/splash/index.dart b/lib/pages/splash/index.dart index c5b95cb..0f7dbd0 100644 --- a/lib/pages/splash/index.dart +++ b/lib/pages/splash/index.dart @@ -22,75 +22,14 @@ class SplashPage extends StatefulWidget { class _SplashPageState extends State { int _count = 1; - late Timer _timer; + final banCardStore = Get.put(BanCardStore()); @override void dispose() { super.dispose(); - _timer.cancel(); - } - - Future fetchBanCards({bool force = false}) async { - final banCardStore = Get.put(BanCardStore()); - final cardIds = await BanStatusCardHiveDb.getCardIds(); - final expireDate = await BanStatusCardHiveDb.getExpireTime(); - var refreshFlag = false; - - var list = []; - - if (cardIds == null || force) { - if (cardIds == null) { - log('splash 获取本地ban cards为空'); - } - - final params = { - 'limit': '0', - r'banStatus[$exists]': 'true', - r'alternateArt[$ne]': 'true', - r'rush[$ne]': 'true', - // 'fields': 'oid,banStatus' - }; - - final (err, res) = await CardApi().list(params).toCatch; - if (err != null || res == null) { - banCardStore.setPageStatus(PageStatus.fail); - return false; - } - list = res; - - // 保存ids - await BanStatusCardHiveDb.setCardIds(list.map((e) => e.oid).toList()); - await BanStatusCardHiveDb.setExpireTime(DateTime.now().add(const Duration(days: 1))); - - list.forEach(CardHiveDb.setCard); - } else { - log('本地有ban cards数据'); - - final start = DateTime.now(); - for (var i = 0; i < cardIds.length; i++) { - final card = await CardHiveDb.get(cardIds[i]); - list.add( - card ?? MdCard() - ..oid = cardIds[i], - ); - } - - log('读取本地数据消耗时间: ${DateTime.now().difference(start).inMilliseconds}'); - - // 过期 - if (expireDate == null || expireDate.isBefore(DateTime.now())) { - refreshFlag = true; - } - } - - log('获取 ban cards 成功'); - banCardStore - ..setCards(list) - ..setPageStatus(PageStatus.success); - - return refreshFlag; + _timer.cancel(); } // @@ -115,7 +54,7 @@ class _SplashPageState extends State { } Future initDarkMode() async { - final mode = await DarkModeHiveDb.get(); + final mode = await DarkModeHiveDb().get(); if (mode != ThemeMode.light) { Get.changeThemeMode(mode); @@ -127,11 +66,7 @@ class _SplashPageState extends State { startCounterDown(); - final shouldFetchBanCards = await fetchBanCards(); - - if (shouldFetchBanCards) { - await fetchBanCards(force: true); - } + banCardStore.setupLocalData().ignore(); } @override diff --git a/lib/pages/tier_list/components/TierListView.dart b/lib/pages/tier_list/components/TierListView.dart index 03d7658..4176fd2 100644 --- a/lib/pages/tier_list/components/TierListView.dart +++ b/lib/pages/tier_list/components/TierListView.dart @@ -1,29 +1,21 @@ import 'dart:developer'; -import 'package:duel_links_meta/components/Loading.dart'; -import 'package:duel_links_meta/extension/DateTime.dart'; import 'package:duel_links_meta/extension/Future.dart'; -import 'package:duel_links_meta/extension/String.dart'; -import 'package:duel_links_meta/hive/MyHive.dart'; +import 'package:duel_links_meta/hive/db/PowerRankingsHiveDb.dart'; +import 'package:duel_links_meta/hive/db/TierListHiveDb.dart'; import 'package:duel_links_meta/http/TierListApi.dart'; import 'package:duel_links_meta/pages/deck_type_detail/index.dart'; import 'package:duel_links_meta/pages/tier_list/components/TierListItemView.dart'; import 'package:duel_links_meta/pages/tier_list/type/TierListGroup.dart'; import 'package:duel_links_meta/pages/tier_list/type/TierListType.dart'; -import 'package:duel_links_meta/type/deck_type/TierList_PowerRanking.dart'; -import 'package:duel_links_meta/type/deck_type/TierList_PowerRanking_Expire.dart'; +import 'package:duel_links_meta/type/enum/PageStatus.dart'; import 'package:duel_links_meta/type/tier_list_top_tier/TierList_TopTier.dart'; -import 'package:duel_links_meta/type/tier_list_top_tier/TierList_TopTier_Expire.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; -import 'package:flutter/widgets.dart'; import 'package:get/get.dart'; -import 'package:duel_links_meta/type/enum/PageStatus.dart'; - class TierListView extends StatefulWidget { - const TierListView({super.key, required this.tierListType, this.minHeight}); + const TierListView({required this.tierListType, super.key, this.minHeight}); final TierListType tierListType; final double? minHeight; @@ -37,7 +29,7 @@ class _TierListViewState extends State with AutomaticKeepAliveClie final _refreshIndicatorKey = GlobalKey(); TierListType get _tierListType => widget.tierListType; - bool initFlag = false; + bool _isInit = false; var _pageStatus = PageStatus.loading; Future navigateToDeckTypeDetailPage(TierList_TopTier deckType) async { @@ -61,51 +53,27 @@ class _TierListViewState extends State with AutomaticKeepAliveClie // Future fetchTopTiers({bool force = false}) async { - var list = []; - - const hiveBoxKey = 'tier_list:top_tier'; - + var list = await TierListHiveDb().get(); + final expireTime = await TierListHiveDb().getExpireTime(); var reRefreshFlag = false; - final hiveValue = await MyHive.box2.get(hiveBoxKey); - log('[fetchTopTiers] box取值,value: $hiveValue, value == null ${hiveValue == null}, value type: ${hiveValue.runtimeType}'); - - if (hiveValue == null || force) { - if (hiveValue == null) { - log('[fetchTopTiers] box值为空'); - } else { - log('需要强制刷新'); - } - + if (list == null || force) { final (err, res) = await TierListApi().getTopTiers().toCatch; - if (err != null) { - setState(() { - _pageStatus = PageStatus.fail; - }); + if (err != null || res == null) { + if (list == null) { + setState(() { + _pageStatus = PageStatus.fail; + }); + } return false; } - list = res?.map(TierList_TopTier.fromJson).toList() ?? []; - await MyHive.box2.put(hiveBoxKey, TierList_TopTier_Expire(data: list, expire: DateTime.now().add(const Duration(hours: 6)))); + list = res; + await TierListHiveDb().set(list); + await TierListHiveDb().setExpireTime(DateTime.now().add(const Duration(days: 1))); } else { - try { - final value = hiveValue as TierList_TopTier_Expire; - - log('相差ms: ${DateTime.now().difference(value.expire).inSeconds}'); - - if (value.expire.isBefore(DateTime.now())) { - log('已过期, 需要渲染本地数据后再重新请求数据'); - reRefreshFlag = true; - } - - list = hiveValue.data; - log('转换成功'); - } catch (e) { - log('[fetchTopTiers] 转换失败: $e'); - await MyHive.box2.delete(hiveBoxKey); - return true; - } + reRefreshFlag = expireTime == null || expireTime.isBefore(DateTime.now()); } final tier2DeckTypesMap = >{}; @@ -138,39 +106,28 @@ class _TierListViewState extends State with AutomaticKeepAliveClie // Future fetchPowerRankings(bool rush, {bool force = false}) async { - var list = []; - final boxKey = 'tier_list:power_ranking:${rush ? 'rush' : ''}'; - var reRefreshFlag = false; + var list = await PowerRankingsHiveDb().get(isRush: rush); + final expireTime = await PowerRankingsHiveDb().getExpireTime(isRush: rush); - final hiveValue = await MyHive.box2.get(boxKey); + var reRefreshFlag = false; - if (hiveValue == null || force) { + if (list == null || force) { final (err, res) = await (rush ? TierListApi().getRushRankings() : TierListApi().getPowerRankings()).toCatch; - if (err != null) { - setState(() { - _pageStatus = PageStatus.fail; - }); + if (err != null || res == null) { + if (list == null) { + setState(() { + _pageStatus = PageStatus.fail; + }); + } return false; } - list = res?.map(TierList_PowerRanking.fromJson).toList() ?? []; - await MyHive.box2.put(boxKey, TierList_PowerRanking_Expire(data: list, expire: DateTime.now().add(const Duration(hours: 6)))); + list = res; - await Future.delayed(const Duration(milliseconds: 300)).then((value) { - '已刷新'.toast; - }); + await PowerRankingsHiveDb().set(list, isRush: rush); + await PowerRankingsHiveDb().setExpireTime(DateTime.now().add(const Duration(days: 1)), isRush: rush); } else { - try { - final _value = hiveValue as TierList_PowerRanking_Expire; - if (_value.expire.isBefore(DateTime.now())) { - reRefreshFlag = true; - } - - list = hiveValue.data; - } catch (e) { - await MyHive.box2.delete(boxKey); - return true; - } + reRefreshFlag = expireTime == null || expireTime.isBefore(DateTime.now()); } final tier0 = @@ -224,27 +181,19 @@ class _TierListViewState extends State with AutomaticKeepAliveClie final needReRefresh = await fetchTopTiers(force: force); if (needReRefresh) { await fetchTopTiers(force: true); - return; } - - return; - } - - var needReRefresh = await fetchPowerRankings(_tierListType == TierListType.rushRankings); - if (needReRefresh) { - await fetchPowerRankings(_tierListType == TierListType.rushRankings, force: true); + } else { + final needReRefresh = await fetchPowerRankings(_tierListType == TierListType.rushRankings); + if (needReRefresh) { + await fetchPowerRankings(_tierListType == TierListType.rushRankings, force: true); + } } } Future _handleRefresh() async { - print('[_handleRefresh] 开始 $initFlag'); - await fetchData(force: initFlag); - - print('[_handleRefresh] 完成'); + await fetchData(force: _isInit); - if (!initFlag) { - initFlag = true; - } + _isInit = true; } @override @@ -252,7 +201,7 @@ class _TierListViewState extends State with AutomaticKeepAliveClie super.initState(); SchedulerBinding.instance.addPostFrameCallback((_) { - _refreshIndicatorKey.currentState?.show(atTop: true); + _refreshIndicatorKey.currentState?.show(); }); } @@ -321,7 +270,7 @@ class _TierListViewState extends State with AutomaticKeepAliveClie if (_pageStatus == PageStatus.fail) const Positioned.fill( child: Center( - child: Text('加载失败'), + child: Text('Loading failed'), )) ], )); diff --git a/lib/pages/tier_list/components/TierListView2.dart b/lib/pages/tier_list/components/TierListView2.dart deleted file mode 100644 index d1c7f4f..0000000 --- a/lib/pages/tier_list/components/TierListView2.dart +++ /dev/null @@ -1,332 +0,0 @@ -import 'dart:developer'; - -import 'package:duel_links_meta/components/Loading.dart'; -import 'package:duel_links_meta/extension/DateTime.dart'; -import 'package:duel_links_meta/extension/Future.dart'; -import 'package:duel_links_meta/extension/String.dart'; -import 'package:duel_links_meta/hive/MyHive.dart'; -import 'package:duel_links_meta/http/TierListApi.dart'; -import 'package:duel_links_meta/pages/deck_type_detail/index.dart'; -import 'package:duel_links_meta/pages/tier_list/components/TierListItemView.dart'; -import 'package:duel_links_meta/pages/tier_list/type/TierListGroup.dart'; -import 'package:duel_links_meta/pages/tier_list/type/TierListType.dart'; -import 'package:duel_links_meta/type/deck_type/TierList_PowerRanking.dart'; -import 'package:duel_links_meta/type/deck_type/TierList_PowerRanking_Expire.dart'; -import 'package:duel_links_meta/type/tier_list_top_tier/TierList_TopTier.dart'; -import 'package:duel_links_meta/type/tier_list_top_tier/TierList_TopTier_Expire.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/scheduler.dart'; -import 'package:flutter/widgets.dart'; -import 'package:get/get.dart'; - -import 'package:duel_links_meta/type/enum/PageStatus.dart'; - -class TierListView extends StatefulWidget { - const TierListView({super.key, required this.tierListType, this.minHeight}); - - final TierListType tierListType; - final double? minHeight; - - @override - State createState() => _TierListViewState(); -} - -class _TierListViewState extends State with AutomaticKeepAliveClientMixin { - List _tierListGroup = []; - final _refreshIndicatorKey = GlobalKey(); - - TierListType get _tierListType => widget.tierListType; - bool initFlag = false; - var _pageStatus = PageStatus.loading; - - Future navigateToDeckTypeDetailPage(TierList_TopTier deckType) async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) => DeckTypeDetailPage( - name: deckType.name, - ), - ), - ); - } - - final tierDescMap = { - 0: '', - 1: 'Expected to be a large percentage of the top cut in a competitive tournament.*', - 2: 'Expected to be in the top cut of a competitive tournament, but not a large percentage.*', - 3: 'Expected to be played in a competitive tournament, with the possibility of being in the top cut.*', - 4: 'Decks acknowledged by the Top Player Council as having potential for being on the Tier List and which should be further explored. Without established results, it is not recommended to invest in these decks.', - }; - - // - Future fetchTopTiers({bool force = false}) async { - var list = []; - - const hiveBoxKey = 'tier_list:top_tier'; - - var reRefreshFlag = false; - - final hiveValue = await MyHive.box2.get(hiveBoxKey); - log('[fetchTopTiers] box取值,value: $hiveValue, value == null ${hiveValue == null}, value type: ${hiveValue.runtimeType}'); - - if (hiveValue == null || force) { - if (hiveValue == null) { - log('[fetchTopTiers] box值为空'); - } else { - log('需要强制刷新'); - } - - final (err, res) = await TierListApi().getTopTiers().toCatch; - - if (err != null) { - setState(() { - _pageStatus = PageStatus.fail; - }); - return false; - } - - list = res?.map(TierList_TopTier.fromJson).toList() ?? []; - await MyHive.box2.put(hiveBoxKey, TierList_TopTier_Expire(data: list, expire: DateTime.now().add(const Duration(hours: 6)))); - } else { - try { - final value = hiveValue as TierList_TopTier_Expire; - - log('相差ms: ${DateTime.now().difference(value.expire).inSeconds}'); - - if (value.expire.isBefore(DateTime.now())) { - log('已过期, 需要渲染本地数据后再重新请求数据'); - reRefreshFlag = true; - } - - list = hiveValue.data; - log('转换成功'); - } catch (e) { - log('[fetchTopTiers] 转换失败: $e'); - await MyHive.box2.delete(hiveBoxKey); - return true; - } - } - - final tier2DeckTypesMap = >{}; - - for (final item in list) { - if (tier2DeckTypesMap[item.tier] != null) { - tier2DeckTypesMap[item.tier]?.add(item); - } else { - tier2DeckTypesMap[item.tier] = [item]; - } - } - - final _list = []; - tier2DeckTypesMap.forEach((key, value) { - _list.add(TierListGroup(tier: key, deckTypes: value, desc: tierDescMap[key] ?? '')); - }); - - _list.sort((a, b) => a.tier.compareTo(b.tier)); - _list.forEach((element) { - log(element.desc); - }); - - setState(() { - _tierListGroup = _list; - _pageStatus = PageStatus.success; - }); - - return reRefreshFlag; - } - - Future fetchPowerRankings(bool rush, {bool force = false}) async { - var list = []; - final boxKey = 'tier_list:power_ranking:${rush ? 'rush' : ''}'; - var reRefreshFlag = false; - - final hiveValue = await MyHive.box2.get(boxKey); - - if (hiveValue == null || force) { - final (err, res) = await (rush ? TierListApi().getRushRankings() : TierListApi().getPowerRankings()).toCatch; - if (err != null) { - setState(() { - _pageStatus = PageStatus.fail; - }); - return false; - } - - list = res?.map(TierList_PowerRanking.fromJson).toList() ?? []; - await MyHive.box2.put(boxKey, TierList_PowerRanking_Expire(data: list, expire: DateTime.now().add(const Duration(hours: 6)))); - - await Future.delayed(const Duration(milliseconds: 300)).then((value) { - '已刷新'.toast; - }); - } else { - try { - final _value = hiveValue as TierList_PowerRanking_Expire; - if (_value.expire.isBefore(DateTime.now())) { - reRefreshFlag = true; - } - - list = hiveValue.data ; - } catch (e) { - await MyHive.box2.delete(boxKey); - return true; - } - } - - final tier0 = - TierListGroup(tier: 0, deckTypes: [], desc: 'The most successful Tournament Topping Decks, with power levels of at least 37.'); - final tier1 = - TierListGroup(tier: 1, deckTypes: [], desc: 'The most successful Tournament Topping Decks, with power levels of at least 27.'); - final tier2 = TierListGroup(tier: 2, deckTypes: [], desc: 'Decks with power levels between 16 and 27.'); - final tier3 = TierListGroup(tier: 3, deckTypes: [], desc: 'Decks with power levels between 6 and 16.'); - - for (var item in list) { - if (item.tournamentPower >= 45) { - tier0.deckTypes.add(TierList_TopTier(name: item.name, tier: 0, oid: item.oid)..power = item.tournamentPower); - continue; - } - if (item.tournamentPower >= 27) { - tier1.deckTypes.add(TierList_TopTier(name: item.name, tier: 1, oid: item.oid)..power = item.tournamentPower); - continue; - } - if (item.tournamentPower >= 16) { - tier2.deckTypes.add(TierList_TopTier(name: item.name, tier: 2, oid: item.oid)..power = item.tournamentPower); - continue; - } - - tier3.deckTypes.add(TierList_TopTier(name: item.name, tier: 3, oid: item.oid)..power = item.tournamentPower); - } - - final List group = []; - if (tier0.deckTypes.isNotEmpty) { - group.add(tier0); - } - if (tier1.deckTypes.isNotEmpty) { - group.add(tier1); - } - if (tier2.deckTypes.isNotEmpty) { - group.add(tier2); - } - if (tier3.deckTypes.isNotEmpty) { - group.add(tier3); - } - setState(() { - _tierListGroup = group; - _pageStatus = PageStatus.success; - }); - - return reRefreshFlag; - } - - Future fetchData({bool force = false}) async { - if (_tierListType == TierListType.topTires) { - final needReRefresh = await fetchTopTiers(force: force); - if (needReRefresh) { - await fetchTopTiers(force: true); - return; - } - - return; - } - - var needReRefresh = await fetchPowerRankings(_tierListType == TierListType.rushRankings); - if (needReRefresh) { - await fetchPowerRankings(_tierListType == TierListType.rushRankings, force: true); - } - } - - Future _handleRefresh() async { - print('[_handleRefresh] 开始 $initFlag'); - await fetchData(force: initFlag); - - print('[_handleRefresh] 完成'); - - if (!initFlag) { - initFlag = true; - } - } - - @override - void initState() { - super.initState(); - - SchedulerBinding.instance.addPostFrameCallback((_) { - _refreshIndicatorKey.currentState?.show(atTop: true); - }); - } - - @override - bool get wantKeepAlive => true; - - @override - Widget build(BuildContext context) { - return RefreshIndicator( - key: _refreshIndicatorKey, - onRefresh: _handleRefresh, - notificationPredicate: (_) => _pageStatus != PageStatus.loading, - child: SingleChildScrollView( - physics: const AlwaysScrollableScrollPhysics(), - child: Column( - children: [ - AnimatedOpacity( - opacity: _pageStatus == PageStatus.success ? 1 : 0, - duration: const Duration(milliseconds: 400), - child: Padding( - padding: const EdgeInsets.all(8), - child: Column( - children: _tierListGroup - .map( - (group) => Column( - children: [ - const SizedBox(height: 20), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox( - width: 102, - height: 25, - child: Image.asset( - 'assets/images/tier_${group.tier}${Get.isDarkMode ? '' : '_dark'}.png', - fit: BoxFit.contain, - ), - ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 8), - child: Text( - group.desc, - style: const TextStyle(fontSize: 12), - ), - ), - GridView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, - mainAxisSpacing: 8, - crossAxisSpacing: 8, - childAspectRatio: _tierListType != TierListType.topTires ? 3 : 4, - ), - itemCount: group.deckTypes.length, - itemBuilder: (BuildContext context, int index) { - return TierListItemView( - deckType: group.deckTypes[index], - showPower: _tierListType != TierListType.topTires, - onTap: () => navigateToDeckTypeDetailPage(group.deckTypes[index]), - ); - }, - ) - ], - ) - ], - ), - ) - .toList(), - ), - ), - ), - - ], - ), - ), - ); - } -} diff --git a/lib/pages/tier_list/index.dart b/lib/pages/tier_list/index.dart index 117f39d..fe2a6bb 100644 --- a/lib/pages/tier_list/index.dart +++ b/lib/pages/tier_list/index.dart @@ -27,9 +27,8 @@ class _TierListPageState extends State with SingleTickerProviderSt appBar: AppBar( title: const Text('Tier List') , bottom: TabBar( - // indicatorColor: Colors.transparent, controller: _tabController, - // dividerHeight: 0, + dividerHeight: 0, tabs: const [ Tab(text: 'TOP TIERS'), Tab(text: 'POWER RANKINGS'), diff --git a/lib/pages/top_decks/components/TopDeckListView.dart b/lib/pages/top_decks/components/TopDeckListView.dart index d3d712e..68e0e55 100644 --- a/lib/pages/top_decks/components/TopDeckListView.dart +++ b/lib/pages/top_decks/components/TopDeckListView.dart @@ -2,7 +2,9 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:duel_links_meta/extension/DateTime.dart'; import 'package:duel_links_meta/gen/assets.gen.dart'; import 'package:duel_links_meta/type/top_deck/TopDeck.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; class TopDeckListView extends StatefulWidget { const TopDeckListView({required this.topDecks, super.key, this.onTap}); @@ -20,120 +22,124 @@ class _TopDeckListViewState extends State { @override Widget build(BuildContext context) { - return Column( - children: [ - Container( - color: Colors.black12, - padding: const EdgeInsets.symmetric(vertical: 5), - child: Row( - children: [ - Expanded( - child: Container( - padding: const EdgeInsets.only(left: 42), - child: const Text('Skill', style: TextStyle(fontSize: 13)), + return ClipRRect( + borderRadius: const BorderRadius.only(topRight: Radius.circular(18), topLeft: Radius.circular(18)), + child: Column( + children: [ + Container( + color: Theme.of(context).colorScheme.onPrimary, + padding: const EdgeInsets.symmetric(vertical: 10), + child: Row( + children: [ + Expanded( + child: Container( + padding: const EdgeInsets.only(left: 42), + child: const Text('Skill', style: TextStyle(fontSize: 13)), + ), ), - ), - Container( - width: 40, - padding: const EdgeInsets.symmetric(horizontal: 4), - child: const Text('Top',style: TextStyle(fontSize: 13),), - ), - Container( - width: 90, - padding: const EdgeInsets.symmetric(horizontal: 4), - child: const Text('Price', style: TextStyle(fontSize: 13),), - ), - Container( - width: 80, - padding: const EdgeInsets.symmetric(horizontal: 4), - child: const Text('Date', style: TextStyle(fontSize: 13)), - ) - ], + Container( + width: 45, + // padding: const EdgeInsets.symmetric(horizontal: 6), + child: const Text('Top',style: TextStyle(fontSize: 13),), + ), + Container( + width: 90, + // padding: const EdgeInsets.symmetric(horizontal: 6), + child: const Text('Price', style: TextStyle(fontSize: 13),), + ), + Container( + width: 80, + padding: const EdgeInsets.symmetric(horizontal: 6), + child: const Text('Date', style: TextStyle(fontSize: 13)), + ) + ], + ), ), - ), - Expanded( - child: Container( - color: Theme.of(context).colorScheme.surfaceVariant, - child: ListView.builder( - itemCount: _topDecks.length, - itemBuilder: (context, index) { - return InkWell( - onTap: ()=> widget.onTap?.call(_topDecks[index]), - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 4), - child: Row( - children: [ - ClipRRect( - borderRadius: BorderRadius.circular(3), - child: SizedBox( - height: 32, - width: 32, - child: CachedNetworkImage( - width: 32, - fit: BoxFit.cover, - imageUrl: - 'https://imgserv.duellinksmeta.com/v2/dlm/deck-type/${Uri.encodeComponent(_topDecks[index].deckType.name)}?portrait=true&width=50', + + Expanded( + child: Container( + color: Theme.of(context).colorScheme.onPrimary, + child: ListView.builder( + itemCount: _topDecks.length, + itemBuilder: (context, index) { + return Material( + child: InkWell( + onTap: ()=> widget.onTap?.call(_topDecks[index]), + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + child: Row( + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(3), + child: CachedNetworkImage( + width: 38, + height: 38, + fit: BoxFit.cover, + imageUrl: + 'https://imgserv.duellinksmeta.com/v2/dlm/deck-type/${Uri.encodeComponent(_topDecks[index].deckType.name)}?portrait=true&width=50', + ), ), - ), - ), - Expanded( - child: Container( - padding: const EdgeInsets.only(left: 4), - child: Text( - _topDecks[index].skill?.name ?? '', - style: const TextStyle(fontSize: 12), - overflow: TextOverflow.ellipsis, - maxLines: 2, + Expanded( + child: Container( + padding: const EdgeInsets.only(left: 4), + child: Text( + _topDecks[index].skill?.name ?? '', + style: const TextStyle(fontSize: 12), + overflow: TextOverflow.ellipsis, + maxLines: 2, + ), + ), ), - ), - ), - SizedBox( - width: 45, - child: Center( - child: CachedNetworkImage( - width: 32, - fit: BoxFit.cover, - imageUrl: - 'https://wsrv.nl/?url=https://s3.duellinksmeta.com${_topDecks[index].tournamentType?.icon ?? _topDecks[index].rankedType?.icon}&w=100&output=webp&we&n=-1&maxage=7d', + SizedBox( + width: 45, + child: Center( + child: CachedNetworkImage( + width: 32, + fit: BoxFit.cover, + imageUrl: + 'https://wsrv.nl/?url=https://s3.duellinksmeta.com${_topDecks[index].tournamentType?.icon ?? _topDecks[index].rankedType?.icon}&w=100&output=webp&we&n=-1&maxage=7d', + ), + ), ), - ), - ), - SizedBox( - width: 90, - child: Row( - children: [ - Assets.images.iconGem.image(width: 16), - SizedBox(width: 2,), - Text( - '${(_topDecks[index].gemsPrice / 1000).toStringAsFixed(0)}k', + Container( + width: 90, + padding: const EdgeInsets.only(left: 6), + child: Row( + children: [ + Assets.images.iconGem.image(width: 16), + SizedBox(width: 2,), + Text( + '${(_topDecks[index].gemsPrice / 1000).toStringAsFixed(0)}k', + style: const TextStyle(fontSize: 12), + ), + if (_topDecks[index].dollarsPrice > 0) + Text( + '+ \$${_topDecks[index].dollarsPrice}', + style: const TextStyle(fontSize: 12), + ), + ], + ), + ), + Container( + padding: const EdgeInsets.only(right: 2), + width: 80, + child: Text( + _topDecks[index].created?.toLocal().format ?? '', style: const TextStyle(fontSize: 12), + textAlign: TextAlign.right, ), - if (_topDecks[index].dollarsPrice > 0) - Text( - '+ \$${_topDecks[index].dollarsPrice}', - style: const TextStyle(fontSize: 12), - ), - ], - ), + ) + ], ), - Container( - padding: const EdgeInsets.only(right: 2), - width: 80, - child: Text( - _topDecks[index].created?.toLocal().format ?? '', - style: const TextStyle(fontSize: 12), - textAlign: TextAlign.right, - ), - ) - ], + ), ), - ), - ); - }, + ); + }, + ), ), ), - ), - ], + ], + ), ); } } diff --git a/lib/pages/top_decks/index.dart b/lib/pages/top_decks/index.dart index 170788e..5af3f58 100644 --- a/lib/pages/top_decks/index.dart +++ b/lib/pages/top_decks/index.dart @@ -1,8 +1,6 @@ -import 'dart:developer'; - import 'package:duel_links_meta/components/TopDeckItem.dart'; import 'package:duel_links_meta/extension/Future.dart'; -import 'package:duel_links_meta/hive/MyHive.dart'; +import 'package:duel_links_meta/hive/db/TopDeckHiveDb.dart'; import 'package:duel_links_meta/http/TopDeckApi.dart'; import 'package:duel_links_meta/pages/deck_detail/index.dart'; import 'package:duel_links_meta/pages/top_decks/components/TopDeckListView.dart'; @@ -47,14 +45,14 @@ class _TopDecksPageState extends State { // void _handleTapTopDeckItem(Group group) { showModalBottomSheet( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical(top: Radius.circular(2)), - ), context: context, builder: (context) => TopDeckListView( topDecks: group.data, onTap: (topDeck) => { - Navigator.push(context, MaterialPageRoute(builder: (context) => DeckDetailPage(topDeck: topDeck))), + Navigator.push( + context, + MaterialPageRoute(builder: (context) => DeckDetailPage(topDeck: topDeck)), + ), }, ), ); @@ -62,16 +60,11 @@ class _TopDecksPageState extends State { // Future fetchData({bool force = false}) async { - final hiveDataKey = 'top_deck:list:${_isRush ? 'rush' : 'speed'}'; - final hiveRefreshKey = 'top_deck:list:${_isRush ? 'rush' : 'speed'}:refresh'; - - final hiveData = await MyHive.box2.get(hiveDataKey) as List?; - final hiveRefreshDate = await MyHive.box2.get(hiveRefreshKey) as DateTime?; + var topDecks = await TopDeckHiveDb().get(isRush: _isRush); + final expireTime = await TopDeckHiveDb().getExpireTime(isRush: _isRush); var refreshFlag = false; - var topDecks = []; - - if (hiveData == null || force) { + if (topDecks == null || force) { final params = { r'created[$gte]': '(days-28)', 'fields': 'rankedType,deckType,created,tournamentType,gemsPrice,dollarsPrice,url,skill', @@ -82,29 +75,21 @@ class _TopDecksPageState extends State { }; final (err, res) = await TopDeckApi().list(params).toCatch; - if (err != null) { - setState(() { - _pageStatus = PageStatus.fail; - }); + if (err != null || res == null) { + if (topDecks == null) { + setState(() { + _pageStatus = PageStatus.fail; + }); + } return false; } - topDecks = res!; - await MyHive.box2.put(hiveDataKey, topDecks); - await MyHive.box2.put(hiveRefreshKey, DateTime.now()); + topDecks = res; + await TopDeckHiveDb().set(topDecks, isRush: _isRush); + await TopDeckHiveDb().setExpireTime(DateTime.now().add(const Duration(days: 1)), isRush: _isRush); } else { - try { - topDecks = hiveData.map((e) => e as TopDeck).toList(); - if (hiveRefreshDate != null && hiveRefreshDate.add(const Duration(hours: 12)).isBefore(DateTime.now())) { - refreshFlag = true; - } - } catch (e) { - await MyHive.box2.delete(hiveDataKey); - await MyHive.box2.delete(hiveRefreshKey); - - return true; - } + refreshFlag = expireTime == null || expireTime.isBefore(DateTime.now()); } final countMap = >{}; @@ -152,14 +137,11 @@ class _TopDecksPageState extends State { } }); tournamentTypeMap.forEach((key, value) { - log('key: $key, count: ${value.length}'); - tournamentTypeGroups.add(Group(key: key, data: value)); }); tournamentTypeGroups.sort((a, b) => a.key.compareTo(b.key)); setState(() { - // _topDecks = topDecks; _pageStatus = PageStatus.success; _topDeckGroups = groups; _tournamentTypeTopDeckGroups = tournamentTypeGroups; diff --git a/lib/store/AppStore.dart b/lib/store/AppStore.dart index d3ed893..2dc26a4 100644 --- a/lib/store/AppStore.dart +++ b/lib/store/AppStore.dart @@ -1,10 +1,24 @@ -import 'package:flutter/material.dart'; +import 'dart:developer'; + import 'package:get/get.dart'; +import 'package:package_info_plus/package_info_plus.dart'; + +class AppStore extends GetxController{ + + PackageInfo? packageInfo; + + @override + void onClose() { + super.onClose(); + log('AppStore on close'); + } + + @override + Future onInit() async{ + super.onInit(); + log('AppStore on init'); -class AppStore extends GetxController{ - // Rx themeMode = ThemeMode.light.obs; - // - // void changeThemeMode(ThemeMode mode) { - // themeMode.value = mode; - // } + final info = await PackageInfo.fromPlatform(); + packageInfo = info; + } } \ No newline at end of file diff --git a/lib/store/BanCardStore.dart b/lib/store/BanCardStore.dart index 72ff9d6..48ad449 100644 --- a/lib/store/BanCardStore.dart +++ b/lib/store/BanCardStore.dart @@ -1,9 +1,14 @@ +import 'dart:developer'; + +import 'package:duel_links_meta/extension/Future.dart'; +import 'package:duel_links_meta/hive/db/CardHiveDb.dart'; +import 'package:duel_links_meta/http/CardApi.dart'; +import 'package:duel_links_meta/pages/splash/BanStatusCardHiveDb.dart'; import 'package:duel_links_meta/type/MdCard.dart'; import 'package:duel_links_meta/type/enum/PageStatus.dart'; import 'package:get/get.dart'; class BanCardStore extends GetxController { - RxList cards = [].obs; Rx pageStatus = PageStatus.loading.obs; @@ -18,23 +23,81 @@ class BanCardStore extends GetxController { }); void setCards(List data) { - final _group = >{ + final banStatus2Cards = >{ 'Forbidden': [], 'Limited 1': [], 'Limited 2': [], 'Limited 3': [], }; data.forEach((item) { - _group[item.banStatus]?.add(item); + banStatus2Cards[item.banStatus]?.add(item); idToCardMap[item.oid] = item; }); - group.value = _group; + group.value = banStatus2Cards; cards.value = data; } void setPageStatus(PageStatus value) { pageStatus.value = value; } -} \ No newline at end of file + + Future setupLocalData() async { + final cardIds = await BanStatusCardHiveDb.getCardIds(); + + if (cardIds == null) { + fetchData().ignore(); + return; + } + + final expireDate = await BanStatusCardHiveDb.getExpireTime(); + + final list = []; + + final start = DateTime.now(); + for (var i = 0; i < cardIds.length; i++) { + final card = await CardHiveDb().get(cardIds[i]); + + list.add( + card ?? MdCard() + ..oid = cardIds[i], + ); + } + log('读取本地数据消耗时间: ${DateTime.now().difference(start).inMilliseconds}'); + setCards(list); + + setPageStatus(PageStatus.success); + + if (expireDate == null || expireDate.isBefore(DateTime.now())) { + fetchData().ignore(); + } + } + + Future fetchData({bool force = false}) async { + final params = { + 'limit': '0', + r'banStatus[$exists]': 'true', + r'alternateArt[$ne]': 'true', + r'rush[$ne]': 'true', + // 'fields': 'oid,banStatus' + }; + + final (err, list) = await CardApi().list(params).toCatch; + + if (err != null || list == null) { + if (cards.isEmpty) { + setPageStatus(PageStatus.fail); + } + return; + } + + await BanStatusCardHiveDb.setCardIds(list.map((e) => e.oid).toList()); + await BanStatusCardHiveDb.setExpireTime(DateTime.now().add(const Duration(days: 1))); + + list.forEach(CardHiveDb().set); + + setPageStatus(PageStatus.success); + setCards(list); + } +} diff --git a/lib/type/Article.dart b/lib/type/Article.dart index a27c0a1..493c11e 100644 --- a/lib/type/Article.dart +++ b/lib/type/Article.dart @@ -1,34 +1,46 @@ +import 'package:duel_links_meta/hive/MyHive.dart'; +import 'package:hive/hive.dart'; import 'package:json_annotation/json_annotation.dart'; part 'Article.g.dart'; @JsonSerializable(includeIfNull: false) +@HiveType(typeId: MyHive.article) class Article { + + Article(); + + factory Article.fromJson(dynamic json) => _$ArticleFromJson(json as Map); + + @HiveField(0) @JsonKey(name: '_id') String id = ''; + @HiveField(1) String url = ''; + @HiveField(2) String title = ''; + @HiveField(3) @JsonKey(defaultValue: '') String category = ''; + @HiveField(4) String image = ''; + @HiveField(5) String? subCategory = ''; + @HiveField(6) @JsonKey(defaultValue: false) bool featured = false; + @HiveField(7) String description = ''; + @HiveField(8) DateTime? date; - Article(); - - // factory Article.fromJson(Map json) => _$ArticleFromJson(json); - factory Article.fromJson(dynamic json) => _$ArticleFromJson(json as Map); - dynamic toJson() => _$ArticleToJson(this); } diff --git a/lib/type/Article.g.dart b/lib/type/Article.g.dart index d5e4c64..4b296b1 100644 --- a/lib/type/Article.g.dart +++ b/lib/type/Article.g.dart @@ -2,6 +2,67 @@ part of 'Article.dart'; +// ************************************************************************** +// TypeAdapterGenerator +// ************************************************************************** + +class ArticleAdapter extends TypeAdapter
{ + @override + final int typeId = 26; + + @override + Article read(BinaryReader reader) { + final numOfFields = reader.readByte(); + final fields = { + for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), + }; + return Article() + ..id = fields[0] as String + ..url = fields[1] as String + ..title = fields[2] as String + ..category = fields[3] as String + ..image = fields[4] as String + ..subCategory = fields[5] as String? + ..featured = fields[6] as bool + ..description = fields[7] as String + ..date = fields[8] as DateTime?; + } + + @override + void write(BinaryWriter writer, Article obj) { + writer + ..writeByte(9) + ..writeByte(0) + ..write(obj.id) + ..writeByte(1) + ..write(obj.url) + ..writeByte(2) + ..write(obj.title) + ..writeByte(3) + ..write(obj.category) + ..writeByte(4) + ..write(obj.image) + ..writeByte(5) + ..write(obj.subCategory) + ..writeByte(6) + ..write(obj.featured) + ..writeByte(7) + ..write(obj.description) + ..writeByte(8) + ..write(obj.date); + } + + @override + int get hashCode => typeId.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is ArticleAdapter && + runtimeType == other.runtimeType && + typeId == other.typeId; +} + // ************************************************************************** // JsonSerializableGenerator // ************************************************************************** diff --git a/lib/type/NavTab.dart b/lib/type/NavTab.dart index cd0a099..de45b0b 100644 --- a/lib/type/NavTab.dart +++ b/lib/type/NavTab.dart @@ -8,6 +8,10 @@ part 'NavTab.g.dart'; @HiveType(typeId: MyHive.nav_tab) class NavTab { + NavTab({required this.id, this.title = '', this.url}); + + factory NavTab.fromJson(dynamic json) => _$NavTabFromJson(json as Map); + @JsonKey(name: '_id') String oid = ''; @@ -20,10 +24,7 @@ class NavTab { // @JsonKey(includeFromJson: false, includeToJson: true) String? title; - NavTab({required this.id, this.title = ''}); - - factory NavTab.fromJson(dynamic json) => _$NavTabFromJson(json as Map); + String? url; Map toJson() => _$NavTabToJson(this); - } diff --git a/lib/type/listViewData.dart b/lib/type/listViewData.dart index 4e13dcf..43dba5a 100644 --- a/lib/type/listViewData.dart +++ b/lib/type/listViewData.dart @@ -1,12 +1,12 @@ -import 'enum/PageStatus.dart'; +import 'package:duel_links_meta/type/enum/PageStatus.dart'; class ListViewData { - var pageStatus = PageStatus.loading; - var hasMore = true; - var page = 1; - var size = 10; - var loadMoreStatus = PageStatus.success; - List data = []; ListViewData({this.size = 10}); + PageStatus pageStatus = PageStatus.loading; + bool hasMore = true; + int page = 1; + int size = 10; + PageStatus loadMoreStatus = PageStatus.success; + List data = []; } diff --git a/pubspec.lock b/pubspec.lock index 4623f0a..5cf035a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -957,6 +957,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "5.1.0" + visibility_detector: + dependency: "direct main" + description: + name: visibility_detector + sha256: dd5cc11e13494f432d15939c3aa8ae76844c42b723398643ce9addb88a5ed420 + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.4.0+2" vm_service: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 6e04713..90c7ad0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -48,6 +48,7 @@ dependencies: animations: ^2.0.11 url_launcher: 6.3.0 package_info_plus: 8.0.2 + visibility_detector: 0.4.0+2 dev_dependencies: flutter_test: