diff --git a/lib/api/response/page_info.dart b/lib/api/response/page_info.dart index a717fbd..d461027 100644 --- a/lib/api/response/page_info.dart +++ b/lib/api/response/page_info.dart @@ -49,9 +49,11 @@ class PageInfo { int? watchers; int? visitingwatchers; - MWPageProtectionInfo? protection; + List? protection; List? restrictiontypes; + Map? pageviews; + PageInfo({ required this.pageid, required this.ns, @@ -74,6 +76,7 @@ class PageInfo { this.visitingwatchers, this.protection, this.restrictiontypes, + this.pageviews, }); String get mainTitle { @@ -84,8 +87,7 @@ class PageInfo { return null; } - factory PageInfo.fromJson(Map json) => - _$PageInfoFromJson(json); + factory PageInfo.fromJson(Map json) => _$PageInfoFromJson(json); Map toJson() => _$PageInfoToJson(this); @@ -110,15 +112,12 @@ class PageInfo { class PagesResponse with _$PagesResponse { factory PagesResponse({required List pages}) = _PageResponse; - factory PagesResponse.fromJson(Map json) => - _$PagesResponseFromJson(json); + factory PagesResponse.fromJson(Map json) => _$PagesResponseFromJson(json); } @Freezed(copyWith: false) class PageImageInfo with _$PageImageInfo { - factory PageImageInfo({required String source, int? width, int? height}) = - _PageImageInfo; + factory PageImageInfo({required String source, int? width, int? height}) = _PageImageInfo; - factory PageImageInfo.fromJson(Map json) => - _$PageImageInfoFromJson(json); + factory PageImageInfo.fromJson(Map json) => _$PageImageInfoFromJson(json); } diff --git a/lib/app.dart b/lib/app.dart index 04b93b1..a31812d 100755 --- a/lib/app.dart +++ b/lib/app.dart @@ -27,10 +27,10 @@ class _IsekaiWikiAppWrapperState extends State { if (GetPlatform.isAndroid) { // 仅允许竖屏 - SystemChrome.setPreferredOrientations([ + /*SystemChrome.setPreferredOrientations([ DeviceOrientation.portraitUp, DeviceOrientation.portraitDown, - ]); + ]);*/ if (GetPlatform.isAndroid) { SystemChrome.setSystemUIOverlayStyle( @@ -97,9 +97,7 @@ class IsekaiWikiApp extends StatelessWidget { ? Styles.materialLightTheme : Styles.materialDarkTheme, child: CupertinoTheme( - data: - Styles.cupertinoTheme.copyWith(brightness: brightness), - child: child), + data: Styles.cupertinoTheme.copyWith(brightness: brightness), child: child), ), ); } diff --git a/lib/components/auto_wrap.dart b/lib/components/auto_wrap.dart index 8896447..aa8eef8 100644 --- a/lib/components/auto_wrap.dart +++ b/lib/components/auto_wrap.dart @@ -1,42 +1,72 @@ import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; -class AutoWrap extends StatefulWidget { +class ResponsivePair extends StatefulWidget { final List children; + final MainAxisAlignment rowMainAxisAlignment; + final CrossAxisAlignment rowCrossAxisAlignment; - const AutoWrap({super.key, required this.children}); + final MainAxisAlignment colMainAxisAlignment; + final CrossAxisAlignment colCrossAxisAlignment; + + const ResponsivePair({ + super.key, + required this.children, + this.rowMainAxisAlignment = MainAxisAlignment.spaceBetween, + this.rowCrossAxisAlignment = CrossAxisAlignment.center, + this.colMainAxisAlignment = MainAxisAlignment.spaceBetween, + this.colCrossAxisAlignment = CrossAxisAlignment.start, + }); @override - State createState() => _AutoWrapState(); + State createState() => _ResponsivePairState(); } -class _AutoWrapState extends State { +class _ResponsivePairState extends State { var _computed = false; - var _isFirstBuild = true; final Map _childWidth = {}; var _isRow = true; - void handleSizeChange(Size size, int id) { - _childWidth[id] = size.width; + @override + void initState() { + super.initState(); + + WidgetsBinding.instance.addPostFrameCallback((_) { + refreshSize(); + }); } - @override - void didUpdateWidget(covariant AutoWrap oldWidget) { - super.didUpdateWidget(oldWidget); - if (_isFirstBuild) { - _isFirstBuild = false; - var sum = _childWidth.values.reduce((value, element) => value + element); - var width = context.size?.width ?? 0; + void refreshSize() { + if (_childWidth.length != widget.children.length) return; - setState(() { - _computed = true; - _isRow = sum < width; - }); + var sum = _childWidth.values.reduce((value, element) => value + element); + var width = context.size?.width ?? 0; + + setState(() { + _computed = true; + _isRow = sum < width; + }); + } + + void handleSizeChange(Size size, int id) { + _childWidth[id] = size.width; + if (_childWidth.length == widget.children.length) { + refreshSize(); } } + @override + void didChangeDependencies() { + super.didChangeDependencies(); + + setState(() { + _computed = false; + }); + } + @override Widget build(BuildContext context) { + Widget inner; if (!_computed) { List stackChildren = []; for (var i = 0; i < widget.children.length; i++) { @@ -52,16 +82,29 @@ class _AutoWrapState extends State { ), ); } - return Stack( + inner = Stack( children: stackChildren, ); } else { if (_isRow) { - return Row(children: widget.children); + inner = Row( + mainAxisAlignment: widget.rowMainAxisAlignment, + crossAxisAlignment: widget.rowCrossAxisAlignment, + children: widget.children, + ); } else { - return Column(children: widget.children); + inner = Column( + mainAxisAlignment: widget.colMainAxisAlignment, + crossAxisAlignment: widget.colCrossAxisAlignment, + children: widget.children, + ); } } + return AnimatedSize( + duration: const Duration(milliseconds: 150), + curve: Curves.linear, + child: inner, + ); } } @@ -87,8 +130,8 @@ class WidgetSizeRenderObject extends RenderProxyBox { onSizeChange(newSize, id); }); } - } catch (e) { - print(e); + } catch (e, stack) { + debugPrint("$e $stack"); } } } diff --git a/lib/components/gesture_detector.dart b/lib/components/gesture_detector.dart index c9dfa63..e164c34 100755 --- a/lib/components/gesture_detector.dart +++ b/lib/components/gesture_detector.dart @@ -33,10 +33,13 @@ class _OpacityGestureDetectorState extends State { enum PointerActiveMode { none, hover, active } class ClickableBuilder extends StatefulWidget { + final int minActiveDuration; + final Widget Function(BuildContext context, PointerActiveMode mode, Widget child) builder; final Widget? child; - const ClickableBuilder({super.key, required this.builder, this.child}); + const ClickableBuilder( + {super.key, required this.builder, this.child, this.minActiveDuration = 150}); @override State createState() => _ClickableBuilder(); @@ -67,7 +70,7 @@ class _ClickableBuilder extends State { setState(() { isActive = true; }); - Future.delayed(const Duration(milliseconds: 150)).then((value) { + Future.delayed(Duration(milliseconds: widget.minActiveDuration)).then((value) { if (isPointerDown) { isPersistActive = true; } else { diff --git a/lib/components/wikipage_parser.dart b/lib/components/wikipage_parser.dart index 438b0ac..9d02a44 100644 --- a/lib/components/wikipage_parser.dart +++ b/lib/components/wikipage_parser.dart @@ -271,6 +271,13 @@ class _WikiParserState extends ReactiveState { c = Get.put(c); } + @override + void dispose() { + super.dispose(); + + c.loading.value = true; + } + @override void receiveProps() { c.pageInfo.value = widget.pageInfo; diff --git a/lib/pages/wiki/info.dart b/lib/pages/wiki/info.dart index aef264d..2a62511 100644 --- a/lib/pages/wiki/info.dart +++ b/lib/pages/wiki/info.dart @@ -38,17 +38,14 @@ class WikiInfoPageController extends GetxController { pageInfoRes = await MWApiList.getPageInfoList(pageids: [ pageId.value ], prop: [ - "extracts", "info", "pageimages", "pageviews" ], extraParams: { - "inprop": - "url|protection|watched|watchers|visitingwatchers|displaytitle", + "inprop": "url|protection|watched|watchers|visitingwatchers|displaytitle", }); } else { - pageInfoRes = - await MWApiList.getPageInfoList(titles: [pageTitle.value]); + pageInfoRes = await MWApiList.getPageInfoList(titles: [pageTitle.value]); } if (pageInfoRes.data.isEmpty) { throw MWApiErrorException(code: 'no-page', info: "页面信息丢失"); @@ -58,8 +55,7 @@ class WikiInfoPageController extends GetxController { pageId.value = pageInfo.value!.pageid; pageTitle.value = pageInfo.value!.title; } catch (err, stack) { - alert(Get.overlayContext!, ErrorUtils.getErrorMessage(err), - title: "错误"); + alert(Get.overlayContext!, ErrorUtils.getErrorMessage(err), title: "错误"); if (kDebugMode) { print("Exception in page: $err"); stack.printError(); @@ -90,13 +86,14 @@ class _WikiInfoPageState extends ReactiveState { void initState() { super.initState(); - c = Get.put(c); + Get.put(c); } @override void dispose() { - c.dispose(); super.dispose(); + + Get.delete(); } @override @@ -112,16 +109,14 @@ class _WikiInfoPageState extends ReactiveState { children: [ CupertinoListTile.notched( title: Center( - child: CupertinoActivityIndicator( - radius: 10 * MediaQuery.of(context).textScaleFactor), + child: CupertinoActivityIndicator(radius: 10 * MediaQuery.of(context).textScaleFactor), ), ), ], ); } - CupertinoListTile buildPageInfo( - BuildContext context, String label, dynamic value, + CupertinoListTile buildPageInfo(BuildContext context, String label, dynamic value, {VoidFutureCallback? onTap}) { final Locale appLocale = Localizations.localeOf(context); var valueStr = "未知"; @@ -129,8 +124,7 @@ class _WikiInfoPageState extends ReactiveState { valueStr = value; } if (value is int) { - valueStr = - NumberFormat.decimalPattern(appLocale.toLanguageTag()).format(value); + valueStr = NumberFormat.decimalPattern(appLocale.toLanguageTag()).format(value); } else if (value is double) { var pattern = NumberFormat.decimalPattern(appLocale.toLanguageTag()); pattern.maximumFractionDigits = 2; @@ -146,48 +140,62 @@ class _WikiInfoPageState extends ReactiveState { padding: const EdgeInsetsDirectional.fromSTEB(20.0, 10.0, 14.0, 10.0), title: Container( width: MediaQuery.of(context).size.width, - child: AutoWrap( + child: ResponsivePair( children: [ Text(label), Text( valueStr, - style: TextStyle( - color: CupertinoColors.systemGrey2.resolveFrom(context)), + style: TextStyle(color: CupertinoColors.systemGrey2.resolveFrom(context)), ), ], ), ), - trailing: onTap != null ? const CupertinoListTileChevron() : null, + trailing: onTap != null + ? const Padding( + padding: EdgeInsets.only(left: 8), + child: CupertinoListTileChevron(), + ) + : null, onTap: onTap, ); } + int? getPageViews(Map? pageViews) { + if (pageViews == null) return null; + + int validData = 0; + int visitSum = 0; + pageViews.forEach((key, value) { + if (value != null) { + validData++; + visitSum += value; + } + }); + + return validData != 0 ? visitSum : null; + } + Widget buildPageInfoList(BuildContext context) { var pageInfo = c.pageInfo.value; return Column( children: [ - Obx( - () => CupertinoListSection.insetGrouped( - additionalDividerMargin: 6, - header: const Text("基本信息"), - backgroundColor: CupertinoTheme.of(context).scaffoldBackgroundColor, - children: [ - buildPageInfo( - context, - "显示标题", - pageInfo?.mainTitle, - onTap: () async {}, - ), - buildPageInfo(context, "页面长度(字节)", pageInfo?.length), - buildPageInfo(context, "页面ID", pageInfo?.pageid), - buildPageInfo(context, "页面内容语言", pageInfo?.pagelanguage), - buildPageInfo(context, "页面内容类型", pageInfo?.contentmodel), - buildPageInfo(context, "收藏者数", pageInfo?.watchers), - buildPageInfo(context, "最后修改于", pageInfo?.updatedTime), - buildPageInfo(context, "该页面的子页面数", null), - buildPageInfo(context, "过去30天的页面访问量", null), - ], - ), + CupertinoListSection.insetGrouped( + additionalDividerMargin: 6, + header: const Text("基本信息"), + backgroundColor: CupertinoTheme.of(context).scaffoldBackgroundColor, + children: [ + buildPageInfo(context, "页面标题", pageInfo?.title), + buildPageInfo(context, "显示标题", pageInfo?.mainTitle), + buildPageInfo(context, "页面长度(字节)", pageInfo?.length), + buildPageInfo(context, "页面ID", pageInfo?.pageid), + buildPageInfo(context, "页面内容语言", pageInfo?.pagelanguage), + buildPageInfo(context, "页面内容类型", pageInfo?.contentmodel), + buildPageInfo(context, "收藏者数", pageInfo?.watchers), + buildPageInfo(context, "更新后已查看的收藏者", pageInfo?.visitingwatchers), + buildPageInfo(context, "最后修改于", pageInfo?.updatedTime), + buildPageInfo(context, "该页面的子页面数", null), + buildPageInfo(context, "过去30天的页面访问量", getPageViews(pageInfo?.pageviews)), + ], ), ], ); @@ -202,9 +210,7 @@ class _WikiInfoPageState extends ReactiveState { child: SafeArea( child: ListView( children: [ - Obx(() => c.loadingPageInfo.value - ? buildLoading(context) - : buildPageInfoList(context)) + Obx(() => c.loadingPageInfo.value ? buildLoading(context) : buildPageInfoList(context)) ], ), ), diff --git a/lib/pages/wiki/view.dart b/lib/pages/wiki/view.dart index eb72e1b..4032e18 100755 --- a/lib/pages/wiki/view.dart +++ b/lib/pages/wiki/view.dart @@ -28,11 +28,7 @@ class MinimumArticleData { final String? mainCategory; final DateTime? updateTime; - MinimumArticleData( - {required this.title, - this.description, - this.mainCategory, - this.updateTime}); + MinimumArticleData({required this.title, this.description, this.mainCategory, this.updateTime}); } class ArticleCategoryData { @@ -77,11 +73,9 @@ class WikiViewPageController extends GetxController { loading.value = true; MWResponse> pageInfoRes; if (pageId.value != 0) { - pageInfoRes = - await MWApiList.getPageInfoList(pageids: [pageId.value]); + pageInfoRes = await MWApiList.getPageInfoList(pageids: [pageId.value]); } else { - pageInfoRes = - await MWApiList.getPageInfoList(titles: [pageTitle.value]); + pageInfoRes = await MWApiList.getPageInfoList(titles: [pageTitle.value]); } if (pageInfoRes.data.isEmpty) { throw MWApiErrorException(code: 'no-page', info: "页面信息丢失"); @@ -96,13 +90,11 @@ class WikiViewPageController extends GetxController { var parseRes = await MWApiParse.parse(pageId: pageId.value); parseInfo.value = parseRes.data; } catch (err, stack) { - alert(Get.overlayContext!, ErrorUtils.getErrorMessage(err), - title: "错误"); + alert(Get.overlayContext!, ErrorUtils.getErrorMessage(err), title: "错误"); if (kDebugMode) { print("Exception in page: $err"); stack.printError(); } - } finally { loading.value = false; } } @@ -114,8 +106,7 @@ class WikiViewPageController extends GetxController { void handleShareButtonClick() { if (pageInfo.value != null) { - var pageUrl = - pageInfo.value!.fullurl ?? getPageUrl(pageInfo.value!.title); + var pageUrl = pageInfo.value!.fullurl ?? getPageUrl(pageInfo.value!.title); var shareTitle = "${pageInfo.value!.title} - ${Global.siteTitle}"; var shareText = "$shareTitle\n$pageUrl"; @@ -150,8 +141,7 @@ class WikiViewPage extends StatefulWidget { final String? targetPage; final int? targetPageId; - const WikiViewPage( - {super.key, this.targetPage, this.targetPageId, this.initialArticleData}); + const WikiViewPage({super.key, this.targetPage, this.targetPageId, this.initialArticleData}); @override State createState() => _WikiViewPageState(); @@ -173,7 +163,7 @@ class _WikiViewPageState extends ReactiveState { void dispose() { super.dispose(); - c.dispose(); + Get.delete(); } @override @@ -223,8 +213,7 @@ class _WikiViewPageState extends ReactiveState { semanticLabel: "讨论", ), Obx(() { - if (c.pageInfo.value != null && - flc.isFavorite(c.pageInfo.value!)) { + if (c.pageInfo.value != null && flc.isFavorite(c.pageInfo.value!)) { return NavBarButton( icon: CupertinoIcons.heart_fill, onPressed: () {},