mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
handle relation url
Closes #1566 Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
34
lib/common/widgets/button/more_btn.dart
Normal file
34
lib/common/widgets/button/more_btn.dart
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
Widget moreTextButton({
|
||||||
|
String text = '查看更多',
|
||||||
|
required VoidCallback onTap,
|
||||||
|
EdgeInsets? padding,
|
||||||
|
Color? color,
|
||||||
|
}) {
|
||||||
|
Widget child = Text.rich(
|
||||||
|
style: TextStyle(color: color, height: 1),
|
||||||
|
strutStyle: const StrutStyle(leading: 0, height: 1),
|
||||||
|
TextSpan(
|
||||||
|
children: [
|
||||||
|
TextSpan(text: text),
|
||||||
|
WidgetSpan(
|
||||||
|
alignment: PlaceholderAlignment.middle,
|
||||||
|
child: Icon(
|
||||||
|
size: 22,
|
||||||
|
color: color,
|
||||||
|
Icons.keyboard_arrow_right,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (padding != null) {
|
||||||
|
child = Padding(padding: padding, child: child);
|
||||||
|
}
|
||||||
|
return GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
onTap: onTap,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -966,4 +966,8 @@ class Api {
|
|||||||
static const String danmakuRecall = '/x/dm/recall';
|
static const String danmakuRecall = '/x/dm/recall';
|
||||||
|
|
||||||
static const String danmakuEditState = '/x/v2/dm/edit/state';
|
static const String danmakuEditState = '/x/v2/dm/edit/state';
|
||||||
|
|
||||||
|
static const String followedUp = '/x/relation/followings/followed_upper';
|
||||||
|
|
||||||
|
static const String sameFollowing = '/x/relation/same/followings';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import 'package:PiliPlus/http/api.dart';
|
import 'package:PiliPlus/http/api.dart';
|
||||||
import 'package:PiliPlus/http/init.dart';
|
import 'package:PiliPlus/http/init.dart';
|
||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
import 'package:PiliPlus/models_new/fans/data.dart';
|
import 'package:PiliPlus/models_new/follow/data.dart';
|
||||||
|
|
||||||
class FanHttp {
|
class FanHttp {
|
||||||
static Future<LoadingState<FansData>> fans({
|
static Future<LoadingState<FollowData>> fans({
|
||||||
int? vmid,
|
int? vmid,
|
||||||
int? pn,
|
int? pn,
|
||||||
int ps = 20,
|
int ps = 20,
|
||||||
@@ -21,7 +21,7 @@ class FanHttp {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
return Success(FansData.fromJson(res.data['data']));
|
return Success(FollowData.fromJson(res.data['data']));
|
||||||
} else {
|
} else {
|
||||||
return Error(res.data['message']);
|
return Error(res.data['message']);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -328,7 +328,9 @@ class MemberHttp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future memberCardInfo({int? mid}) async {
|
static Future<LoadingState<MemberCardInfoData>> memberCardInfo({
|
||||||
|
int? mid,
|
||||||
|
}) async {
|
||||||
var res = await Request().get(
|
var res = await Request().get(
|
||||||
Api.memberCardInfo,
|
Api.memberCardInfo,
|
||||||
queryParameters: {
|
queryParameters: {
|
||||||
@@ -337,12 +339,9 @@ class MemberHttp {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
return {
|
return Success(MemberCardInfoData.fromJson(res.data['data']));
|
||||||
'status': true,
|
|
||||||
'data': MemberCardInfoData.fromJson(res.data['data']),
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
return {'status': false, 'msg': res.data['message']};
|
return Error(res.data['message']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import 'package:PiliPlus/http/loading_state.dart';
|
|||||||
import 'package:PiliPlus/models/user/info.dart';
|
import 'package:PiliPlus/models/user/info.dart';
|
||||||
import 'package:PiliPlus/models/user/stat.dart';
|
import 'package:PiliPlus/models/user/stat.dart';
|
||||||
import 'package:PiliPlus/models_new/coin_log/data.dart';
|
import 'package:PiliPlus/models_new/coin_log/data.dart';
|
||||||
|
import 'package:PiliPlus/models_new/follow/data.dart';
|
||||||
import 'package:PiliPlus/models_new/history/data.dart';
|
import 'package:PiliPlus/models_new/history/data.dart';
|
||||||
import 'package:PiliPlus/models_new/later/data.dart';
|
import 'package:PiliPlus/models_new/later/data.dart';
|
||||||
import 'package:PiliPlus/models_new/login_log/data.dart';
|
import 'package:PiliPlus/models_new/login_log/data.dart';
|
||||||
@@ -496,4 +497,48 @@ class UserHttp {
|
|||||||
return Error(res.data['message']);
|
return Error(res.data['message']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<LoadingState<FollowData>> followedUp({
|
||||||
|
required Object mid,
|
||||||
|
required int pn,
|
||||||
|
}) async {
|
||||||
|
final res = await Request().get(
|
||||||
|
Api.followedUp,
|
||||||
|
queryParameters: {
|
||||||
|
'csrf': Accounts.main.csrf,
|
||||||
|
'pn': pn,
|
||||||
|
'vmid': mid,
|
||||||
|
'web_location': 333.789,
|
||||||
|
'x-bili-device-req-json':
|
||||||
|
'{"platform":"web","device":"pc","spmid":"333.789"}',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return Success(FollowData.fromJson(res.data['data']));
|
||||||
|
} else {
|
||||||
|
return Error(res.data['message']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<LoadingState<FollowData>> sameFollowing({
|
||||||
|
required Object mid,
|
||||||
|
int? pn,
|
||||||
|
}) async {
|
||||||
|
final res = await Request().get(
|
||||||
|
Api.sameFollowing,
|
||||||
|
queryParameters: {
|
||||||
|
'csrf': Accounts.main.csrf,
|
||||||
|
'pn': ?pn,
|
||||||
|
'vmid': mid,
|
||||||
|
'web_location': 333.789,
|
||||||
|
'x-bili-device-req-json':
|
||||||
|
'{"platform":"web","device":"pc","spmid":"333.789"}',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return Success(FollowData.fromJson(res.data['data']));
|
||||||
|
} else {
|
||||||
|
return Error(res.data['message']);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
import 'package:PiliPlus/models_new/fans/list.dart';
|
|
||||||
|
|
||||||
class FansData {
|
|
||||||
List<FansItemModel>? list;
|
|
||||||
String? offset;
|
|
||||||
int? reVersion;
|
|
||||||
int? total;
|
|
||||||
|
|
||||||
FansData({this.list, this.offset, this.reVersion, this.total});
|
|
||||||
|
|
||||||
factory FansData.fromJson(Map<String, dynamic> json) => FansData(
|
|
||||||
list: (json['list'] as List<dynamic>?)
|
|
||||||
?.map((e) => FansItemModel.fromJson(e as Map<String, dynamic>))
|
|
||||||
.toList(),
|
|
||||||
offset: json['offset'] as String?,
|
|
||||||
reVersion: json['re_version'] as int?,
|
|
||||||
total: json['total'] as int?,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
import 'package:PiliPlus/models/model_avatar.dart';
|
|
||||||
|
|
||||||
class FansItemModel {
|
|
||||||
int? mid;
|
|
||||||
int? attribute;
|
|
||||||
int? mtime;
|
|
||||||
dynamic tag;
|
|
||||||
int? special;
|
|
||||||
String? uname;
|
|
||||||
String? face;
|
|
||||||
String? sign;
|
|
||||||
int? faceNft;
|
|
||||||
BaseOfficialVerify? officialVerify;
|
|
||||||
Vip? vip;
|
|
||||||
String? nftIcon;
|
|
||||||
String? recReason;
|
|
||||||
String? trackId;
|
|
||||||
String? followTime;
|
|
||||||
|
|
||||||
FansItemModel({
|
|
||||||
this.mid,
|
|
||||||
this.attribute,
|
|
||||||
this.mtime,
|
|
||||||
this.tag,
|
|
||||||
this.special,
|
|
||||||
this.uname,
|
|
||||||
this.face,
|
|
||||||
this.sign,
|
|
||||||
this.faceNft,
|
|
||||||
this.officialVerify,
|
|
||||||
this.vip,
|
|
||||||
this.nftIcon,
|
|
||||||
this.recReason,
|
|
||||||
this.trackId,
|
|
||||||
this.followTime,
|
|
||||||
});
|
|
||||||
|
|
||||||
factory FansItemModel.fromJson(Map<String, dynamic> json) => FansItemModel(
|
|
||||||
mid: json['mid'] as int?,
|
|
||||||
attribute: json['attribute'] as int?,
|
|
||||||
mtime: json['mtime'] as int?,
|
|
||||||
tag: json['tag'] as dynamic,
|
|
||||||
special: json['special'] as int?,
|
|
||||||
uname: json['uname'] as String?,
|
|
||||||
face: json['face'] as String?,
|
|
||||||
sign: json['sign'] as String?,
|
|
||||||
faceNft: json['face_nft'] as int?,
|
|
||||||
officialVerify: json['official_verify'] == null
|
|
||||||
? null
|
|
||||||
: BaseOfficialVerify.fromJson(
|
|
||||||
json['official_verify'] as Map<String, dynamic>,
|
|
||||||
),
|
|
||||||
vip: json['vip'] == null
|
|
||||||
? null
|
|
||||||
: Vip.fromJson(json['vip'] as Map<String, dynamic>),
|
|
||||||
nftIcon: json['nft_icon'] as String?,
|
|
||||||
recReason: json['rec_reason'] as String?,
|
|
||||||
trackId: json['track_id'] as String?,
|
|
||||||
followTime: json['follow_time'] as String?,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -2,10 +2,9 @@ import 'package:PiliPlus/models_new/follow/list.dart';
|
|||||||
|
|
||||||
class FollowData {
|
class FollowData {
|
||||||
late List<FollowItemModel> list;
|
late List<FollowItemModel> list;
|
||||||
int? reVersion;
|
|
||||||
int? total;
|
int? total;
|
||||||
|
|
||||||
FollowData({required this.list, this.reVersion, this.total});
|
FollowData({required this.list, this.total});
|
||||||
|
|
||||||
factory FollowData.fromJson(Map<String, dynamic> json) => FollowData(
|
factory FollowData.fromJson(Map<String, dynamic> json) => FollowData(
|
||||||
list:
|
list:
|
||||||
@@ -13,7 +12,6 @@ class FollowData {
|
|||||||
?.map((e) => FollowItemModel.fromJson(e as Map<String, dynamic>))
|
?.map((e) => FollowItemModel.fromJson(e as Map<String, dynamic>))
|
||||||
.toList() ??
|
.toList() ??
|
||||||
<FollowItemModel>[],
|
<FollowItemModel>[],
|
||||||
reVersion: json['re_version'] as int?,
|
|
||||||
total: json['total'] as int?,
|
total: json['total'] as int?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,8 @@ class FollowItemModel extends UpItem {
|
|||||||
dynamic tag;
|
dynamic tag;
|
||||||
int? special;
|
int? special;
|
||||||
String? sign;
|
String? sign;
|
||||||
int? faceNft;
|
|
||||||
BaseOfficialVerify? officialVerify;
|
BaseOfficialVerify? officialVerify;
|
||||||
Vip? vip;
|
Vip? vip;
|
||||||
String? nftIcon;
|
|
||||||
String? recReason;
|
|
||||||
String? trackId;
|
|
||||||
String? followTime;
|
String? followTime;
|
||||||
|
|
||||||
FollowItemModel({
|
FollowItemModel({
|
||||||
@@ -24,12 +20,8 @@ class FollowItemModel extends UpItem {
|
|||||||
super.uname,
|
super.uname,
|
||||||
super.face,
|
super.face,
|
||||||
this.sign,
|
this.sign,
|
||||||
this.faceNft,
|
|
||||||
this.officialVerify,
|
this.officialVerify,
|
||||||
this.vip,
|
this.vip,
|
||||||
this.nftIcon,
|
|
||||||
this.recReason,
|
|
||||||
this.trackId,
|
|
||||||
this.followTime,
|
this.followTime,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -43,7 +35,6 @@ class FollowItemModel extends UpItem {
|
|||||||
uname: json['uname'] as String?,
|
uname: json['uname'] as String?,
|
||||||
face: json['face'] as String?,
|
face: json['face'] as String?,
|
||||||
sign: json['sign'] as String?,
|
sign: json['sign'] as String?,
|
||||||
faceNft: json['face_nft'] as int?,
|
|
||||||
officialVerify: json['official_verify'] == null
|
officialVerify: json['official_verify'] == null
|
||||||
? null
|
? null
|
||||||
: BaseOfficialVerify.fromJson(
|
: BaseOfficialVerify.fromJson(
|
||||||
@@ -52,9 +43,6 @@ class FollowItemModel extends UpItem {
|
|||||||
vip: json['vip'] == null
|
vip: json['vip'] == null
|
||||||
? null
|
? null
|
||||||
: Vip.fromJson(json['vip'] as Map<String, dynamic>),
|
: Vip.fromJson(json['vip'] as Map<String, dynamic>),
|
||||||
nftIcon: json['nft_icon'] as String?,
|
|
||||||
recReason: json['rec_reason'] as String?,
|
|
||||||
trackId: json['track_id'] as String?,
|
|
||||||
followTime: json['follow_time'] as String?,
|
followTime: json['follow_time'] as String?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,13 @@
|
|||||||
class RejectPage {
|
class RejectPage {
|
||||||
String? title;
|
String? title;
|
||||||
String? text;
|
String? text;
|
||||||
String? img;
|
String? img;
|
||||||
|
|
||||||
RejectPage({this.title, this.text, this.img});
|
RejectPage({this.title, this.text, this.img});
|
||||||
|
|
||||||
factory RejectPage.fromJson(Map<String, dynamic> json) => RejectPage(
|
factory RejectPage.fromJson(Map<String, dynamic> json) => RejectPage(
|
||||||
title: json['title'] as String?,
|
title: json['title'] as String?,
|
||||||
text: json['text'] as String?,
|
text: json['text'] as String?,
|
||||||
img: json['img'] as String?,
|
img: json['img'] as String?,
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
|
||||||
'title': title,
|
|
||||||
'text': text,
|
|
||||||
'img': img,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ class _ContactPageState extends State<ContactPage>
|
|||||||
onSelect: widget.isFromSelect ? onSelect : null,
|
onSelect: widget.isFromSelect ? onSelect : null,
|
||||||
),
|
),
|
||||||
FansPage(
|
FansPage(
|
||||||
mid: accountService.mid,
|
showName: false,
|
||||||
onSelect: widget.isFromSelect ? onSelect : null,
|
onSelect: widget.isFromSelect ? onSelect : null,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,29 +1,35 @@
|
|||||||
import 'package:PiliPlus/http/fan.dart';
|
import 'package:PiliPlus/http/fan.dart';
|
||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
import 'package:PiliPlus/http/video.dart';
|
import 'package:PiliPlus/http/video.dart';
|
||||||
import 'package:PiliPlus/models_new/fans/data.dart';
|
import 'package:PiliPlus/models_new/follow/data.dart';
|
||||||
import 'package:PiliPlus/models_new/fans/list.dart';
|
import 'package:PiliPlus/pages/follow_type/controller.dart';
|
||||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
import 'package:PiliPlus/utils/accounts.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
class FansController extends CommonListController<FansData, FansItemModel> {
|
class FansController extends FollowTypeController {
|
||||||
FansController(this.mid);
|
FansController(this.showName);
|
||||||
int total = 0;
|
final bool showName;
|
||||||
int mid;
|
late final bool isOwner;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void init() {
|
||||||
super.onInit();
|
final ownerMid = Accounts.main.mid;
|
||||||
|
final mid = Get.parameters['mid'];
|
||||||
|
this.mid = mid != null ? int.parse(mid) : ownerMid;
|
||||||
|
isOwner = ownerMid == this.mid;
|
||||||
|
if (showName && !isOwner) {
|
||||||
|
final name = Get.parameters['name'];
|
||||||
|
this.name = RxnString(name);
|
||||||
|
if (name == null) {
|
||||||
|
queryUserName();
|
||||||
|
}
|
||||||
|
}
|
||||||
queryData();
|
queryData();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<FansItemModel>? getDataList(FansData response) {
|
Future<LoadingState<FollowData>> customGetData() => FanHttp.fans(
|
||||||
return response.list;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<LoadingState<FansData>> customGetData() => FanHttp.fans(
|
|
||||||
vmid: mid,
|
vmid: mid,
|
||||||
pn: page,
|
pn: page,
|
||||||
orderType: 'attention',
|
orderType: 'attention',
|
||||||
|
|||||||
@@ -1,16 +1,9 @@
|
|||||||
import 'package:PiliPlus/common/skeleton/msg_feed_top.dart';
|
|
||||||
import 'package:PiliPlus/common/widgets/dialog/dialog.dart';
|
import 'package:PiliPlus/common/widgets/dialog/dialog.dart';
|
||||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
import 'package:PiliPlus/models_new/follow/list.dart';
|
||||||
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
|
|
||||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
|
||||||
import 'package:PiliPlus/common/widgets/view_sliver_safe_area.dart';
|
|
||||||
import 'package:PiliPlus/http/loading_state.dart';
|
|
||||||
import 'package:PiliPlus/models/common/image_type.dart';
|
|
||||||
import 'package:PiliPlus/models_new/fans/list.dart';
|
|
||||||
import 'package:PiliPlus/pages/fan/controller.dart';
|
import 'package:PiliPlus/pages/fan/controller.dart';
|
||||||
|
import 'package:PiliPlus/pages/follow_type/view.dart';
|
||||||
|
import 'package:PiliPlus/pages/follow_type/widgets/item.dart';
|
||||||
import 'package:PiliPlus/pages/share/view.dart' show UserModel;
|
import 'package:PiliPlus/pages/share/view.dart' show UserModel;
|
||||||
import 'package:PiliPlus/services/account_service.dart';
|
|
||||||
import 'package:PiliPlus/utils/grid.dart';
|
|
||||||
import 'package:PiliPlus/utils/utils.dart';
|
import 'package:PiliPlus/utils/utils.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
@@ -18,160 +11,63 @@ import 'package:get/get.dart';
|
|||||||
class FansPage extends StatefulWidget {
|
class FansPage extends StatefulWidget {
|
||||||
const FansPage({
|
const FansPage({
|
||||||
super.key,
|
super.key,
|
||||||
this.mid,
|
this.showName,
|
||||||
this.onSelect,
|
this.onSelect,
|
||||||
});
|
});
|
||||||
|
|
||||||
final int? mid;
|
final bool? showName;
|
||||||
final ValueChanged<UserModel>? onSelect;
|
final ValueChanged<UserModel>? onSelect;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<FansPage> createState() => _FansPageState();
|
State<FansPage> createState() => _FansPageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _FansPageState extends State<FansPage> {
|
class _FansPageState extends FollowTypePageState<FansPage> {
|
||||||
late int mid;
|
|
||||||
String? name;
|
|
||||||
late bool isOwner;
|
|
||||||
late FansController _fansController;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
late final FansController controller = Get.put(
|
||||||
super.initState();
|
FansController(widget.showName ?? true),
|
||||||
AccountService accountService = Get.find<AccountService>();
|
tag: Utils.generateRandomString(8),
|
||||||
late final mid = Get.parameters['mid'];
|
|
||||||
this.mid =
|
|
||||||
widget.mid ?? (mid != null ? int.parse(mid) : accountService.mid);
|
|
||||||
isOwner = this.mid == accountService.mid;
|
|
||||||
name = Get.parameters['name'] ?? accountService.name.value;
|
|
||||||
_fansController = Get.put(
|
|
||||||
FansController(this.mid),
|
|
||||||
tag: Utils.makeHeroTag(this.mid),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final theme = Theme.of(context).colorScheme;
|
|
||||||
return Scaffold(
|
|
||||||
resizeToAvoidBottomInset: false,
|
|
||||||
appBar: widget.mid != null
|
|
||||||
? null
|
|
||||||
: AppBar(title: Text(isOwner ? '我的粉丝' : '$name的粉丝')),
|
|
||||||
body: refreshIndicator(
|
|
||||||
onRefresh: _fansController.onRefresh,
|
|
||||||
child: CustomScrollView(
|
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
|
||||||
controller: _fansController.scrollController,
|
|
||||||
slivers: [
|
|
||||||
ViewSliverSafeArea(
|
|
||||||
sliver: Obx(
|
|
||||||
() => _buildBody(theme, _fansController.loadingState.value),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
late final gridDelegate = SliverGridDelegateWithMaxCrossAxisExtent(
|
|
||||||
maxCrossAxisExtent: Grid.smallCardWidth * 2,
|
|
||||||
mainAxisExtent: 66,
|
|
||||||
);
|
);
|
||||||
|
late final flag = widget.onSelect == null && controller.isOwner;
|
||||||
|
|
||||||
Widget _buildBody(
|
@override
|
||||||
ColorScheme theme,
|
PreferredSizeWidget? get appBar => widget.showName == false
|
||||||
LoadingState<List<FansItemModel>?> loadingState,
|
? null
|
||||||
) {
|
: AppBar(
|
||||||
return switch (loadingState) {
|
title: controller.isOwner
|
||||||
Loading() => SliverGrid.builder(
|
? const Text('我的粉丝')
|
||||||
gridDelegate: gridDelegate,
|
: Obx(() {
|
||||||
itemBuilder: (context, index) => const MsgFeedTopSkeleton(),
|
final name = controller.name.value;
|
||||||
itemCount: 16,
|
if (name != null) return Text('$name的粉丝');
|
||||||
),
|
return const SizedBox.shrink();
|
||||||
Success(:var response) =>
|
}),
|
||||||
response?.isNotEmpty == true
|
);
|
||||||
? SliverGrid.builder(
|
|
||||||
gridDelegate: gridDelegate,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
if (index == response.length - 1) {
|
|
||||||
_fansController.onLoadMore();
|
|
||||||
}
|
|
||||||
return _buildItem(theme, index, response[index]);
|
|
||||||
},
|
|
||||||
itemCount: response!.length,
|
|
||||||
)
|
|
||||||
: HttpError(onReload: _fansController.onReload),
|
|
||||||
Error(:var errMsg) => HttpError(
|
|
||||||
errMsg: errMsg,
|
|
||||||
onReload: _fansController.onReload,
|
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildItem(ColorScheme theme, int index, FansItemModel item) {
|
@override
|
||||||
final isSelect = widget.onSelect != null;
|
Widget buildItem(int index, FollowItemModel item) {
|
||||||
void onRemove() => showConfirmDialog(
|
void onRemove() => showConfirmDialog(
|
||||||
context: context,
|
context: context,
|
||||||
title: '确定移除 ${item.uname} ?',
|
title: '确定移除 ${item.uname} ?',
|
||||||
onConfirm: () => _fansController.onRemoveFan(index, item.mid!),
|
onConfirm: () => controller.onRemoveFan(index, item.mid),
|
||||||
);
|
);
|
||||||
|
|
||||||
final flag = !isSelect && isOwner;
|
return FollowTypeItem(
|
||||||
return SizedBox(
|
item: item,
|
||||||
height: 66,
|
onTap: () {
|
||||||
child: InkWell(
|
if (widget.onSelect != null) {
|
||||||
onTap: () {
|
widget.onSelect!(
|
||||||
if (widget.onSelect != null) {
|
UserModel(
|
||||||
widget.onSelect!(
|
mid: item.mid,
|
||||||
UserModel(
|
name: item.uname!,
|
||||||
mid: item.mid!,
|
avatar: item.face!,
|
||||||
name: item.uname!,
|
),
|
||||||
avatar: item.face!,
|
);
|
||||||
),
|
return;
|
||||||
);
|
}
|
||||||
return;
|
Get.toNamed('/member?mid=${item.mid}');
|
||||||
}
|
},
|
||||||
Get.toNamed('/member?mid=${item.mid}');
|
onLongPress: flag ? onRemove : null,
|
||||||
},
|
onSecondaryTap: flag && !Utils.isMobile ? onRemove : null,
|
||||||
onLongPress: flag ? onRemove : null,
|
|
||||||
onSecondaryTap: flag && !Utils.isMobile ? onRemove : null,
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 12,
|
|
||||||
vertical: 10,
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
spacing: 10,
|
|
||||||
children: [
|
|
||||||
NetworkImgLayer(
|
|
||||||
width: 45,
|
|
||||||
height: 45,
|
|
||||||
type: ImageType.avatar,
|
|
||||||
src: item.face,
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
spacing: 3,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
item.uname!,
|
|
||||||
style: const TextStyle(fontSize: 14),
|
|
||||||
),
|
|
||||||
if (item.sign != null)
|
|
||||||
Text(
|
|
||||||
item.sign!,
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: TextStyle(fontSize: 13, color: theme.outline),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:PiliPlus/http/follow.dart';
|
import 'package:PiliPlus/http/follow.dart';
|
||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
import 'package:PiliPlus/http/member.dart';
|
import 'package:PiliPlus/http/member.dart';
|
||||||
|
import 'package:PiliPlus/http/user.dart';
|
||||||
import 'package:PiliPlus/models/common/follow_order_type.dart';
|
import 'package:PiliPlus/models/common/follow_order_type.dart';
|
||||||
import 'package:PiliPlus/models_new/follow/data.dart';
|
import 'package:PiliPlus/models_new/follow/data.dart';
|
||||||
import 'package:PiliPlus/models_new/follow/list.dart';
|
import 'package:PiliPlus/models_new/follow/list.dart';
|
||||||
@@ -14,6 +15,11 @@ class FollowChildController
|
|||||||
final FollowController? controller;
|
final FollowController? controller;
|
||||||
final int? tagid;
|
final int? tagid;
|
||||||
final int mid;
|
final int mid;
|
||||||
|
int? total;
|
||||||
|
|
||||||
|
late final loadSameFollow = controller?.isOwner == false;
|
||||||
|
late final Rx<LoadingState<List<FollowItemModel>?>> sameState =
|
||||||
|
LoadingState<List<FollowItemModel>?>.loading().obs;
|
||||||
|
|
||||||
late final Rx<FollowOrderType> orderType = FollowOrderType.def.obs;
|
late final Rx<FollowOrderType> orderType = FollowOrderType.def.obs;
|
||||||
|
|
||||||
@@ -21,13 +27,24 @@ class FollowChildController
|
|||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
queryData();
|
queryData();
|
||||||
|
if (loadSameFollow) {
|
||||||
|
_loadSameFollow();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<FollowItemModel>? getDataList(FollowData response) {
|
List<FollowItemModel>? getDataList(FollowData response) {
|
||||||
|
total = response.total;
|
||||||
return response.list;
|
return response.list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void checkIsEnd(int length) {
|
||||||
|
if (total != null && length >= total!) {
|
||||||
|
isEnd = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool customHandleResponse(bool isRefresh, Success<FollowData> response) {
|
bool customHandleResponse(bool isRefresh, Success<FollowData> response) {
|
||||||
if (controller != null) {
|
if (controller != null) {
|
||||||
@@ -57,4 +74,11 @@ class FollowChildController
|
|||||||
orderType: orderType.value.type,
|
orderType: orderType.value.type,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _loadSameFollow() async {
|
||||||
|
final res = await UserHttp.sameFollowing(mid: mid);
|
||||||
|
if (res.isSuccess) {
|
||||||
|
sameState.value = Success(res.data.list);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:PiliPlus/common/skeleton/msg_feed_top.dart';
|
import 'package:PiliPlus/common/skeleton/msg_feed_top.dart';
|
||||||
|
import 'package:PiliPlus/common/widgets/button/more_btn.dart';
|
||||||
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
|
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
|
||||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
@@ -42,7 +43,24 @@ class _FollowChildPageState extends State<FollowChildPage>
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
super.build(context);
|
super.build(context);
|
||||||
|
final colorScheme = ColorScheme.of(context);
|
||||||
final padding = MediaQuery.viewPaddingOf(context);
|
final padding = MediaQuery.viewPaddingOf(context);
|
||||||
|
Widget sliver = Obx(
|
||||||
|
() => _buildBody(_followController.loadingState.value),
|
||||||
|
);
|
||||||
|
if (_followController.loadSameFollow) {
|
||||||
|
sliver = SliverMainAxisGroup(
|
||||||
|
slivers: [
|
||||||
|
Obx(
|
||||||
|
() => _buildSameFollowing(
|
||||||
|
colorScheme,
|
||||||
|
_followController.sameState.value,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
sliver,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
Widget child = refreshIndicator(
|
Widget child = refreshIndicator(
|
||||||
onRefresh: _followController.onRefresh,
|
onRefresh: _followController.onRefresh,
|
||||||
child: CustomScrollView(
|
child: CustomScrollView(
|
||||||
@@ -55,9 +73,7 @@ class _FollowChildPageState extends State<FollowChildPage>
|
|||||||
right: padding.right,
|
right: padding.right,
|
||||||
bottom: padding.bottom + 100,
|
bottom: padding.bottom + 100,
|
||||||
),
|
),
|
||||||
sliver: Obx(
|
sliver: sliver,
|
||||||
() => _buildBody(_followController.loadingState.value),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -122,6 +138,68 @@ class _FollowChildPageState extends State<FollowChildPage>
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildSameFollowing(
|
||||||
|
ColorScheme colorScheme,
|
||||||
|
LoadingState<List<FollowItemModel>?> state,
|
||||||
|
) {
|
||||||
|
return switch (state) {
|
||||||
|
Success(:var response) =>
|
||||||
|
response?.isNotEmpty == true
|
||||||
|
? SliverMainAxisGroup(
|
||||||
|
slivers: [
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 16,
|
||||||
|
right: 16,
|
||||||
|
bottom: 6,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'我们的共同关注',
|
||||||
|
style: TextStyle(
|
||||||
|
color: colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
moreTextButton(
|
||||||
|
onTap: () => Get.toNamed(
|
||||||
|
'/sameFollowing?mid=${_followController.mid}&name=${widget.controller?.name.value}',
|
||||||
|
),
|
||||||
|
color: colorScheme.outline,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SliverList.builder(
|
||||||
|
itemCount: response!.length,
|
||||||
|
itemBuilder: (_, index) =>
|
||||||
|
FollowItem(item: response[index]),
|
||||||
|
),
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 16,
|
||||||
|
top: 16,
|
||||||
|
bottom: 6,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
'全部关注',
|
||||||
|
style: TextStyle(
|
||||||
|
color: colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: const SliverToBoxAdapter(),
|
||||||
|
_ => const SliverToBoxAdapter(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get wantKeepAlive =>
|
bool get wantKeepAlive =>
|
||||||
widget.onSelect != null || widget.controller?.tabController != null;
|
widget.onSelect != null || widget.controller?.tabController != null;
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
import 'package:PiliPlus/http/member.dart';
|
import 'package:PiliPlus/http/member.dart';
|
||||||
import 'package:PiliPlus/models/member/tags.dart';
|
import 'package:PiliPlus/models/member/tags.dart';
|
||||||
import 'package:PiliPlus/services/account_service.dart';
|
|
||||||
import 'package:PiliPlus/utils/accounts.dart';
|
import 'package:PiliPlus/utils/accounts.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
class FollowController extends GetxController with GetTickerProviderStateMixin {
|
class FollowController extends GetxController with GetTickerProviderStateMixin {
|
||||||
late int mid;
|
late final int mid;
|
||||||
String? name;
|
late final RxnString name;
|
||||||
late bool isOwner;
|
late final bool isOwner;
|
||||||
|
|
||||||
late final Rx<LoadingState> followState = LoadingState.loading().obs;
|
late final Rx<LoadingState> followState = LoadingState.loading().obs;
|
||||||
late final RxList<MemberTagItemModel> tabs = <MemberTagItemModel>[].obs;
|
late final RxList<MemberTagItemModel> tabs = <MemberTagItemModel>[].obs;
|
||||||
@@ -19,16 +18,26 @@ class FollowController extends GetxController with GetTickerProviderStateMixin {
|
|||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
int ownerMid = Accounts.main.mid;
|
final ownerMid = Accounts.main.mid;
|
||||||
final mid = Get.parameters['mid'];
|
final mid = Get.parameters['mid'];
|
||||||
this.mid = mid != null ? int.parse(mid) : ownerMid;
|
this.mid = mid != null ? int.parse(mid) : ownerMid;
|
||||||
isOwner = ownerMid == this.mid;
|
isOwner = ownerMid == this.mid;
|
||||||
name = Get.parameters['name'] ?? Get.find<AccountService>().name.value;
|
|
||||||
if (isOwner) {
|
if (isOwner) {
|
||||||
queryFollowUpTags();
|
queryFollowUpTags();
|
||||||
|
} else {
|
||||||
|
final name = Get.parameters['name'];
|
||||||
|
this.name = RxnString(name);
|
||||||
|
if (name == null) {
|
||||||
|
_queryUserName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _queryUserName() async {
|
||||||
|
final res = await MemberHttp.memberCardInfo(mid: mid);
|
||||||
|
name.value = res.dataOrNull?.card?.name;
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> queryFollowUpTags() async {
|
Future<void> queryFollowUpTags() async {
|
||||||
var res = await MemberHttp.followUpTags();
|
var res = await MemberHttp.followUpTags();
|
||||||
if (res.isSuccess) {
|
if (res.isSuccess) {
|
||||||
|
|||||||
@@ -31,9 +31,13 @@ class _FollowPageState extends State<FollowPage> {
|
|||||||
return Scaffold(
|
return Scaffold(
|
||||||
resizeToAvoidBottomInset: false,
|
resizeToAvoidBottomInset: false,
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(
|
title: _followController.isOwner
|
||||||
_followController.isOwner ? '我的关注' : '${_followController.name}的关注',
|
? const Text('我的关注')
|
||||||
),
|
: Obx(() {
|
||||||
|
final name = _followController.name.value;
|
||||||
|
if (name != null) return Text('$name的关注');
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}),
|
||||||
actions: _followController.isOwner
|
actions: _followController.isOwner
|
||||||
? [
|
? [
|
||||||
IconButton(
|
IconButton(
|
||||||
|
|||||||
50
lib/pages/follow_type/controller.dart
Normal file
50
lib/pages/follow_type/controller.dart
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import 'package:PiliPlus/http/member.dart';
|
||||||
|
import 'package:PiliPlus/models_new/follow/data.dart';
|
||||||
|
import 'package:PiliPlus/models_new/follow/list.dart';
|
||||||
|
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||||
|
import 'package:PiliPlus/utils/accounts.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
abstract class FollowTypeController
|
||||||
|
extends CommonListController<FollowData, FollowItemModel> {
|
||||||
|
late final int mid;
|
||||||
|
late final RxnString name;
|
||||||
|
|
||||||
|
RxInt total = 0.obs;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
super.onInit();
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void init() {
|
||||||
|
final ownerMid = Accounts.main.mid;
|
||||||
|
final mid = Get.parameters['mid'];
|
||||||
|
this.mid = mid != null ? int.parse(mid) : ownerMid;
|
||||||
|
final name = Get.parameters['name'];
|
||||||
|
this.name = RxnString(name);
|
||||||
|
if (name == null) {
|
||||||
|
queryUserName();
|
||||||
|
}
|
||||||
|
queryData();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> queryUserName() async {
|
||||||
|
final res = await MemberHttp.memberCardInfo(mid: mid);
|
||||||
|
name.value = res.dataOrNull?.card?.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<FollowItemModel>? getDataList(FollowData response) {
|
||||||
|
total.value = response.total ?? 0;
|
||||||
|
return response.list;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void checkIsEnd(int length) {
|
||||||
|
if (length >= total.value) {
|
||||||
|
isEnd = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
lib/pages/follow_type/follow_same/controller.dart
Normal file
10
lib/pages/follow_type/follow_same/controller.dart
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
|
import 'package:PiliPlus/http/user.dart';
|
||||||
|
import 'package:PiliPlus/models_new/follow/data.dart';
|
||||||
|
import 'package:PiliPlus/pages/follow_type/controller.dart';
|
||||||
|
|
||||||
|
class FollowSameController extends FollowTypeController {
|
||||||
|
@override
|
||||||
|
Future<LoadingState<FollowData>> customGetData() =>
|
||||||
|
UserHttp.sameFollowing(mid: mid, pn: page);
|
||||||
|
}
|
||||||
30
lib/pages/follow_type/follow_same/view.dart
Normal file
30
lib/pages/follow_type/follow_same/view.dart
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import 'package:PiliPlus/pages/follow_type/follow_same/controller.dart';
|
||||||
|
import 'package:PiliPlus/pages/follow_type/view.dart';
|
||||||
|
import 'package:PiliPlus/utils/utils.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
class FollowSamePage extends StatefulWidget {
|
||||||
|
const FollowSamePage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<FollowSamePage> createState() => _FollowSamePageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FollowSamePageState extends FollowTypePageState<FollowSamePage> {
|
||||||
|
@override
|
||||||
|
final controller = Get.put(
|
||||||
|
FollowSameController(),
|
||||||
|
tag: Utils.generateRandomString(8),
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
PreferredSizeWidget get appBar => AppBar(
|
||||||
|
title: Obx(
|
||||||
|
() {
|
||||||
|
final name = controller.name.value;
|
||||||
|
return Text('${name == null ? '' : '我与$name的'}共同关注');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
10
lib/pages/follow_type/followed/controller.dart
Normal file
10
lib/pages/follow_type/followed/controller.dart
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
|
import 'package:PiliPlus/http/user.dart';
|
||||||
|
import 'package:PiliPlus/models_new/follow/data.dart';
|
||||||
|
import 'package:PiliPlus/pages/follow_type/controller.dart';
|
||||||
|
|
||||||
|
class FollowedController extends FollowTypeController {
|
||||||
|
@override
|
||||||
|
Future<LoadingState<FollowData>> customGetData() =>
|
||||||
|
UserHttp.followedUp(mid: mid, pn: page);
|
||||||
|
}
|
||||||
29
lib/pages/follow_type/followed/view.dart
Normal file
29
lib/pages/follow_type/followed/view.dart
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import 'package:PiliPlus/pages/follow_type/followed/controller.dart';
|
||||||
|
import 'package:PiliPlus/pages/follow_type/view.dart';
|
||||||
|
import 'package:PiliPlus/utils/utils.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
class FollowedPage extends StatefulWidget {
|
||||||
|
const FollowedPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<FollowedPage> createState() => _FollowedPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FollowedPageState extends FollowTypePageState<FollowedPage> {
|
||||||
|
@override
|
||||||
|
final controller = Get.put(
|
||||||
|
FollowedController(),
|
||||||
|
tag: Utils.generateRandomString(8),
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
PreferredSizeWidget get appBar => AppBar(
|
||||||
|
title: Obx(
|
||||||
|
() => Text(
|
||||||
|
'我关注的${controller.total.value}人也关注了${controller.name.value ?? 'TA'}',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
77
lib/pages/follow_type/view.dart
Normal file
77
lib/pages/follow_type/view.dart
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import 'package:PiliPlus/common/skeleton/msg_feed_top.dart';
|
||||||
|
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
|
||||||
|
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||||
|
import 'package:PiliPlus/common/widgets/view_sliver_safe_area.dart';
|
||||||
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
|
import 'package:PiliPlus/models_new/follow/list.dart';
|
||||||
|
import 'package:PiliPlus/pages/follow/widgets/follow_item.dart';
|
||||||
|
import 'package:PiliPlus/pages/follow_type/controller.dart';
|
||||||
|
import 'package:PiliPlus/utils/grid.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
abstract class FollowTypePageState<T extends StatefulWidget> extends State<T> {
|
||||||
|
FollowTypeController get controller;
|
||||||
|
|
||||||
|
PreferredSizeWidget? get appBar;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final theme = Theme.of(context).colorScheme;
|
||||||
|
return Scaffold(
|
||||||
|
resizeToAvoidBottomInset: false,
|
||||||
|
appBar: appBar,
|
||||||
|
body: refreshIndicator(
|
||||||
|
onRefresh: controller.onRefresh,
|
||||||
|
child: CustomScrollView(
|
||||||
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
|
controller: controller.scrollController,
|
||||||
|
slivers: [
|
||||||
|
ViewSliverSafeArea(
|
||||||
|
sliver: Obx(
|
||||||
|
() => _buildBody(theme, controller.loadingState.value),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
late final gridDelegate = SliverGridDelegateWithMaxCrossAxisExtent(
|
||||||
|
maxCrossAxisExtent: Grid.smallCardWidth * 2,
|
||||||
|
mainAxisExtent: 66,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildBody(
|
||||||
|
ColorScheme theme,
|
||||||
|
LoadingState<List<FollowItemModel>?> loadingState,
|
||||||
|
) {
|
||||||
|
return switch (loadingState) {
|
||||||
|
Loading() => SliverGrid.builder(
|
||||||
|
gridDelegate: gridDelegate,
|
||||||
|
itemBuilder: (context, index) => const MsgFeedTopSkeleton(),
|
||||||
|
itemCount: 16,
|
||||||
|
),
|
||||||
|
Success(:var response) =>
|
||||||
|
response?.isNotEmpty == true
|
||||||
|
? SliverGrid.builder(
|
||||||
|
gridDelegate: gridDelegate,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
if (index == response.length - 1) {
|
||||||
|
controller.onLoadMore();
|
||||||
|
}
|
||||||
|
return buildItem(index, response[index]);
|
||||||
|
},
|
||||||
|
itemCount: response!.length,
|
||||||
|
)
|
||||||
|
: HttpError(onReload: controller.onReload),
|
||||||
|
Error(:var errMsg) => HttpError(
|
||||||
|
errMsg: errMsg,
|
||||||
|
onReload: controller.onReload,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget buildItem(int index, FollowItemModel item) => FollowItem(item: item);
|
||||||
|
}
|
||||||
71
lib/pages/follow_type/widgets/item.dart
Normal file
71
lib/pages/follow_type/widgets/item.dart
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||||
|
import 'package:PiliPlus/models/common/image_type.dart';
|
||||||
|
import 'package:PiliPlus/models_new/follow/list.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
class FollowTypeItem extends StatelessWidget {
|
||||||
|
const FollowTypeItem({
|
||||||
|
super.key,
|
||||||
|
required this.item,
|
||||||
|
this.onTap,
|
||||||
|
this.onLongPress,
|
||||||
|
this.onSecondaryTap,
|
||||||
|
});
|
||||||
|
|
||||||
|
final FollowItemModel item;
|
||||||
|
final VoidCallback? onTap;
|
||||||
|
final VoidCallback? onLongPress;
|
||||||
|
final VoidCallback? onSecondaryTap;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SizedBox(
|
||||||
|
height: 66,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: onTap ?? () => Get.toNamed('/member?mid=${item.mid}'),
|
||||||
|
onLongPress: onLongPress,
|
||||||
|
onSecondaryTap: onSecondaryTap,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 12,
|
||||||
|
vertical: 10,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
spacing: 10,
|
||||||
|
children: [
|
||||||
|
NetworkImgLayer(
|
||||||
|
width: 45,
|
||||||
|
height: 45,
|
||||||
|
type: ImageType.avatar,
|
||||||
|
src: item.face,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
spacing: 3,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
item.uname!,
|
||||||
|
style: const TextStyle(fontSize: 14),
|
||||||
|
),
|
||||||
|
if (item.sign case final sign?)
|
||||||
|
Text(
|
||||||
|
sign,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
color: ColorScheme.of(context).outline,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:PiliPlus/common/constants.dart';
|
import 'package:PiliPlus/common/constants.dart';
|
||||||
import 'package:PiliPlus/common/skeleton/video_card_v.dart';
|
import 'package:PiliPlus/common/skeleton/video_card_v.dart';
|
||||||
import 'package:PiliPlus/common/widgets/button/icon_button.dart';
|
import 'package:PiliPlus/common/widgets/button/icon_button.dart';
|
||||||
|
import 'package:PiliPlus/common/widgets/button/more_btn.dart';
|
||||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||||
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
|
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
|
||||||
import 'package:PiliPlus/common/widgets/pair.dart';
|
import 'package:PiliPlus/common/widgets/pair.dart';
|
||||||
@@ -251,25 +252,9 @@ class _LivePageState extends CommonPageState<LivePage, LiveController>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
GestureDetector(
|
moreTextButton(
|
||||||
behavior: HitTestBehavior.opaque,
|
|
||||||
onTap: () => Get.to(const LiveFollowPage()),
|
onTap: () => Get.to(const LiveFollowPage()),
|
||||||
child: Row(
|
color: theme.colorScheme.outline,
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'查看更多',
|
|
||||||
style: TextStyle(
|
|
||||||
color: theme.colorScheme.outline,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Icon(
|
|
||||||
size: 20,
|
|
||||||
Icons.keyboard_arrow_right_outlined,
|
|
||||||
color: theme.colorScheme.outline,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -652,17 +652,9 @@ class UserInfoCard extends StatelessWidget {
|
|||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
if (item.jumpUrl?.isNotEmpty == true) {
|
return GestureDetector(
|
||||||
return GestureDetector(
|
onTap: () => Get.toNamed('/followed?mid=${card.mid}&name=${card.name}'),
|
||||||
onTap: () {
|
child: child,
|
||||||
final isDark = Get.isDarkMode;
|
);
|
||||||
PageUtils.handleWebview(
|
|
||||||
'${item.jumpUrl}&native.theme=${isDark ? 2 : 1}&night=${isDark ? 1 : 0}',
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return child;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:PiliPlus/common/constants.dart';
|
import 'package:PiliPlus/common/constants.dart';
|
||||||
|
import 'package:PiliPlus/common/widgets/button/more_btn.dart';
|
||||||
import 'package:PiliPlus/common/widgets/loading_widget/loading_widget.dart';
|
import 'package:PiliPlus/common/widgets/loading_widget/loading_widget.dart';
|
||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
import 'package:PiliPlus/models_new/space/space/data.dart';
|
import 'package:PiliPlus/models_new/space/space/data.dart';
|
||||||
@@ -301,7 +302,7 @@ class _MemberHomeState extends State<MemberHome>
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
GestureDetector(
|
moreTextButton(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
int index = _ctr.tab2!.indexWhere(
|
int index = _ctr.tab2!.indexWhere(
|
||||||
(item) => item.param == param,
|
(item) => item.param == param,
|
||||||
@@ -361,25 +362,7 @@ class _MemberHomeState extends State<MemberHome>
|
|||||||
SmartDialog.showToast('view $param');
|
SmartDialog.showToast('view $param');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Text.rich(
|
color: color,
|
||||||
TextSpan(
|
|
||||||
children: [
|
|
||||||
TextSpan(
|
|
||||||
text: '查看更多',
|
|
||||||
style: TextStyle(color: color),
|
|
||||||
),
|
|
||||||
WidgetSpan(
|
|
||||||
alignment: PlaceholderAlignment.middle,
|
|
||||||
child: Icon(
|
|
||||||
Icons.arrow_forward_ios,
|
|
||||||
size: 14,
|
|
||||||
color: color,
|
|
||||||
),
|
|
||||||
style: TextStyle(fontSize: 13, color: color),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:PiliPlus/common/constants.dart';
|
import 'package:PiliPlus/common/constants.dart';
|
||||||
|
import 'package:PiliPlus/common/widgets/button/more_btn.dart';
|
||||||
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
|
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
|
||||||
import 'package:PiliPlus/common/widgets/loading_widget/loading_widget.dart';
|
import 'package:PiliPlus/common/widgets/loading_widget/loading_widget.dart';
|
||||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||||
@@ -231,8 +232,8 @@ class _PgcPageState extends CommonPageState<PgcPage, PgcController>
|
|||||||
'推荐',
|
'推荐',
|
||||||
style: theme.textTheme.titleMedium,
|
style: theme.textTheme.titleMedium,
|
||||||
),
|
),
|
||||||
GestureDetector(
|
moreTextButton(
|
||||||
behavior: HitTestBehavior.opaque,
|
padding: const EdgeInsets.symmetric(vertical: 2),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (widget.tabType == HomeTabType.bangumi) {
|
if (widget.tabType == HomeTabType.bangumi) {
|
||||||
Get.to(const PgcIndexPage());
|
Get.to(const PgcIndexPage());
|
||||||
@@ -291,26 +292,7 @@ class _PgcPageState extends CommonPageState<PgcPage, PgcController>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Padding(
|
color: theme.colorScheme.secondary,
|
||||||
padding: const EdgeInsets.symmetric(vertical: 2),
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'查看更多',
|
|
||||||
strutStyle: const StrutStyle(leading: 0, height: 1),
|
|
||||||
style: TextStyle(
|
|
||||||
height: 1,
|
|
||||||
color: theme.colorScheme.secondary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Icon(
|
|
||||||
Icons.chevron_right,
|
|
||||||
color: theme.colorScheme.secondary,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -394,34 +376,16 @@ class _PgcPageState extends CommonPageState<PgcPage, PgcController>
|
|||||||
() => controller.accountService.isLogin.value
|
() => controller.accountService.isLogin.value
|
||||||
? Padding(
|
? Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||||
child: GestureDetector(
|
child: moreTextButton(
|
||||||
behavior: HitTestBehavior.opaque,
|
text: '查看全部',
|
||||||
onTap: () => Get.toNamed(
|
onTap: () => Get.toNamed(
|
||||||
'/fav',
|
'/fav',
|
||||||
arguments: widget.tabType == HomeTabType.bangumi
|
arguments: widget.tabType == HomeTabType.bangumi
|
||||||
? FavTabType.bangumi.index
|
? FavTabType.bangumi.index
|
||||||
: FavTabType.cinema.index,
|
: FavTabType.cinema.index,
|
||||||
),
|
),
|
||||||
child: Padding(
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
color: theme.colorScheme.secondary,
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'查看全部',
|
|
||||||
strutStyle: const StrutStyle(leading: 0, height: 1),
|
|
||||||
style: TextStyle(
|
|
||||||
height: 1,
|
|
||||||
color: theme.colorScheme.secondary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Icon(
|
|
||||||
Icons.chevron_right,
|
|
||||||
color: theme.colorScheme.secondary,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: const SizedBox.shrink(),
|
: const SizedBox.shrink(),
|
||||||
|
|||||||
@@ -157,8 +157,8 @@ class UgcIntroController extends CommonIntroController with ReloadMixin {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var result = await MemberHttp.memberCardInfo(mid: mid);
|
var result = await MemberHttp.memberCardInfo(mid: mid);
|
||||||
if (result['status']) {
|
if (result.isSuccess) {
|
||||||
userStat.value = result['data'];
|
userStat.value = result.data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ import 'package:PiliPlus/pages/fav_detail/view.dart';
|
|||||||
import 'package:PiliPlus/pages/fav_search/view.dart';
|
import 'package:PiliPlus/pages/fav_search/view.dart';
|
||||||
import 'package:PiliPlus/pages/follow/view.dart';
|
import 'package:PiliPlus/pages/follow/view.dart';
|
||||||
import 'package:PiliPlus/pages/follow_search/view.dart';
|
import 'package:PiliPlus/pages/follow_search/view.dart';
|
||||||
|
import 'package:PiliPlus/pages/follow_type/follow_same/view.dart';
|
||||||
|
import 'package:PiliPlus/pages/follow_type/followed/view.dart';
|
||||||
import 'package:PiliPlus/pages/history/view.dart';
|
import 'package:PiliPlus/pages/history/view.dart';
|
||||||
import 'package:PiliPlus/pages/history_search/view.dart';
|
import 'package:PiliPlus/pages/history_search/view.dart';
|
||||||
import 'package:PiliPlus/pages/home/view.dart';
|
import 'package:PiliPlus/pages/home/view.dart';
|
||||||
@@ -223,6 +225,8 @@ class Routes {
|
|||||||
),
|
),
|
||||||
CustomGetPage(name: '/audio', page: () => const AudioPage()),
|
CustomGetPage(name: '/audio', page: () => const AudioPage()),
|
||||||
CustomGetPage(name: '/mainReply', page: () => const MainReplyPage()),
|
CustomGetPage(name: '/mainReply', page: () => const MainReplyPage()),
|
||||||
|
CustomGetPage(name: '/followed', page: () => const FollowedPage()),
|
||||||
|
CustomGetPage(name: '/sameFollowing', page: () => const FollowSamePage()),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -633,16 +633,53 @@ abstract class PiliScheme {
|
|||||||
launchURL();
|
launchURL();
|
||||||
return false;
|
return false;
|
||||||
} else if (host.contains('space.bilibili.com')) {
|
} else if (host.contains('space.bilibili.com')) {
|
||||||
String? sid =
|
void toType({
|
||||||
uri.queryParameters['sid'] ??
|
required String mid,
|
||||||
|
required String? type,
|
||||||
|
}) {
|
||||||
|
switch (type) {
|
||||||
|
case 'follow':
|
||||||
|
Get.toNamed('/follow?mid=$mid');
|
||||||
|
break;
|
||||||
|
case 'fans':
|
||||||
|
Get.toNamed('/fan?mid=$mid');
|
||||||
|
break;
|
||||||
|
case 'followed':
|
||||||
|
Get.toNamed('/followed?mid=$mid');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
PageUtils.toDupNamed('/member?mid=$mid', off: off);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
late final queryParameters = uri.queryParameters;
|
||||||
|
|
||||||
|
// space.bilibili.com/h5/follow?mid={{mid}}&type={{type}}
|
||||||
|
if (path.startsWith('/h5/follow')) {
|
||||||
|
final mid = queryParameters['mid'];
|
||||||
|
final type = queryParameters['type'];
|
||||||
|
if (mid != null) {
|
||||||
|
toType(mid: mid, type: type);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// space.bilibili.com/{{uid}}/lists/{{season_id}}
|
||||||
|
// space.bilibili.com/{{uid}}/lists?sid={{season_id}}
|
||||||
|
// space.bilibili.com/{{uid}}/channel/collectiondetail?sid={{season_id}}
|
||||||
|
final sid =
|
||||||
|
queryParameters['sid'] ??
|
||||||
RegExp(r'lists/(\d+)').firstMatch(path)?.group(1);
|
RegExp(r'lists/(\d+)').firstMatch(path)?.group(1);
|
||||||
if (sid != null) {
|
if (sid != null) {
|
||||||
SubDetailPage.toSubDetailPage(int.parse(sid));
|
SubDetailPage.toSubDetailPage(int.parse(sid));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
String? mid = uriDigitRegExp.firstMatch(path)?.group(1);
|
|
||||||
|
// space.bilibili.com/{{mid}}/relation/{{type}}
|
||||||
|
final mid = uriDigitRegExp.firstMatch(path)?.group(1);
|
||||||
|
final type = RegExp(r'relation/([a-z]+)').firstMatch(path)?.group(1);
|
||||||
if (mid != null) {
|
if (mid != null) {
|
||||||
PageUtils.toDupNamed('/member?mid=$mid', off: off);
|
toType(mid: mid, type: type);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
launchURL();
|
launchURL();
|
||||||
|
|||||||
Reference in New Issue
Block a user