完成页面信息的样式

main
落雨楓 2 years ago
parent 1aab54a6d8
commit 23264dfd7e

@ -5,15 +5,40 @@ var titleList = [];
var currentTitle = '_top'; var currentTitle = '_top';
var titleOffset = 10; var titleOffset = 10;
window.MugenApp = {
emit: function() {
window.flutter_inappwebview.callHandler.apply(this, arguments);
},
scrollToTitle: function (anchor) {
var el = document.getElementById(anchor);
if (el) {
var scrollTop = window.scrollY + el.getBoundingClientRect().top;
if (navigator.safeArea) {
scrollTop -= navigator.safeArea.top + titleOffset;
}
scrollTop = Math.max(0, scrollTop);
enableScrollSpy = false;
window.scrollTo({
top: scrollTop,
});
setTimeout(function() {
enableScrollSpy = true;
}, 50);
}
},
};
function onReadyStateChange() { function onReadyStateChange() {
if (_InAppWebViewReady && document.readyState === "complete") { if (_InAppWebViewReady && document.readyState === "complete") {
titleList = document.querySelectorAll('.mw-parser-output h1, .mw-parser-output h2, .mw-parser-output h3, .mw-parser-output h4, .mw-parser-output h5, .mw-parser-output h6'); titleList = document.querySelectorAll('.mw-parser-output h1, .mw-parser-output h2, .mw-parser-output h3, .mw-parser-output h4, .mw-parser-output h5, .mw-parser-output h6');
window.flutter_inappwebview.callHandler('pageLoaded', true); window.MugenApp.emit('pageLoaded', true);
} }
} }
window.addEventListener("flutterInAppWebViewPlatformReady", function(event) { window.addEventListener("flutterInAppWebViewPlatformReady", function(event) {
_InAppWebViewReady = true; _InAppWebViewReady = true;
window.flutter_inappwebview.callHandler('bridgeConnected', true); window.MugenApp.emit('bridgeConnected', true);
onReadyStateChange(); onReadyStateChange();
}); });
@ -67,28 +92,6 @@ document.addEventListener('scroll', debouce(function() {
if (newCurrentTitle && newCurrentTitle !== currentTitle) { if (newCurrentTitle && newCurrentTitle !== currentTitle) {
currentTitle = newCurrentTitle; currentTitle = newCurrentTitle;
window.flutter_inappwebview.callHandler('sectionChange', newCurrentTitle); window.MugenApp.emit('sectionChange', newCurrentTitle);
} }
}, 200), { passive: true }); }, 200), { passive: true });
window.MugenApp = {
scrollToTitle: function (anchor) {
var el = document.getElementById(anchor);
if (el) {
var scrollTop = window.scrollY + el.getBoundingClientRect().top;
if (navigator.safeArea) {
scrollTop -= navigator.safeArea.top + titleOffset;
}
scrollTop = Math.max(0, scrollTop);
enableScrollSpy = false;
window.scrollTo({
top: scrollTop,
});
setTimeout(function() {
enableScrollSpy = true;
}, 50);
}
},
};

@ -7,6 +7,8 @@ PODS:
- flutter_inappwebview/Core (0.0.1): - flutter_inappwebview/Core (0.0.1):
- Flutter - Flutter
- OrderedSet (~> 5.0) - OrderedSet (~> 5.0)
- flutter_share (0.0.1):
- Flutter
- flutter_web_browser (0.17.1): - flutter_web_browser (0.17.1):
- Flutter - Flutter
- fluttertoast (0.0.2): - fluttertoast (0.0.2):
@ -18,7 +20,10 @@ PODS:
- OrderedSet (5.0.0) - OrderedSet (5.0.0)
- package_info_plus (0.4.5): - package_info_plus (0.4.5):
- Flutter - Flutter
- path_provider_ios (0.0.1): - path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- share_plus (0.0.1):
- Flutter - Flutter
- sqflite (0.0.2): - sqflite (0.0.2):
- Flutter - Flutter
@ -36,10 +41,12 @@ PODS:
DEPENDENCIES: DEPENDENCIES:
- Flutter (from `Flutter`) - Flutter (from `Flutter`)
- flutter_inappwebview (from `.symlinks/plugins/flutter_inappwebview/ios`) - flutter_inappwebview (from `.symlinks/plugins/flutter_inappwebview/ios`)
- flutter_share (from `.symlinks/plugins/flutter_share/ios`)
- flutter_web_browser (from `.symlinks/plugins/flutter_web_browser/ios`) - flutter_web_browser (from `.symlinks/plugins/flutter_web_browser/ios`)
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`) - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`)
- share_plus (from `.symlinks/plugins/share_plus/ios`)
- sqflite (from `.symlinks/plugins/sqflite/ios`) - sqflite (from `.symlinks/plugins/sqflite/ios`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
- video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/ios`) - video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/ios`)
@ -57,14 +64,18 @@ EXTERNAL SOURCES:
:path: Flutter :path: Flutter
flutter_inappwebview: flutter_inappwebview:
:path: ".symlinks/plugins/flutter_inappwebview/ios" :path: ".symlinks/plugins/flutter_inappwebview/ios"
flutter_share:
:path: ".symlinks/plugins/flutter_share/ios"
flutter_web_browser: flutter_web_browser:
:path: ".symlinks/plugins/flutter_web_browser/ios" :path: ".symlinks/plugins/flutter_web_browser/ios"
fluttertoast: fluttertoast:
:path: ".symlinks/plugins/fluttertoast/ios" :path: ".symlinks/plugins/fluttertoast/ios"
package_info_plus: package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios" :path: ".symlinks/plugins/package_info_plus/ios"
path_provider_ios: path_provider_foundation:
:path: ".symlinks/plugins/path_provider_ios/ios" :path: ".symlinks/plugins/path_provider_foundation/ios"
share_plus:
:path: ".symlinks/plugins/share_plus/ios"
sqflite: sqflite:
:path: ".symlinks/plugins/sqflite/ios" :path: ".symlinks/plugins/sqflite/ios"
url_launcher_ios: url_launcher_ios:
@ -79,15 +90,17 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS: SPEC CHECKSUMS:
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
flutter_inappwebview: bfd58618f49dc62f2676de690fc6dcda1d6c3721 flutter_inappwebview: bfd58618f49dc62f2676de690fc6dcda1d6c3721
flutter_share: 4be0208963c60b537e6255ed2ce1faae61cd9ac2
flutter_web_browser: 7bccaafbb0c5b8862afe7bcd158f15557109f61f flutter_web_browser: 7bccaafbb0c5b8862afe7bcd158f15557109f61f
fluttertoast: eb263d302cc92e04176c053d2385237e9f43fad0 fluttertoast: eb263d302cc92e04176c053d2385237e9f43fad0
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e
path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02 path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852
share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
Toast: 91b396c56ee72a5790816f40d3a94dd357abc196 Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de url_launcher_ios: ae1517e5e344f5544fb090b079e11f399dfbe4d2
video_player_avfoundation: e489aac24ef5cf7af82702979ed16f2a5ef84cff video_player_avfoundation: e489aac24ef5cf7af82702979ed16f2a5ef84cff
wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f
webview_flutter_wkwebview: b7e70ef1ddded7e69c796c7390ee74180182971f webview_flutter_wkwebview: b7e70ef1ddded7e69c796c7390ee74180182971f

@ -3,7 +3,7 @@
archiveVersion = 1; archiveVersion = 1;
classes = { classes = {
}; };
objectVersion = 50; objectVersion = 54;
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
@ -217,6 +217,7 @@
}; };
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
); );
@ -253,6 +254,7 @@
}; };
9740EEB61CF901F6004384FC /* Run Script */ = { 9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
); );

@ -6,6 +6,18 @@ import 'package:isekai_wiki/utils/utils.dart';
part 'page_info.freezed.dart'; part 'page_info.freezed.dart';
part 'page_info.g.dart'; part 'page_info.g.dart';
@freezed
class MWPageProtectionInfo with _$MWPageProtectionInfo {
factory MWPageProtectionInfo({
required String type,
required String level,
required String expiry,
}) = _MWPageProtectionInfo;
factory MWPageProtectionInfo.fromJson(Map<String, dynamic> json) =>
_$MWPageProtectionInfoFromJson(json);
}
@JsonSerializable() @JsonSerializable()
class PageInfo { class PageInfo {
int pageid; int pageid;
@ -34,6 +46,12 @@ class PageInfo {
String? editurl; String? editurl;
String? canonicalurl; String? canonicalurl;
int? watchers;
int? visitingwatchers;
MWPageProtectionInfo? protection;
List<String>? restrictiontypes;
PageInfo({ PageInfo({
required this.pageid, required this.pageid,
required this.ns, required this.ns,
@ -52,6 +70,10 @@ class PageInfo {
this.fullurl, this.fullurl,
this.editurl, this.editurl,
this.canonicalurl, this.canonicalurl,
this.watchers,
this.visitingwatchers,
this.protection,
this.restrictiontypes,
}); });
String get mainTitle { String get mainTitle {
@ -62,7 +84,8 @@ class PageInfo {
return null; return null;
} }
factory PageInfo.fromJson(Map<String, dynamic> json) => _$PageInfoFromJson(json); factory PageInfo.fromJson(Map<String, dynamic> json) =>
_$PageInfoFromJson(json);
Map<String, dynamic> toJson() => _$PageInfoToJson(this); Map<String, dynamic> toJson() => _$PageInfoToJson(this);
@ -87,12 +110,15 @@ class PageInfo {
class PagesResponse with _$PagesResponse { class PagesResponse with _$PagesResponse {
factory PagesResponse({required List<PageInfo> pages}) = _PageResponse; factory PagesResponse({required List<PageInfo> pages}) = _PageResponse;
factory PagesResponse.fromJson(Map<String, dynamic> json) => _$PagesResponseFromJson(json); factory PagesResponse.fromJson(Map<String, dynamic> json) =>
_$PagesResponseFromJson(json);
} }
@Freezed(copyWith: false) @Freezed(copyWith: false)
class PageImageInfo with _$PageImageInfo { 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<String, dynamic> json) => _$PageImageInfoFromJson(json); factory PageImageInfo.fromJson(Map<String, dynamic> json) =>
_$PageImageInfoFromJson(json);
} }

@ -79,8 +79,8 @@ class IsekaiWikiApp extends StatelessWidget {
GlobalCupertinoLocalizations.delegate, GlobalCupertinoLocalizations.delegate,
], ],
supportedLocales: const [ supportedLocales: const [
Locale('zh', 'CN'),
Locale('en'), Locale('en'),
Locale('zh', 'CN'),
], ],
initialBinding: InitialBinding(), initialBinding: InitialBinding(),
home: _buildApp(context), home: _buildApp(context),
@ -97,7 +97,9 @@ class IsekaiWikiApp extends StatelessWidget {
? Styles.materialLightTheme ? Styles.materialLightTheme
: Styles.materialDarkTheme, : Styles.materialDarkTheme,
child: CupertinoTheme( child: CupertinoTheme(
data: Styles.cupertinoTheme.copyWith(brightness: brightness), child: child), data:
Styles.cupertinoTheme.copyWith(brightness: brightness),
child: child),
), ),
); );
} }

@ -0,0 +1,111 @@
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
class AutoWrap extends StatefulWidget {
final List<Widget> children;
const AutoWrap({super.key, required this.children});
@override
State<StatefulWidget> createState() => _AutoWrapState();
}
class _AutoWrapState extends State<AutoWrap> {
var _computed = false;
var _isFirstBuild = true;
final Map<int, double> _childWidth = {};
var _isRow = true;
void handleSizeChange(Size size, int id) {
_childWidth[id] = size.width;
}
@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;
setState(() {
_computed = true;
_isRow = sum < width;
});
}
}
@override
Widget build(BuildContext context) {
if (!_computed) {
List<Widget> stackChildren = [];
for (var i = 0; i < widget.children.length; i++) {
// Only first child is visible
stackChildren.add(
Opacity(
opacity: i == 0 ? 1 : 0,
child: WidgetSizeOffsetWrapper(
id: i,
onSizeChange: handleSizeChange,
child: widget.children[i],
),
),
);
}
return Stack(
children: stackChildren,
);
} else {
if (_isRow) {
return Row(children: widget.children);
} else {
return Column(children: widget.children);
}
}
}
}
typedef OnWidgetSizeChange = void Function(Size size, int id);
class WidgetSizeRenderObject extends RenderProxyBox {
final OnWidgetSizeChange onSizeChange;
final int id;
Size? currentSize;
WidgetSizeRenderObject(this.onSizeChange, this.id);
@override
void performLayout() {
super.performLayout();
try {
Size? newSize = child?.size;
if (newSize != null && currentSize != newSize) {
currentSize = newSize;
WidgetsBinding.instance.addPostFrameCallback((_) {
onSizeChange(newSize, id);
});
}
} catch (e) {
print(e);
}
}
}
class WidgetSizeOffsetWrapper extends SingleChildRenderObjectWidget {
final OnWidgetSizeChange onSizeChange;
final int id;
const WidgetSizeOffsetWrapper({
Key? key,
required this.onSizeChange,
required this.id,
required Widget child,
}) : super(key: key, child: child);
@override
RenderObject createRenderObject(BuildContext context) {
return WidgetSizeRenderObject(onSizeChange, id);
}
}

@ -7,22 +7,28 @@ import 'package:isekai_wiki/api/response/page_info.dart';
import 'package:isekai_wiki/components/flutter_scale_tap/flutter_scale_tap.dart'; import 'package:isekai_wiki/components/flutter_scale_tap/flutter_scale_tap.dart';
import 'package:isekai_wiki/components/isekai_text.dart'; import 'package:isekai_wiki/components/isekai_text.dart';
import 'package:isekai_wiki/components/utils.dart'; import 'package:isekai_wiki/components/utils.dart';
import 'package:isekai_wiki/pages/article.dart'; import 'package:isekai_wiki/pages/wiki/info.dart';
import 'package:isekai_wiki/pages/wiki/view.dart';
import 'package:like_button/like_button.dart'; import 'package:like_button/like_button.dart';
import 'package:pull_down_button/pull_down_button.dart'; import 'package:pull_down_button/pull_down_button.dart';
import 'package:skeletons/skeletons.dart'; import 'package:skeletons/skeletons.dart';
typedef AddFavoriteCallback = Future<bool> Function(PageInfo pageInfo, bool localIsFavorite); typedef AddFavoriteCallback = Future<bool> Function(
PageInfo pageInfo, bool localIsFavorite);
typedef PageInfoCallback = Future<void> Function(PageInfo pageInfo); typedef PageInfoCallback = Future<void> Function(PageInfo pageInfo);
class PageCardStyles { class PageCardStyles {
static const cardContainerHeight = 100.0; static const cardContainerHeight = 100.0;
static const cardContentHeight = 100.0; static const cardContentHeight = 100.0;
static const cardHeaderPadding = EdgeInsets.only(top: 20, left: 20, right: 20, bottom: 16); static const cardHeaderPadding =
static const cardHeaderCompactPadding = EdgeInsets.only(top: 20, left: 20, right: 20, bottom: 10); EdgeInsets.only(top: 20, left: 20, right: 20, bottom: 16);
static const cardContentPadding = EdgeInsets.only(top: 0, left: 20, right: 20, bottom: 14); static const cardHeaderCompactPadding =
static const cardFooterPadding = EdgeInsets.only(top: 0, left: 20, right: 20, bottom: 14); EdgeInsets.only(top: 20, left: 20, right: 20, bottom: 10);
static const cardContentPadding =
EdgeInsets.only(top: 0, left: 20, right: 20, bottom: 14);
static const cardFooterPadding =
EdgeInsets.only(top: 0, left: 20, right: 20, bottom: 14);
static const titleFontSize = 24.0; static const titleFontSize = 24.0;
static const subTitleFontSize = 12.0; static const subTitleFontSize = 12.0;
static const contentFontSize = 14.0; static const contentFontSize = 14.0;
@ -95,8 +101,18 @@ class PageCard extends StatelessWidget {
} }
} }
void handlePageInfoClick() { void handlePageInfoClick() async {
if (pageInfo != null) {} if (pageInfo != null) {
var cPageInfo = pageInfo!;
await Navigator.of(Get.context!).push(
CupertinoPageRoute(
builder: (_) => WikiInfoPage(
targetPage: cPageInfo.title,
targetPageId: cPageInfo.pageid,
),
),
);
}
} }
Future<void> handleCardClick() async { Future<void> handleCardClick() async {
@ -108,8 +124,9 @@ class PageCard extends StatelessWidget {
var cPageInfo = pageInfo!; var cPageInfo = pageInfo!;
await Navigator.of(Get.context!).push( await Navigator.of(Get.context!).push(
CupertinoPageRoute( CupertinoPageRoute(
builder: (_) => ArticlePage( builder: (_) => WikiViewPage(
targetPage: cPageInfo.title, targetPage: cPageInfo.title,
targetPageId: cPageInfo.pageid,
initialArticleData: MinimumArticleData( initialArticleData: MinimumArticleData(
title: cPageInfo.mainTitle, title: cPageInfo.mainTitle,
description: cPageInfo.description, description: cPageInfo.description,
@ -127,7 +144,8 @@ class PageCard extends StatelessWidget {
isLoading: isLoading, isLoading: isLoading,
skeleton: const SkeletonLine( skeleton: const SkeletonLine(
style: SkeletonLineStyle( style: SkeletonLineStyle(
width: PageCardStyles.footerButtonSize, height: PageCardStyles.footerButtonSize), width: PageCardStyles.footerButtonSize,
height: PageCardStyles.footerButtonSize),
), ),
child: child, child: child,
); );
@ -154,7 +172,8 @@ class PageCard extends StatelessWidget {
isLoading: isLoading, isLoading: isLoading,
skeleton: SkeletonLine( skeleton: SkeletonLine(
style: SkeletonLineStyle( style: SkeletonLineStyle(
height: (PageCardStyles.titleFontSize * 1.1) * textScale, randomLength: true), height: (PageCardStyles.titleFontSize * 1.1) * textScale,
randomLength: true),
), ),
child: IsekaiText( child: IsekaiText(
pageInfo?.mainTitle ?? "页面信息丢失", pageInfo?.mainTitle ?? "页面信息丢失",
@ -186,12 +205,15 @@ class PageCard extends StatelessWidget {
style: SkeletonParagraphStyle( style: SkeletonParagraphStyle(
lines: 3, lines: 3,
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(
vertical: PageCardStyles.contentFontSize * textScale * 0.2, vertical: PageCardStyles.contentFontSize *
textScale *
0.2,
horizontal: 0, horizontal: 0,
), ),
lineStyle: SkeletonLineStyle( lineStyle: SkeletonLineStyle(
randomLength: true, randomLength: true,
height: PageCardStyles.contentFontSize * textScale), height:
PageCardStyles.contentFontSize * textScale),
), ),
), ),
) )
@ -232,7 +254,9 @@ class PageCard extends StatelessWidget {
style: const TextStyle(color: Colors.black54)), style: const TextStyle(color: Colors.black54)),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap) materialTapTargetSize: MaterialTapTargetSize.shrinkWrap)
: const SizedBox(), : const SizedBox(),
pageInfo?.mainCategory != null ? const SizedBox(width: 10) : const SizedBox(), pageInfo?.mainCategory != null
? const SizedBox(width: 10)
: const SizedBox(),
// //
Skeleton( Skeleton(
isLoading: isLoading, isLoading: isLoading,
@ -240,7 +264,9 @@ class PageCard extends StatelessWidget {
style: SkeletonLineStyle(width: 100), style: SkeletonLineStyle(width: 100),
), ),
child: Text( child: Text(
pageInfo?.updatedTime != null ? Utils.getFriendDate(pageInfo!.updatedTime!) : "", pageInfo?.updatedTime != null
? Utils.getFriendDate(pageInfo!.updatedTime!)
: "",
overflow: TextOverflow.fade, overflow: TextOverflow.fade,
style: PageCardStyles.contentTextStyle, style: PageCardStyles.contentTextStyle,
textScaleFactor: max(1, MediaQuery.of(context).textScaleFactor), textScaleFactor: max(1, MediaQuery.of(context).textScaleFactor),
@ -345,7 +371,8 @@ class PageCard extends StatelessWidget {
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Theme.of(context).cardTheme.shadowColor ?? Colors.transparent, color:
Theme.of(context).cardTheme.shadowColor ?? Colors.transparent,
blurRadius: 12, blurRadius: 12,
offset: const Offset(0, 2), offset: const Offset(0, 2),
), ),
@ -353,7 +380,8 @@ class PageCard extends StatelessWidget {
), ),
child: SizedBox( child: SizedBox(
height: PageCardStyles.cardContainerHeight + height: PageCardStyles.cardContainerHeight +
(PageCardStyles.cardContentHeight * max(1, MediaQuery.of(context).textScaleFactor)), (PageCardStyles.cardContentHeight *
max(1, MediaQuery.of(context).textScaleFactor)),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [

@ -4,7 +4,7 @@ import 'package:get/get.dart';
import 'package:isekai_wiki/api/response/page_info.dart'; import 'package:isekai_wiki/api/response/page_info.dart';
import 'package:isekai_wiki/components/flutter_scale_tap/flutter_scale_tap.dart'; import 'package:isekai_wiki/components/flutter_scale_tap/flutter_scale_tap.dart';
import 'package:isekai_wiki/components/utils.dart'; import 'package:isekai_wiki/components/utils.dart';
import 'package:isekai_wiki/pages/article.dart'; import 'package:isekai_wiki/pages/wiki/view.dart';
import 'package:isekai_wiki/reactive/reactive.dart'; import 'package:isekai_wiki/reactive/reactive.dart';
import 'package:like_button/like_button.dart'; import 'package:like_button/like_button.dart';
import 'package:pull_down_button/pull_down_button.dart'; import 'package:pull_down_button/pull_down_button.dart';

@ -13,6 +13,7 @@ class RecentPageListController extends GetxController {
ScrollController? scrollController; ScrollController? scrollController;
UserController? uc; UserController? uc;
FavoriteListController? flc; FavoriteListController? flc;
double insetBottom = 0;
var shouldRefresh = true; var shouldRefresh = true;
@ -28,7 +29,8 @@ class RecentPageListController extends GetxController {
@override @override
void onInit() { void onInit() {
scrollController?.addListener(() { scrollController?.addListener(() {
if (scrollController!.position.pixels > scrollController!.position.maxScrollExtent - 10) { if (scrollController!.position.pixels >
scrollController!.position.maxScrollExtent - 10) {
// //
if (hasNextPage.value && !isLoading.value) { if (hasNextPage.value && !isLoading.value) {
loadNextPages(); loadNextPages();
@ -72,7 +74,8 @@ class RecentPageListController extends GetxController {
try { try {
var rcListRes = await MWApiList.getMixedRecentChanges( var rcListRes = await MWApiList.getMixedRecentChanges(
limit: 10, continueInfo: continueInfo.isNotEmpty ? continueInfo : null); limit: 10,
continueInfo: continueInfo.isNotEmpty ? continueInfo : null);
var pageIds = rcListRes.data.map((rcInfo) => rcInfo.pageid).toList(); var pageIds = rcListRes.data.map((rcInfo) => rcInfo.pageid).toList();
var pageListRes = await MWApiList.getPageInfoList( var pageListRes = await MWApiList.getPageInfoList(
@ -116,23 +119,23 @@ class RecentPageListController extends GetxController {
class RecentPageList extends StatelessWidget { class RecentPageList extends StatelessWidget {
final ScrollController? scrollController; final ScrollController? scrollController;
final double insetBottom;
const RecentPageList({ const RecentPageList(
super.key, {super.key, this.scrollController, this.insetBottom = 0});
this.scrollController,
});
Widget _buildSkeletonList() { Widget _buildSkeletonList() {
return Column( return Column(
key: key, key: key,
children: [ children: [
for (var i = 0; i < 6; i++) PageCard(key: ValueKey("rpl-card-$i"), isLoading: true), for (var i = 0; i < 6; i++)
PageCard(key: ValueKey("rpl-card-$i"), isLoading: true),
], ],
); );
} }
Widget _buildPageCard( Widget _buildPageCard(int index, PageInfo pageInfo,
int index, PageInfo pageInfo, RecentPageListController c, FavoriteListController flc) { RecentPageListController c, FavoriteListController flc) {
return PageCard( return PageCard(
key: ValueKey("rpl-card-$index"), key: ValueKey("rpl-card-$index"),
pageInfo: c.pageList[index], pageInfo: c.pageList[index],
@ -169,12 +172,15 @@ class RecentPageList extends StatelessWidget {
child: Center( child: Center(
child: Obx( child: Obx(
() => c.isLoading.value () => c.isLoading.value
? const CupertinoActivityIndicator( ? Padding(
radius: 14, padding: EdgeInsets.only(bottom: insetBottom),
child: const CupertinoActivityIndicator(
radius: 14,
),
) )
: const SizedBox( : SizedBox(
width: 28, width: 28,
height: 28, height: 28 + insetBottom,
), ),
), ),
), ),

@ -7,6 +7,7 @@ import 'package:get/get.dart';
import 'package:isekai_wiki/components/isekai_nav_bar.dart'; import 'package:isekai_wiki/components/isekai_nav_bar.dart';
import 'package:isekai_wiki/components/isekai_page_scaffold.dart'; import 'package:isekai_wiki/components/isekai_page_scaffold.dart';
import 'package:isekai_wiki/components/recent_page_list.dart'; import 'package:isekai_wiki/components/recent_page_list.dart';
import 'package:isekai_wiki/components/safearea_builder.dart';
import 'package:isekai_wiki/models/user.dart'; import 'package:isekai_wiki/models/user.dart';
import 'package:isekai_wiki/pages/tab_page.dart'; import 'package:isekai_wiki/pages/tab_page.dart';
import 'package:web_smooth_scroll/web_smooth_scroll.dart'; import 'package:web_smooth_scroll/web_smooth_scroll.dart';
@ -22,7 +23,8 @@ const Color _kDefaultTabBarBorderColor = CupertinoDynamicColor.withBrightness(
enum HomeTabs { newest, followed } enum HomeTabs { newest, followed }
class HomeController extends GetxController with GetSingleTickerProviderStateMixin { class HomeController extends GetxController
with GetSingleTickerProviderStateMixin {
double _navSearchButtonOffset = 90; double _navSearchButtonOffset = 90;
var showNavSearchButton = false.obs; var showNavSearchButton = false.obs;
@ -41,12 +43,15 @@ class HomeController extends GetxController with GetSingleTickerProviderStateMix
void onInit() { void onInit() {
tabController = TabController(length: 2, vsync: this); tabController = TabController(length: 2, vsync: this);
_navSearchButtonOffset = 48 * MediaQuery.of(Get.context!).textScaleFactor + 48; _navSearchButtonOffset =
48 * MediaQuery.of(Get.context!).textScaleFactor + 48;
scrollController.addListener(() { scrollController.addListener(() {
if (scrollController.offset >= _navSearchButtonOffset && !showNavSearchButton.value) { if (scrollController.offset >= _navSearchButtonOffset &&
!showNavSearchButton.value) {
showNavSearchButton.value = true; showNavSearchButton.value = true;
} else if (scrollController.offset < _navSearchButtonOffset && showNavSearchButton.value) { } else if (scrollController.offset < _navSearchButtonOffset &&
showNavSearchButton.value) {
showNavSearchButton.value = false; showNavSearchButton.value = false;
} }
}); });
@ -104,7 +109,8 @@ class HomeTab extends StatelessWidget {
duration: const Duration(milliseconds: 100), duration: const Duration(milliseconds: 100),
child: CupertinoButton( child: CupertinoButton(
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
child: const Icon(CupertinoIcons.bell, size: 26, color: Styles.themeNavTitleColor), child: const Icon(CupertinoIcons.bell,
size: 26, color: Styles.themeNavTitleColor),
onPressed: () {}, onPressed: () {},
), ),
), ),
@ -120,7 +126,8 @@ class HomeTab extends StatelessWidget {
duration: const Duration(milliseconds: 100), duration: const Duration(milliseconds: 100),
child: CupertinoButton( child: CupertinoButton(
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
child: const Icon(CupertinoIcons.search, size: 26, color: Styles.themeNavTitleColor), child: const Icon(CupertinoIcons.search,
size: 26, color: Styles.themeNavTitleColor),
onPressed: () { onPressed: () {
onSearchClick?.call(); onSearchClick?.call();
}, },
@ -134,126 +141,135 @@ class HomeTab extends StatelessWidget {
final c = Get.put(HomeController()); final c = Get.put(HomeController());
return IsekaiPageScaffold( return IsekaiPageScaffold(
child: WebSmoothScroll( child: SafeAreaBuilder(
controller: c.scrollController, builder: (_, safeArea) => WebSmoothScroll(
child: CustomScrollView(
controller: c.scrollController, controller: c.scrollController,
physics: const BouncingScrollPhysics( child: CustomScrollView(
parent: AlwaysScrollableScrollPhysics(), controller: c.scrollController,
), physics: const BouncingScrollPhysics(
slivers: <Widget>[ parent: AlwaysScrollableScrollPhysics(),
IsekaiSliverNavigationBar(
leading: _buildSearchIconButton(),
backgroundColor: Styles.themeMainColor,
brightness: Brightness.dark,
largeTitle: const Text('首页', style: TextStyle(color: Styles.themeNavTitleColor)),
trailing: _buildNotificationIconButton(),
border: Border.all(style: BorderStyle.none),
), ),
SliverPersistentHeader( slivers: <Widget>[
delegate: _SliverAppBarDelegate( IsekaiSliverNavigationBar(
minHeight: 48, leading: _buildSearchIconButton(),
maxHeight: 48, backgroundColor: Styles.themeMainColor,
child: Container( brightness: Brightness.dark,
decoration: const BoxDecoration( largeTitle: const Text('首页',
color: Styles.themeMainColor, style: TextStyle(color: Styles.themeNavTitleColor)),
boxShadow: [ trailing: _buildNotificationIconButton(),
BoxShadow( border: Border.all(style: BorderStyle.none),
color: Styles.themeMainColor, ),
blurRadius: 0.0, SliverPersistentHeader(
spreadRadius: 0.0, delegate: _SliverAppBarDelegate(
offset: Offset(0, -2), minHeight: 48,
), maxHeight: 48,
], child: Container(
), decoration: const BoxDecoration(
padding: const EdgeInsets.only(bottom: 10, left: 12, right: 12), color: Styles.themeMainColor,
child: CupertinoButton( boxShadow: [
color: Colors.white, BoxShadow(
padding: const EdgeInsets.all(0), color: Styles.themeMainColor,
onPressed: () { blurRadius: 0.0,
onSearchClick?.call(); spreadRadius: 0.0,
}, offset: Offset(0, -2),
child: Wrap(
spacing: 14,
alignment: WrapAlignment.spaceBetween,
children: [
Container(
padding: const EdgeInsets.all(1),
child: const Icon(CupertinoIcons.search, color: Colors.black54),
), ),
const Text("搜索页面...",
textAlign: TextAlign.center, style: TextStyle(color: Colors.black54))
], ],
), ),
padding:
const EdgeInsets.only(bottom: 10, left: 12, right: 12),
child: CupertinoButton(
color: Colors.white,
padding: const EdgeInsets.all(0),
onPressed: () {
onSearchClick?.call();
},
child: Wrap(
spacing: 14,
alignment: WrapAlignment.spaceBetween,
children: [
Container(
padding: const EdgeInsets.all(1),
child: const Icon(CupertinoIcons.search,
color: Colors.black54),
),
const Text("搜索页面...",
textAlign: TextAlign.center,
style: TextStyle(color: Colors.black54))
],
),
),
), ),
), ),
), ),
), if (Global.siteConfig.enableFollowing) //
if (Global.siteConfig.enableFollowing) // SliverPersistentHeader(
pinned: true,
delegate: _SliverAppBarDelegate(
minHeight: 40.0,
maxHeight: 40.0,
child: Container(
decoration:
BoxDecoration(color: Colors.white, boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.2),
spreadRadius: 2,
blurRadius: 4,
offset: const Offset(0, 2),
)
]),
child: Row(
children: [
SizedBox(
child: TabBar(
isScrollable: true,
controller: c.tabController,
indicatorColor: Styles.themeMainColor,
labelColor: Styles.themeMainColor,
unselectedLabelColor: Colors.black45,
tabs: const [
CollapsedTabText('最新'),
CollapsedTabText('关注')
],
onTap: (int selected) {},
),
),
const Expanded(child: Text('')),
],
),
),
),
),
SliverPersistentHeader( SliverPersistentHeader(
pinned: true, pinned: true,
delegate: _SliverAppBarDelegate( delegate: _SliverAppBarDelegate(
minHeight: 40.0, minHeight: 1,
maxHeight: 40.0, maxHeight: 1,
child: Container( child: Container(
decoration: BoxDecoration(color: Colors.white, boxShadow: [ decoration: BoxDecoration(
BoxShadow( color: Theme.of(context).shadowColor,
color: Colors.grey.withOpacity(0.2), boxShadow: [
spreadRadius: 2, BoxShadow(
blurRadius: 4, color: Theme.of(context).shadowColor,
offset: const Offset(0, 2), spreadRadius: 5,
) blurRadius: 4,
]), )
child: Row(
children: [
SizedBox(
child: TabBar(
isScrollable: true,
controller: c.tabController,
indicatorColor: Styles.themeMainColor,
labelColor: Styles.themeMainColor,
unselectedLabelColor: Colors.black45,
tabs: const [CollapsedTabText('最新'), CollapsedTabText('关注')],
onTap: (int selected) {},
),
),
const Expanded(child: Text('')),
], ],
), ),
), ),
), ),
), ),
SliverPersistentHeader( CupertinoSliverRefreshControl(
pinned: true, onRefresh: c.handleRefresh,
delegate: _SliverAppBarDelegate(
minHeight: 1,
maxHeight: 1,
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).shadowColor,
boxShadow: [
BoxShadow(
color: Theme.of(context).shadowColor,
spreadRadius: 5,
blurRadius: 4,
)
],
),
),
), ),
), SliverSafeArea(
CupertinoSliverRefreshControl( top: false,
onRefresh: c.handleRefresh, minimum: const EdgeInsets.only(top: 12, bottom: 12),
), sliver: RecentPageList(
SliverSafeArea( scrollController: c.scrollController,
top: false, ),
bottom: false,
minimum: const EdgeInsets.only(top: 12, bottom: 12),
sliver: RecentPageList(
scrollController: c.scrollController,
), ),
), ],
], ),
), ),
), ),
); );
@ -278,7 +294,8 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
double get maxExtent => max(maxHeight, minHeight); double get maxExtent => max(maxHeight, minHeight);
@override @override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) { Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
return SizedBox.expand(child: child); return SizedBox.expand(child: child);
} }

@ -0,0 +1,213 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:isekai_wiki/api/mw/list.dart';
import 'package:isekai_wiki/api/mw/mw_api.dart';
import 'package:isekai_wiki/api/response/page_info.dart';
import 'package:isekai_wiki/components/auto_wrap.dart';
import 'package:isekai_wiki/components/isekai_nav_bar.dart';
import 'package:isekai_wiki/components/isekai_page_scaffold.dart';
import 'package:isekai_wiki/global.dart';
import 'package:isekai_wiki/reactive/reactive.dart';
import 'package:isekai_wiki/utils/dialog.dart';
import 'package:isekai_wiki/utils/error.dart';
class WikiInfoPageController extends GetxController {
var pageTitle = "".obs;
var pageId = 0.obs;
var pageInfo = Rx<PageInfo?>(null);
var loadingPageInfo = true.obs;
@override
void onInit() {
super.onInit();
Future.delayed(const Duration(milliseconds: 150)).then((_) {
loadPageInfo();
});
}
Future<void> loadPageInfo() async {
if (pageTitle.isNotEmpty || pageId.value != 0) {
try {
//
loadingPageInfo.value = true;
MWResponse<List<PageInfo>> pageInfoRes;
if (pageId.value != 0) {
pageInfoRes = await MWApiList.getPageInfoList(pageids: [
pageId.value
], prop: [
"extracts",
"info",
"pageimages",
"pageviews"
], extraParams: {
"inprop":
"url|protection|watched|watchers|visitingwatchers|displaytitle",
});
} else {
pageInfoRes =
await MWApiList.getPageInfoList(titles: [pageTitle.value]);
}
if (pageInfoRes.data.isEmpty) {
throw MWApiErrorException(code: 'no-page', info: "页面信息丢失");
}
pageInfo.value = pageInfoRes.data[0];
pageId.value = pageInfo.value!.pageid;
pageTitle.value = pageInfo.value!.title;
} catch (err, stack) {
alert(Get.overlayContext!, ErrorUtils.getErrorMessage(err),
title: "错误");
if (kDebugMode) {
print("Exception in page: $err");
stack.printError();
}
} finally {
loadingPageInfo.value = false;
}
} else {
alert(Get.overlayContext!, "页面不存在");
}
}
}
class WikiInfoPage extends StatefulWidget {
final String? targetPage;
final int? targetPageId;
const WikiInfoPage({super.key, this.targetPage, this.targetPageId});
@override
State<StatefulWidget> createState() => _WikiInfoPageState();
}
class _WikiInfoPageState extends ReactiveState<WikiInfoPage> {
WikiInfoPageController c = WikiInfoPageController();
@override
void initState() {
super.initState();
c = Get.put(c);
}
@override
void dispose() {
c.dispose();
super.dispose();
}
@override
void receiveProps() {
c.pageId.value = widget.targetPageId ?? 0;
c.pageTitle.value = widget.targetPage ?? "";
}
Widget buildLoading(BuildContext context) {
return CupertinoListSection.insetGrouped(
header: Text("基本信息"),
backgroundColor: CupertinoTheme.of(context).scaffoldBackgroundColor,
children: <CupertinoListTile>[
CupertinoListTile.notched(
title: Center(
child: CupertinoActivityIndicator(
radius: 10 * MediaQuery.of(context).textScaleFactor),
),
),
],
);
}
CupertinoListTile buildPageInfo(
BuildContext context, String label, dynamic value,
{VoidFutureCallback? onTap}) {
final Locale appLocale = Localizations.localeOf(context);
var valueStr = "未知";
if (value is String) {
valueStr = value;
}
if (value is int) {
valueStr =
NumberFormat.decimalPattern(appLocale.toLanguageTag()).format(value);
} else if (value is double) {
var pattern = NumberFormat.decimalPattern(appLocale.toLanguageTag());
pattern.maximumFractionDigits = 2;
valueStr = pattern.format(value);
} else if (value is DateTime) {
// ignore: prefer_interpolation_to_compose_strings
valueStr = DateFormat.yMMMd(appLocale.toLanguageTag()).format(value) +
" " +
DateFormat.Hms(appLocale.toLanguageTag()).format(value);
}
return CupertinoListTile.notched(
padding: const EdgeInsetsDirectional.fromSTEB(20.0, 10.0, 14.0, 10.0),
title: Container(
width: MediaQuery.of(context).size.width,
child: AutoWrap(
children: [
Text(label),
Text(
valueStr,
style: TextStyle(
color: CupertinoColors.systemGrey2.resolveFrom(context)),
),
],
),
),
trailing: onTap != null ? const CupertinoListTileChevron() : null,
onTap: onTap,
);
}
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: <CupertinoListTile>[
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),
],
),
),
],
);
}
@override
Widget render(BuildContext context) {
return IsekaiPageScaffold(
navigationBar: const IsekaiNavigationBar(
middle: Text("页面信息"),
),
child: SafeArea(
child: ListView(
children: [
Obx(() => c.loadingPageInfo.value
? buildLoading(context)
: buildPageInfoList(context))
],
),
),
);
}
}

@ -1,6 +1,5 @@
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter_share/flutter_share.dart';
import 'package:flutter_slider_drawer/flutter_slider_drawer.dart'; import 'package:flutter_slider_drawer/flutter_slider_drawer.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:isekai_wiki/api/mw/list.dart'; import 'package:isekai_wiki/api/mw/list.dart';
@ -13,6 +12,8 @@ import 'package:isekai_wiki/components/nav_bar_button.dart';
import 'package:isekai_wiki/components/safearea_builder.dart'; import 'package:isekai_wiki/components/safearea_builder.dart';
import 'package:isekai_wiki/components/toc.dart'; import 'package:isekai_wiki/components/toc.dart';
import 'package:isekai_wiki/components/wikipage_parser.dart'; import 'package:isekai_wiki/components/wikipage_parser.dart';
import 'package:isekai_wiki/components/isekai_nav_bar.dart';
import 'package:isekai_wiki/components/isekai_page_scaffold.dart';
import 'package:isekai_wiki/global.dart'; import 'package:isekai_wiki/global.dart';
import 'package:isekai_wiki/models/favorite_list.dart'; import 'package:isekai_wiki/models/favorite_list.dart';
import 'package:isekai_wiki/reactive/reactive.dart'; import 'package:isekai_wiki/reactive/reactive.dart';
@ -21,16 +22,17 @@ import 'package:isekai_wiki/utils/error.dart';
import 'package:isekai_wiki/utils/utils.dart'; import 'package:isekai_wiki/utils/utils.dart';
import 'package:share_plus/share_plus.dart'; import 'package:share_plus/share_plus.dart';
import '../components/isekai_nav_bar.dart';
import '../components/isekai_page_scaffold.dart';
class MinimumArticleData { class MinimumArticleData {
final String title; final String title;
final String? description; final String? description;
final String? mainCategory; final String? mainCategory;
final DateTime? updateTime; 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 { class ArticleCategoryData {
@ -40,7 +42,7 @@ class ArticleCategoryData {
ArticleCategoryData({required this.name, required this.identity}); ArticleCategoryData({required this.name, required this.identity});
} }
class ArticlePageController extends GetxController { class WikiViewPageController extends GetxController {
WikiPageParserController? parserController; WikiPageParserController? parserController;
var loading = true.obs; var loading = true.obs;
@ -75,9 +77,11 @@ class ArticlePageController extends GetxController {
loading.value = true; loading.value = true;
MWResponse<List<PageInfo>> pageInfoRes; MWResponse<List<PageInfo>> pageInfoRes;
if (pageId.value != 0) { if (pageId.value != 0) {
pageInfoRes = await MWApiList.getPageInfoList(pageids: [pageId.value]); pageInfoRes =
await MWApiList.getPageInfoList(pageids: [pageId.value]);
} else { } else {
pageInfoRes = await MWApiList.getPageInfoList(titles: [pageTitle.value]); pageInfoRes =
await MWApiList.getPageInfoList(titles: [pageTitle.value]);
} }
if (pageInfoRes.data.isEmpty) { if (pageInfoRes.data.isEmpty) {
throw MWApiErrorException(code: 'no-page', info: "页面信息丢失"); throw MWApiErrorException(code: 'no-page', info: "页面信息丢失");
@ -92,7 +96,8 @@ class ArticlePageController extends GetxController {
var parseRes = await MWApiParse.parse(pageId: pageId.value); var parseRes = await MWApiParse.parse(pageId: pageId.value);
parseInfo.value = parseRes.data; parseInfo.value = parseRes.data;
} catch (err, stack) { } catch (err, stack) {
alert(Get.overlayContext!, ErrorUtils.getErrorMessage(err), title: "错误"); alert(Get.overlayContext!, ErrorUtils.getErrorMessage(err),
title: "错误");
if (kDebugMode) { if (kDebugMode) {
print("Exception in page: $err"); print("Exception in page: $err");
stack.printError(); stack.printError();
@ -109,12 +114,13 @@ class ArticlePageController extends GetxController {
void handleShareButtonClick() { void handleShareButtonClick() {
if (pageInfo.value != null) { 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 shareTitle = "${pageInfo.value!.title} - ${Global.siteTitle}";
var shareText = "$shareTitle\n$pageUrl"; var shareText = "$shareTitle\n$pageUrl";
FlutterShare.share(
title: shareTitle, text: shareTitle, linkUrl: pageUrl, chooserTitle: "分享到"); Share.share(shareText, subject: shareTitle);
} else { } else {
alert(Get.overlayContext!, "页面还未加载完成"); alert(Get.overlayContext!, "页面还未加载完成");
} }
@ -139,19 +145,20 @@ class ArticlePageController extends GetxController {
} }
} }
class ArticlePage extends StatefulWidget { class WikiViewPage extends StatefulWidget {
final MinimumArticleData? initialArticleData; final MinimumArticleData? initialArticleData;
final String? targetPage; final String? targetPage;
final int? targetPageId; final int? targetPageId;
const ArticlePage({super.key, this.targetPage, this.targetPageId, this.initialArticleData}); const WikiViewPage(
{super.key, this.targetPage, this.targetPageId, this.initialArticleData});
@override @override
State<StatefulWidget> createState() => _ArticlePageState(); State<StatefulWidget> createState() => _WikiViewPageState();
} }
class _ArticlePageState extends ReactiveState<ArticlePage> { class _WikiViewPageState extends ReactiveState<WikiViewPage> {
var c = ArticlePageController(); var c = WikiViewPageController();
@override @override
void initState() { void initState() {
@ -216,7 +223,8 @@ class _ArticlePageState extends ReactiveState<ArticlePage> {
semanticLabel: "讨论", semanticLabel: "讨论",
), ),
Obx(() { Obx(() {
if (c.pageInfo.value != null && flc.isFavorite(c.pageInfo.value!)) { if (c.pageInfo.value != null &&
flc.isFavorite(c.pageInfo.value!)) {
return NavBarButton( return NavBarButton(
icon: CupertinoIcons.heart_fill, icon: CupertinoIcons.heart_fill,
onPressed: () {}, onPressed: () {},

@ -2,5 +2,6 @@ import 'package:isekai_wiki/global.dart';
String getPageUrl(String title) { String getPageUrl(String title) {
var titleEncoded = Uri.encodeFull(title.replaceAll(RegExp(r" "), "_")); var titleEncoded = Uri.encodeFull(title.replaceAll(RegExp(r" "), "_"));
return Global.siteConfig.pageUrlTemplate.replaceAll(RegExp(r"\$1"), titleEncoded); return Global.siteConfig.pageUrlTemplate
.replaceAll(RegExp(r"\$1"), titleEncoded);
} }

@ -1,4 +1,4 @@
platform :osx, '10.11' platform :osx, '10.14'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency. # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true' ENV['COCOAPODS_DISABLE_STATS'] = 'true'

@ -3,7 +3,7 @@
archiveVersion = 1; archiveVersion = 1;
classes = { classes = {
}; };
objectVersion = 51; objectVersion = 54;
objects = { objects = {
/* Begin PBXAggregateTarget section */ /* Begin PBXAggregateTarget section */
@ -273,6 +273,7 @@
}; };
3399D490228B24CF009A79C7 /* ShellScript */ = { 3399D490228B24CF009A79C7 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
); );
@ -404,7 +405,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.13; MACOSX_DEPLOYMENT_TARGET = 10.14;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx; SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule; SWIFT_COMPILATION_MODE = wholemodule;
@ -483,7 +484,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.13; MACOSX_DEPLOYMENT_TARGET = 10.14;
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx; SDKROOT = macosx;
@ -530,7 +531,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.13; MACOSX_DEPLOYMENT_TARGET = 10.14;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx; SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule; SWIFT_COMPILATION_MODE = wholemodule;

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save