diff --git a/lib/api/base_api.dart b/lib/api/base_api.dart index 1c70d22..23e167f 100644 --- a/lib/api/base_api.dart +++ b/lib/api/base_api.dart @@ -110,11 +110,16 @@ class BaseApi { static Future post(Uri uri, {Map? search, dynamic data}) async { var client = await getClient(); + String? contentType; + if (data is Map) { + contentType = Headers.formUrlEncodedContentType; + } + var res = await client.post( uri.toString(), queryParameters: search, data: data, - options: Options(responseType: ResponseType.plain), + options: Options(responseType: ResponseType.plain, contentType: contentType), ); if (res.statusCode != null && res.statusCode != 200) { diff --git a/lib/api/mw/mw_api.dart b/lib/api/mw/mw_api.dart index 588287c..e97411f 100755 --- a/lib/api/mw/mw_api.dart +++ b/lib/api/mw/mw_api.dart @@ -115,26 +115,25 @@ class MWApi { static Future>> post(String action, {Map? params, String? withToken}) async { - Map paramsStr = - params?.map((key, value) => MapEntry(key, value.toString())) ?? {}; - paramsStr.addAll({ + params ??= {}; + params.addAll({ "action": action, "format": "json", - "formatversion": "2", + "formatversion": 2, "uselang": Global.wikiLang, }); if (Global.webOrigin != null) { - paramsStr["origin"] = Global.webOrigin!; + params["origin"] = Global.webOrigin!; } var resText = ""; try { if (withToken != null) { // 获取CSRF Token - paramsStr["token"] = await getToken(type: withToken); + params["token"] = await getToken(type: withToken); } - resText = await BaseApi.post(apiBaseUri, data: paramsStr); + resText = await BaseApi.post(apiBaseUri, data: params); } on DioError catch (err) { if (err.type == DioErrorType.response) { if (err.response != null) { diff --git a/lib/api/response/page_info.dart b/lib/api/response/page_info.dart index 5e3e0a1..5f35539 100644 --- a/lib/api/response/page_info.dart +++ b/lib/api/response/page_info.dart @@ -1,6 +1,7 @@ // ignore_for_file: invalid_annotation_target import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:isekai_wiki/utils/utils.dart'; part 'page_info.freezed.dart'; part 'page_info.g.dart'; @@ -20,6 +21,8 @@ class PageInfo { String? pagelanguage; String? pagelanguagehtmlcode; String? pagelanguagedir; + + @JsonKey(fromJson: _inwatchlistFromInt, toJson: _inwatchlistToInt) bool? inwatchlist; @JsonKey(name: "touched") @@ -62,6 +65,22 @@ class PageInfo { factory PageInfo.fromJson(Map json) => _$PageInfoFromJson(json); Map toJson() => _$PageInfoToJson(this); + + static bool? _inwatchlistFromInt(int? val) { + if (val != null) { + return val == 1; + } else { + return null; + } + } + + static int? _inwatchlistToInt(bool? val) { + if (val != null) { + return val ? 1 : 0; + } else { + return null; + } + } } @freezed diff --git a/lib/components/page_card.dart b/lib/components/page_card.dart index e494dc7..ba6a32b 100755 --- a/lib/components/page_card.dart +++ b/lib/components/page_card.dart @@ -18,7 +18,7 @@ typedef AddFavoriteCallback = Future Function( typedef PageInfoCallback = Future Function(PageInfo pageInfo); class PageCardStyles { - static const double cardInnerHeight = 140; + static const double cardInnerHeight = 150; static const cardInnerPadding = EdgeInsets.only(top: 16, left: 20, right: 20, bottom: 12); static const double footerButtonSize = 30; static const double footerButtonInnerSize = 26; @@ -163,13 +163,15 @@ class _PageCardState extends ReactiveState { Expanded( flex: 1, child: c.isLoading.value - ? SkeletonParagraph( - style: SkeletonParagraphStyle( - lines: 3, - padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 0), - lineStyle: SkeletonLineStyle( - randomLength: true, - height: Styles.pageCardDescription.fontSize! * textScale), + ? ClipRect( + child: SkeletonParagraph( + style: SkeletonParagraphStyle( + lines: 3, + padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 0), + lineStyle: SkeletonLineStyle( + randomLength: true, + height: Styles.pageCardDescription.fontSize! * textScale), + ), ), ) : Text(c.pageInfo.value?.description ?? "没有简介", diff --git a/lib/components/recent_page_list.dart b/lib/components/recent_page_list.dart index 3345487..24be18d 100755 --- a/lib/components/recent_page_list.dart +++ b/lib/components/recent_page_list.dart @@ -28,8 +28,7 @@ class RecentPageListController extends GetxController { @override void onInit() { scrollController?.addListener(() { - if (scrollController!.position.pixels > - scrollController!.position.maxScrollExtent - 10) { + if (scrollController!.position.pixels > scrollController!.position.maxScrollExtent - 10) { // 滚动到底部 if (hasNextPage.value && !isLoading.value) { loadNextPages(); @@ -73,8 +72,7 @@ class RecentPageListController extends GetxController { try { 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 pageListRes = await MWApiList.getPageInfoList( @@ -95,7 +93,7 @@ class RecentPageListController extends GetxController { } hasNextPage.value = rcListRes.continueInfo != null; continueInfo.value = rcListRes.continueInfo ?? {}; - } catch (err) { + } catch (err, stack) { hasNextPage.value = false; if (shouldRefresh) { pageList.clear(); @@ -128,14 +126,13 @@ class RecentPageList extends StatelessWidget { return Column( key: key, 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(int index, PageInfo pageInfo, - RecentPageListController c, FavoriteListController fc) { + Widget _buildPageCard( + int index, PageInfo pageInfo, RecentPageListController c, FavoriteListController fc) { return PageCard( key: ValueKey("rpl-card-$index"), pageInfo: c.pageList[index], @@ -153,11 +150,13 @@ class RecentPageList extends StatelessWidget { (context, index) { if (index == 0) { // 第一页数据,直接加载 - if (c.pageList.isEmpty) { - return _buildSkeletonList(); - } else { - return _buildPageCard(index, c.pageList[index], c, flc); - } + return Obx(() { + if (c.pageList.isEmpty) { + return _buildSkeletonList(); + } else { + return _buildPageCard(index, c.pageList[index], c, flc); + } + }); } // 后续页数据 @@ -168,14 +167,16 @@ class RecentPageList extends StatelessWidget { return Padding( padding: const EdgeInsets.symmetric(vertical: 12), child: Center( - child: c.isLoading.value - ? const CupertinoActivityIndicator( - radius: 14, - ) - : const SizedBox( - width: 28, - height: 28, - ), + child: Obx( + () => c.isLoading.value + ? const CupertinoActivityIndicator( + radius: 14, + ) + : const SizedBox( + width: 28, + height: 28, + ), + ), ), ); } diff --git a/lib/models/user.dart b/lib/models/user.dart index e6b5d2d..a1c4e88 100644 --- a/lib/models/user.dart +++ b/lib/models/user.dart @@ -235,17 +235,19 @@ class UserController extends GetxController { } Future logout({bool logoutRemote = true}) async { - authProcessing.value = true; - - try { - await MWApiUser.logout(); - } catch (err, stack) { - alert(Get.overlayContext!, ErrorUtils.getErrorMessage(err), title: "错误"); - if (kDebugMode) { - print("Exception in logout: $err"); - stack.printError(); + if (logoutRemote) { + authProcessing.value = true; + + try { + await MWApiUser.logout(); + } catch (err, stack) { + authProcessing.value = false; + alert(Get.overlayContext!, ErrorUtils.getErrorMessage(err), title: "错误"); + if (kDebugMode) { + print("Exception in logout: $err"); + stack.printError(); + } } - return; } // 清除Cookie @@ -255,9 +257,9 @@ class UserController extends GetxController { userId.value = 0; userName.value = ""; nickName.value = ""; - avatarUrlSet.clear(); + avatarUrlSet.value = {}; - authProcessing.value = true; + authProcessing.value = false; saveToStorage(); } diff --git a/lib/pages/home.dart b/lib/pages/home.dart index 03994d6..45467ea 100755 --- a/lib/pages/home.dart +++ b/lib/pages/home.dart @@ -16,8 +16,7 @@ import '../styles.dart'; enum HomeTabs { newest, followed } -class HomeController extends GetxController - with GetSingleTickerProviderStateMixin { +class HomeController extends GetxController with GetSingleTickerProviderStateMixin { double _navSearchButtonOffset = 90; var showNavSearchButton = false.obs; @@ -36,15 +35,12 @@ class HomeController extends GetxController void onInit() { tabController = TabController(length: 2, vsync: this); - _navSearchButtonOffset = - 48 * MediaQuery.of(Get.context!).textScaleFactor + 48; + _navSearchButtonOffset = 48 * MediaQuery.of(Get.context!).textScaleFactor + 48; scrollController.addListener(() { - if (scrollController.offset >= _navSearchButtonOffset && - !showNavSearchButton.value) { + if (scrollController.offset >= _navSearchButtonOffset && !showNavSearchButton.value) { showNavSearchButton.value = true; - } else if (scrollController.offset < _navSearchButtonOffset && - showNavSearchButton.value) { + } else if (scrollController.offset < _navSearchButtonOffset && showNavSearchButton.value) { showNavSearchButton.value = false; } }); @@ -102,11 +98,8 @@ class HomeTab extends StatelessWidget { duration: const Duration(milliseconds: 100), child: CupertinoButton( padding: EdgeInsets.zero, - child: const Icon(CupertinoIcons.search, - size: 26, color: Styles.themeNavTitleColor), - onPressed: () { - onSearchClick?.call(); - }, + child: const Icon(CupertinoIcons.bell, size: 26, color: Styles.themeNavTitleColor), + onPressed: () {}, ), ), ); @@ -121,8 +114,7 @@ class HomeTab extends StatelessWidget { duration: const Duration(milliseconds: 100), child: CupertinoButton( padding: EdgeInsets.zero, - child: const Icon(CupertinoIcons.search, - size: 26, color: Styles.themeNavTitleColor), + child: const Icon(CupertinoIcons.search, size: 26, color: Styles.themeNavTitleColor), onPressed: () { onSearchClick?.call(); }, @@ -144,13 +136,12 @@ class HomeTab extends StatelessWidget { ), slivers: [ IsekaiSliverNavigationBar( - leading: _buildNotificationIconButton(), + leading: _buildSearchIconButton(), backgroundColor: Styles.themeMainColor, brightness: Brightness.dark, - largeTitle: const Text('首页', - style: TextStyle(color: Styles.themeNavTitleColor)), + largeTitle: const Text('首页', style: TextStyle(color: Styles.themeNavTitleColor)), border: Border.all(style: BorderStyle.none), - trailing: _buildSearchIconButton(), + trailing: _buildNotificationIconButton(), ), SliverPersistentHeader( delegate: _SliverAppBarDelegate( @@ -181,12 +172,10 @@ class HomeTab extends StatelessWidget { children: [ Container( padding: const EdgeInsets.all(1), - child: const Icon(CupertinoIcons.search, - color: Colors.black54), + child: const Icon(CupertinoIcons.search, color: Colors.black54), ), const Text("搜索页面...", - textAlign: TextAlign.center, - style: TextStyle(color: Colors.black54)) + textAlign: TextAlign.center, style: TextStyle(color: Colors.black54)) ], ), ), @@ -216,10 +205,7 @@ class HomeTab extends StatelessWidget { indicatorColor: Styles.themeMainColor, labelColor: Styles.themeMainColor, unselectedLabelColor: Colors.black45, - tabs: const [ - CollapsedTabText('最新'), - CollapsedTabText('关注') - ], + tabs: const [CollapsedTabText('最新'), CollapsedTabText('关注')], onTap: (int selected) {}, ), ), @@ -264,8 +250,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate { double get maxExtent => max(maxHeight, minHeight); @override - Widget build( - BuildContext context, double shrinkOffset, bool overlapsContent) { + Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) { return SizedBox.expand(child: child); } diff --git a/lib/styles.dart b/lib/styles.dart index 5491042..e1315c7 100755 --- a/lib/styles.dart +++ b/lib/styles.dart @@ -39,7 +39,7 @@ abstract class Styles { static const TextStyle pageCardDescription = TextStyle( color: Colors.black54, - fontSize: 16, + fontSize: 14, fontStyle: FontStyle.normal, fontWeight: FontWeight.normal, ); diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/lib/utils/utils.dart @@ -0,0 +1 @@ +