diff --git a/lib/components/loading.dart b/lib/components/loading.dart index 991b425..a8deca3 100644 --- a/lib/components/loading.dart +++ b/lib/components/loading.dart @@ -89,8 +89,18 @@ abstract class MultiPageLoadingState }); } - @override - void initState() { + void reset() { + setState(() { + _isFirstLoading = true; + _isLoading = false; + _data = null; + _error = null; + _page = 1; + }); + firstLoad(); + } + + void firstLoad() { loadData(_page).then((value) { if(value.success) { _page++; @@ -105,6 +115,11 @@ abstract class MultiPageLoadingState }); } }); + } + + @override + void initState() { + firstLoad(); super.initState(); } diff --git a/lib/network/models.dart b/lib/network/models.dart index d36adda..4d4ab3f 100644 --- a/lib/network/models.dart +++ b/lib/network/models.dart @@ -340,5 +340,5 @@ class UserPreview { account = json['account'], avatar = json['profile_image_urls']['medium'], isFollowed = json['is_followed'], - isBlocking = json['is_access_blocking_user']; + isBlocking = json['is_access_blocking_user'] ?? false; } diff --git a/lib/network/network.dart b/lib/network/network.dart index 6d5625e..4ebbb99 100644 --- a/lib/network/network.dart +++ b/lib/network/network.dart @@ -324,4 +324,16 @@ class Network { return Res.error(res.errorMessage); } } + + Future>> getFollowing(String uid, String type, [String? nextUrl]) async { + var path = nextUrl ?? "/v1/user/following?filter=for_android&user_id=$uid&restrict=$type"; + var res = await apiGet(path); + if (res.success) { + return Res( + (res.data["user_previews"] as List).map((e) => UserPreview.fromJson(e["user"])).toList(), + subData: res.data["next_url"]); + } else { + return Res.error(res.errorMessage); + } + } } diff --git a/lib/pages/following_users_page.dart b/lib/pages/following_users_page.dart new file mode 100644 index 0000000..e8d2546 --- /dev/null +++ b/lib/pages/following_users_page.dart @@ -0,0 +1,82 @@ +import 'package:fluent_ui/fluent_ui.dart'; +import 'package:pixes/appdata.dart'; +import 'package:pixes/components/loading.dart'; +import 'package:pixes/components/segmented_button.dart'; +import 'package:pixes/foundation/app.dart'; +import 'package:pixes/network/network.dart'; +import 'package:pixes/utils/translation.dart'; + +import '../components/grid.dart'; +import '../components/user_preview.dart'; + +class FollowingUsersPage extends StatefulWidget { + const FollowingUsersPage(this.uid, {super.key}); + + final String uid; + + @override + State createState() => _FollowingUsersPageState(); +} + +class _FollowingUsersPageState extends MultiPageLoadingState { + String type = "public"; + + @override + Widget buildContent(BuildContext context, final List data) { + return CustomScrollView( + slivers: [ + SliverToBoxAdapter( + child: Row( + children: [ + Text("Following".tl, + style: const TextStyle(fontSize: 22, fontWeight: FontWeight.bold),) + .paddingVertical(12).paddingLeft(16), + const Spacer(), + if(widget.uid == appdata.account?.user.id) + SegmentedButton( + value: type, + options: [ + SegmentedButtonOption("public", "Public".tl), + SegmentedButtonOption("private", "Private".tl), + ], + onPressed: (s) { + type = s; + reset(); + }, + ), + const SizedBox(width: 16,) + ], + ), + ), + SliverGridViewWithFixedItemHeight( + delegate: SliverChildBuilderDelegate( + (context, index) { + if(index == data.length - 1){ + nextPage(); + } + return UserPreviewWidget(data[index]); + }, + childCount: data.length + ), + maxCrossAxisExtent: 520, + itemHeight: 114, + ).sliverPaddingHorizontal(8) + ], + ); + } + + String? nextUrl; + + @override + Future>> loadData(page) async{ + if(nextUrl == "end") { + return Res.error("No more data"); + } + var res = await Network().getFollowing(widget.uid, type, nextUrl); + if(!res.error) { + nextUrl = res.subData; + nextUrl ??= "end"; + } + return res; + } +} diff --git a/lib/pages/user_info_page.dart b/lib/pages/user_info_page.dart index aa943c3..abe6350 100644 --- a/lib/pages/user_info_page.dart +++ b/lib/pages/user_info_page.dart @@ -1,10 +1,12 @@ import 'package:fluent_ui/fluent_ui.dart'; +import 'package:flutter/gestures.dart'; import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; import 'package:pixes/components/loading.dart'; import 'package:pixes/components/md.dart'; import 'package:pixes/foundation/app.dart'; import 'package:pixes/foundation/image_provider.dart'; import 'package:pixes/network/network.dart'; +import 'package:pixes/pages/following_users_page.dart'; import 'package:pixes/utils/translation.dart'; import 'package:url_launcher/url_launcher_string.dart'; @@ -53,13 +55,18 @@ class _UserInfoPageState extends LoadingState { ), ),), const SizedBox(height: 8), - Text(data!.name, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w500)), + Text(data!.name, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), const SizedBox(height: 4), Text.rich( TextSpan( children: [ TextSpan(text: 'Follows: '.tl), - TextSpan(text: '${data!.totalFollowUsers}', style: const TextStyle(fontWeight: FontWeight.w500)), + TextSpan( + text: '${data!.totalFollowUsers}', + recognizer: TapGestureRecognizer() + ..onTap = (() => context.to(() => FollowingUsersPage(widget.id))), + style: TextStyle(fontWeight: FontWeight.bold, color: FluentTheme.of(context).accentColor) + ), ], ), style: const TextStyle(fontSize: 14),