mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
feat: msg link setting
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
126
lib/common/widgets/dialog/report_member.dart
Normal file
126
lib/common/widgets/dialog/report_member.dart
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
import 'package:PiliPlus/common/widgets/radio_widget.dart';
|
||||||
|
import 'package:PiliPlus/http/member.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
class MemberReportPanel extends StatefulWidget {
|
||||||
|
const MemberReportPanel({
|
||||||
|
super.key,
|
||||||
|
required this.name,
|
||||||
|
required this.mid,
|
||||||
|
});
|
||||||
|
|
||||||
|
final dynamic name;
|
||||||
|
final dynamic mid;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<MemberReportPanel> createState() => _MemberReportPanelState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MemberReportPanelState extends State<MemberReportPanel> {
|
||||||
|
final List<bool> _reasonList = List.generate(3, (_) => false).toList();
|
||||||
|
final Set<int> _reason = {};
|
||||||
|
int? _reasonV2;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
return SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'举报: ${widget.name}',
|
||||||
|
style: const TextStyle(fontSize: 18),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text('uid: ${widget.mid}'),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
const Text('举报内容(必选,可多选)'),
|
||||||
|
...List.generate(
|
||||||
|
3,
|
||||||
|
(index) => _checkBoxWidget(
|
||||||
|
_reasonList[index],
|
||||||
|
(value) {
|
||||||
|
setState(() => _reasonList[index] = value);
|
||||||
|
if (value) {
|
||||||
|
_reason.add(index + 1);
|
||||||
|
} else {
|
||||||
|
_reason.remove(index + 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
['头像违规', '昵称违规', '签名违规'][index],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Text('举报理由(单选,非必选)'),
|
||||||
|
...List.generate(
|
||||||
|
5,
|
||||||
|
(index) => RadioWidget<int>(
|
||||||
|
value: index,
|
||||||
|
groupValue: _reasonV2,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() => _reasonV2 = value);
|
||||||
|
},
|
||||||
|
title: const ['色情低俗', '不实信息', '违禁', '人身攻击', '赌博诈骗'][index],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: Get.back,
|
||||||
|
child: Text(
|
||||||
|
'取消',
|
||||||
|
style: TextStyle(color: theme.colorScheme.outline),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
if (_reason.isEmpty) {
|
||||||
|
SmartDialog.showToast('至少选择一项作为举报内容');
|
||||||
|
} else {
|
||||||
|
Get.back();
|
||||||
|
dynamic result = await MemberHttp.reportMember(
|
||||||
|
widget.mid,
|
||||||
|
reason: _reason.join(','),
|
||||||
|
reasonV2: _reasonV2 != null ? _reasonV2! + 1 : null,
|
||||||
|
);
|
||||||
|
if (result['msg'] is String && result['msg'].isNotEmpty) {
|
||||||
|
SmartDialog.showToast(result['msg']);
|
||||||
|
} else {
|
||||||
|
SmartDialog.showToast('举报失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: const Text('确定'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _checkBoxWidget(
|
||||||
|
bool defValue,
|
||||||
|
ValueChanged onChanged,
|
||||||
|
String title,
|
||||||
|
) {
|
||||||
|
return InkWell(
|
||||||
|
onTap: () => onChanged(!defValue),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Checkbox(
|
||||||
|
value: defValue,
|
||||||
|
onChanged: onChanged,
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
),
|
||||||
|
Text(title),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -834,4 +834,15 @@ class Api {
|
|||||||
|
|
||||||
static const String setMsgDnd =
|
static const String setMsgDnd =
|
||||||
'${HttpString.tUrl}/link_setting/v1/link_setting/set_msg_dnd';
|
'${HttpString.tUrl}/link_setting/v1/link_setting/set_msg_dnd';
|
||||||
|
|
||||||
|
static const String imUserInfos = '${HttpString.tUrl}/x/im/user_infos';
|
||||||
|
|
||||||
|
static const String getSessionSs =
|
||||||
|
'${HttpString.tUrl}/link_setting/v1/link_setting/get_session_ss';
|
||||||
|
|
||||||
|
static const String getMsgDnd =
|
||||||
|
'${HttpString.tUrl}/link_setting/v1/link_setting/get_msg_dnd';
|
||||||
|
|
||||||
|
static const String setPushSs =
|
||||||
|
'${HttpString.tUrl}/link_setting/v1/link_setting/set_push_ss';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,11 +6,14 @@ import 'package:PiliPlus/http/init.dart';
|
|||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
import 'package:PiliPlus/models/common/reply/reply_option_type.dart';
|
import 'package:PiliPlus/models/common/reply/reply_option_type.dart';
|
||||||
import 'package:PiliPlus/models/msg/account.dart';
|
import 'package:PiliPlus/models/msg/account.dart';
|
||||||
|
import 'package:PiliPlus/models/msg/im_user_infos/datum.dart';
|
||||||
|
import 'package:PiliPlus/models/msg/msg_dnd/uid_setting.dart';
|
||||||
import 'package:PiliPlus/models/msg/msgfeed_at_me.dart';
|
import 'package:PiliPlus/models/msg/msgfeed_at_me.dart';
|
||||||
import 'package:PiliPlus/models/msg/msgfeed_like_me.dart';
|
import 'package:PiliPlus/models/msg/msgfeed_like_me.dart';
|
||||||
import 'package:PiliPlus/models/msg/msgfeed_reply_me.dart';
|
import 'package:PiliPlus/models/msg/msgfeed_reply_me.dart';
|
||||||
import 'package:PiliPlus/models/msg/msgfeed_sys_msg.dart';
|
import 'package:PiliPlus/models/msg/msgfeed_sys_msg.dart';
|
||||||
import 'package:PiliPlus/models/msg/session.dart';
|
import 'package:PiliPlus/models/msg/session.dart';
|
||||||
|
import 'package:PiliPlus/models/msg/session_ss/data.dart';
|
||||||
import 'package:PiliPlus/utils/storage.dart';
|
import 'package:PiliPlus/utils/storage.dart';
|
||||||
import 'package:PiliPlus/utils/wbi_sign.dart';
|
import 'package:PiliPlus/utils/wbi_sign.dart';
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
@@ -607,4 +610,96 @@ class MsgHttp {
|
|||||||
return {'status': false, 'msg': res.data['message']};
|
return {'status': false, 'msg': res.data['message']};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future setPushSs({
|
||||||
|
required int setting,
|
||||||
|
required talkerUid,
|
||||||
|
}) async {
|
||||||
|
final csrf = Accounts.main.csrf;
|
||||||
|
var res = await Request().post(
|
||||||
|
Api.setPushSs,
|
||||||
|
data: {
|
||||||
|
'setting': setting,
|
||||||
|
'talker_uid': talkerUid,
|
||||||
|
'build': 0,
|
||||||
|
'mobi_app': 'web',
|
||||||
|
'csrf_token': csrf,
|
||||||
|
'csrf': csrf,
|
||||||
|
},
|
||||||
|
options: Options(contentType: Headers.formUrlEncodedContentType),
|
||||||
|
);
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return {'status': true};
|
||||||
|
} else {
|
||||||
|
return {'status': false, 'msg': res.data['message']};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<LoadingState<List<ImUserInfosData>?>> imUserInfos({
|
||||||
|
required List uids,
|
||||||
|
}) async {
|
||||||
|
final csrf = Accounts.main.csrf;
|
||||||
|
var res = await Request().get(
|
||||||
|
Api.imUserInfos,
|
||||||
|
queryParameters: {
|
||||||
|
'uids': uids.join(','),
|
||||||
|
'build': 0,
|
||||||
|
'mobi_app': 'web',
|
||||||
|
'csrf_token': csrf,
|
||||||
|
'csrf': csrf,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return Success((res.data['data'] as List?)
|
||||||
|
?.map((e) => ImUserInfosData.fromJson(e))
|
||||||
|
.toList());
|
||||||
|
} else {
|
||||||
|
return Error(res.data['message']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<LoadingState<SessionSsData>> getSessionSs({
|
||||||
|
required talkerUid,
|
||||||
|
}) async {
|
||||||
|
final csrf = Accounts.main.csrf;
|
||||||
|
var res = await Request().get(
|
||||||
|
Api.getSessionSs,
|
||||||
|
queryParameters: {
|
||||||
|
'talker_uid': talkerUid,
|
||||||
|
'build': 0,
|
||||||
|
'mobi_app': 'web',
|
||||||
|
'csrf_token': csrf,
|
||||||
|
'csrf': csrf,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return Success(SessionSsData.fromJson(res.data['data']));
|
||||||
|
} else {
|
||||||
|
return Error(res.data['message']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<LoadingState<List<UidSetting>?>> getMsgDnd({
|
||||||
|
required uidsStr,
|
||||||
|
}) async {
|
||||||
|
final csrf = Accounts.main.csrf;
|
||||||
|
var res = await Request().get(
|
||||||
|
Api.getMsgDnd,
|
||||||
|
queryParameters: {
|
||||||
|
'own_uid': Accounts.main.mid,
|
||||||
|
'uids_str': uidsStr,
|
||||||
|
'build': 0,
|
||||||
|
'mobi_app': 'web',
|
||||||
|
'csrf_token': csrf,
|
||||||
|
'csrf': csrf,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return Success((res.data['data']?['uid_settings'] as List?)
|
||||||
|
?.map((e) => UidSetting.fromJson(e))
|
||||||
|
.toList());
|
||||||
|
} else {
|
||||||
|
return Error(res.data['message']);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
78
lib/models/msg/im_user_infos/datum.dart
Normal file
78
lib/models/msg/im_user_infos/datum.dart
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import 'package:PiliPlus/models/model_avatar.dart';
|
||||||
|
|
||||||
|
class ImUserInfosData {
|
||||||
|
int? mid;
|
||||||
|
String? name;
|
||||||
|
String? sex;
|
||||||
|
String? face;
|
||||||
|
String? sign;
|
||||||
|
int? rank;
|
||||||
|
int? level;
|
||||||
|
int? silence;
|
||||||
|
Vip? vip;
|
||||||
|
Pendant? pendant;
|
||||||
|
BaseOfficialVerify? official;
|
||||||
|
int? birthday;
|
||||||
|
int? isFakeAccount;
|
||||||
|
int? isDeleted;
|
||||||
|
int? inRegAudit;
|
||||||
|
int? faceNft;
|
||||||
|
int? faceNftNew;
|
||||||
|
int? isSeniorMember;
|
||||||
|
String? digitalId;
|
||||||
|
int? digitalType;
|
||||||
|
|
||||||
|
ImUserInfosData({
|
||||||
|
this.mid,
|
||||||
|
this.name,
|
||||||
|
this.sex,
|
||||||
|
this.face,
|
||||||
|
this.sign,
|
||||||
|
this.rank,
|
||||||
|
this.level,
|
||||||
|
this.silence,
|
||||||
|
this.vip,
|
||||||
|
this.pendant,
|
||||||
|
this.official,
|
||||||
|
this.birthday,
|
||||||
|
this.isFakeAccount,
|
||||||
|
this.isDeleted,
|
||||||
|
this.inRegAudit,
|
||||||
|
this.faceNft,
|
||||||
|
this.faceNftNew,
|
||||||
|
this.isSeniorMember,
|
||||||
|
this.digitalId,
|
||||||
|
this.digitalType,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory ImUserInfosData.fromJson(Map<String, dynamic> json) =>
|
||||||
|
ImUserInfosData(
|
||||||
|
mid: json['mid'] as int?,
|
||||||
|
name: json['name'] as String?,
|
||||||
|
sex: json['sex'] as String?,
|
||||||
|
face: json['face'] as String?,
|
||||||
|
sign: json['sign'] as String?,
|
||||||
|
rank: json['rank'] as int?,
|
||||||
|
level: json['level'] as int?,
|
||||||
|
silence: json['silence'] as int?,
|
||||||
|
vip: json['vip'] == null
|
||||||
|
? null
|
||||||
|
: Vip.fromJson(json['vip'] as Map<String, dynamic>),
|
||||||
|
pendant: json['pendant'] == null
|
||||||
|
? null
|
||||||
|
: Pendant.fromJson(json['pendant'] as Map<String, dynamic>),
|
||||||
|
official: json['official'] == null
|
||||||
|
? null
|
||||||
|
: BaseOfficialVerify.fromJson(
|
||||||
|
json['official'] as Map<String, dynamic>),
|
||||||
|
birthday: json['birthday'] as int?,
|
||||||
|
isFakeAccount: json['is_fake_account'] as int?,
|
||||||
|
isDeleted: json['is_deleted'] as int?,
|
||||||
|
inRegAudit: json['in_reg_audit'] as int?,
|
||||||
|
faceNft: json['face_nft'] as int?,
|
||||||
|
faceNftNew: json['face_nft_new'] as int?,
|
||||||
|
isSeniorMember: json['is_senior_member'] as int?,
|
||||||
|
digitalId: json['digital_id'] as String?,
|
||||||
|
digitalType: json['digital_type'] as int?,
|
||||||
|
);
|
||||||
|
}
|
||||||
16
lib/models/msg/msg_dnd/uid_setting.dart
Normal file
16
lib/models/msg/msg_dnd/uid_setting.dart
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
class UidSetting {
|
||||||
|
int? id;
|
||||||
|
int? setting;
|
||||||
|
|
||||||
|
UidSetting({this.id, this.setting});
|
||||||
|
|
||||||
|
factory UidSetting.fromJson(Map<String, dynamic> json) => UidSetting(
|
||||||
|
id: json['id'] as int?,
|
||||||
|
setting: json['setting'] as int?,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
'id': id,
|
||||||
|
'setting': setting,
|
||||||
|
};
|
||||||
|
}
|
||||||
20
lib/models/msg/session_ss/data.dart
Normal file
20
lib/models/msg/session_ss/data.dart
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
class SessionSsData {
|
||||||
|
int? followStatus;
|
||||||
|
int? special;
|
||||||
|
int? pushSetting;
|
||||||
|
int? showPushSetting;
|
||||||
|
|
||||||
|
SessionSsData({
|
||||||
|
this.followStatus,
|
||||||
|
this.special,
|
||||||
|
this.pushSetting,
|
||||||
|
this.showPushSetting,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory SessionSsData.fromJson(Map<String, dynamic> json) => SessionSsData(
|
||||||
|
followStatus: json['follow_status'] as int?,
|
||||||
|
special: json['special'] as int?,
|
||||||
|
pushSetting: json['push_setting'] as int?,
|
||||||
|
showPushSetting: json['show_push_setting'] as int?,
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
|
import 'package:PiliPlus/common/widgets/dialog/report_member.dart';
|
||||||
import 'package:PiliPlus/common/widgets/dynamic_sliver_appbar.dart';
|
import 'package:PiliPlus/common/widgets/dynamic_sliver_appbar.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/radio_widget.dart';
|
|
||||||
import 'package:PiliPlus/common/widgets/scroll_physics.dart';
|
import 'package:PiliPlus/common/widgets/scroll_physics.dart';
|
||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
import 'package:PiliPlus/http/member.dart';
|
|
||||||
import 'package:PiliPlus/models/space/data.dart';
|
import 'package:PiliPlus/models/space/data.dart';
|
||||||
import 'package:PiliPlus/pages/member/controller.dart';
|
import 'package:PiliPlus/pages/member/controller.dart';
|
||||||
import 'package:PiliPlus/pages/member/widget/user_info_card.dart';
|
import 'package:PiliPlus/pages/member/widget/user_info_card.dart';
|
||||||
@@ -16,7 +15,6 @@ import 'package:PiliPlus/utils/extension.dart';
|
|||||||
import 'package:PiliPlus/utils/utils.dart';
|
import 'package:PiliPlus/utils/utils.dart';
|
||||||
import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart';
|
import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
class MemberPage extends StatefulWidget {
|
class MemberPage extends StatefulWidget {
|
||||||
@@ -137,7 +135,7 @@ class _MemberPageState extends State<MemberPage> {
|
|||||||
horizontal: 20,
|
horizontal: 20,
|
||||||
vertical: 16,
|
vertical: 16,
|
||||||
),
|
),
|
||||||
content: ReportPanel(
|
content: MemberReportPanel(
|
||||||
name: _userController.username,
|
name: _userController.username,
|
||||||
mid: _mid,
|
mid: _mid,
|
||||||
),
|
),
|
||||||
@@ -290,124 +288,3 @@ class _MemberPageState extends State<MemberPage> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ReportPanel extends StatefulWidget {
|
|
||||||
const ReportPanel({
|
|
||||||
super.key,
|
|
||||||
required this.name,
|
|
||||||
required this.mid,
|
|
||||||
});
|
|
||||||
|
|
||||||
final dynamic name;
|
|
||||||
final dynamic mid;
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<ReportPanel> createState() => _ReportPanelState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ReportPanelState extends State<ReportPanel> {
|
|
||||||
final List<bool> _reasonList = List.generate(3, (_) => false).toList();
|
|
||||||
final Set<int> _reason = {};
|
|
||||||
int? _reasonV2;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final theme = Theme.of(context);
|
|
||||||
return SingleChildScrollView(
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'举报: ${widget.name}',
|
|
||||||
style: const TextStyle(fontSize: 18),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
Text('uid: ${widget.mid}'),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
const Text('举报内容(必选,可多选)'),
|
|
||||||
...List.generate(
|
|
||||||
3,
|
|
||||||
(index) => _checkBoxWidget(
|
|
||||||
_reasonList[index],
|
|
||||||
(value) {
|
|
||||||
setState(() => _reasonList[index] = value);
|
|
||||||
if (value) {
|
|
||||||
_reason.add(index + 1);
|
|
||||||
} else {
|
|
||||||
_reason.remove(index + 1);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
['头像违规', '昵称违规', '签名违规'][index],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Text('举报理由(单选,非必选)'),
|
|
||||||
...List.generate(
|
|
||||||
5,
|
|
||||||
(index) => RadioWidget<int>(
|
|
||||||
value: index,
|
|
||||||
groupValue: _reasonV2,
|
|
||||||
onChanged: (value) {
|
|
||||||
setState(() => _reasonV2 = value);
|
|
||||||
},
|
|
||||||
title: const ['色情低俗', '不实信息', '违禁', '人身攻击', '赌博诈骗'][index],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
|
||||||
children: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: Get.back,
|
|
||||||
child: Text(
|
|
||||||
'取消',
|
|
||||||
style: TextStyle(color: theme.colorScheme.outline),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () async {
|
|
||||||
if (_reason.isEmpty) {
|
|
||||||
SmartDialog.showToast('至少选择一项作为举报内容');
|
|
||||||
} else {
|
|
||||||
Get.back();
|
|
||||||
dynamic result = await MemberHttp.reportMember(
|
|
||||||
widget.mid,
|
|
||||||
reason: _reason.join(','),
|
|
||||||
reasonV2: _reasonV2 != null ? _reasonV2! + 1 : null,
|
|
||||||
);
|
|
||||||
if (result['msg'] is String && result['msg'].isNotEmpty) {
|
|
||||||
SmartDialog.showToast(result['msg']);
|
|
||||||
} else {
|
|
||||||
SmartDialog.showToast('举报失败');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: const Text('确定'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _checkBoxWidget(
|
|
||||||
bool defValue,
|
|
||||||
ValueChanged onChanged,
|
|
||||||
String title,
|
|
||||||
) {
|
|
||||||
return InkWell(
|
|
||||||
onTap: () => onChanged(!defValue),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Checkbox(
|
|
||||||
value: defValue,
|
|
||||||
onChanged: onChanged,
|
|
||||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
||||||
),
|
|
||||||
Text(title),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -343,11 +343,11 @@ class UserInfoCard extends StatelessWidget {
|
|||||||
if (GStorage.userInfo.get('userInfoCache') != null) {
|
if (GStorage.userInfo.get('userInfoCache') != null) {
|
||||||
Get.toNamed(
|
Get.toNamed(
|
||||||
'/whisperDetail',
|
'/whisperDetail',
|
||||||
parameters: {
|
arguments: {
|
||||||
'talkerId': card.mid ?? '',
|
'talkerId': int.parse(card.mid!),
|
||||||
'name': card.name ?? '',
|
'name': card.name,
|
||||||
'face': card.face ?? '',
|
'face': card.face,
|
||||||
'mid': card.mid ?? '',
|
'mid': card.mid,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,9 +39,13 @@ class _AtMePageState extends State<AtMePage> {
|
|||||||
imSettingType: IMSettingType.SETTING_TYPE_OLD_AT_ME),
|
imSettingType: IMSettingType.SETTING_TYPE_OLD_AT_ME),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
icon: const Icon(size: 22, Icons.settings),
|
icon: Icon(
|
||||||
|
size: 20,
|
||||||
|
Icons.settings,
|
||||||
|
color: theme.colorScheme.onSurfaceVariant.withOpacity(0.8),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 16),
|
const SizedBox(width: 10),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: refreshIndicator(
|
body: refreshIndicator(
|
||||||
|
|||||||
@@ -18,6 +18,14 @@ class LikeMeController extends CommonDataController<MsgFeedLikeMe, dynamic> {
|
|||||||
queryData();
|
queryData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> queryData([bool isRefresh = true]) {
|
||||||
|
if (!isRefresh && isEnd) {
|
||||||
|
return Future.value();
|
||||||
|
}
|
||||||
|
return super.queryData(isRefresh);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool customHandleResponse(bool isRefresh, Success<MsgFeedLikeMe> response) {
|
bool customHandleResponse(bool isRefresh, Success<MsgFeedLikeMe> response) {
|
||||||
MsgFeedLikeMe data = response.response;
|
MsgFeedLikeMe data = response.response;
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ class _LikeMePageState extends State<LikeMePage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final theme = Theme.of(context);
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text('收到的赞'),
|
title: const Text('收到的赞'),
|
||||||
@@ -39,9 +40,13 @@ class _LikeMePageState extends State<LikeMePage> {
|
|||||||
imSettingType: IMSettingType.SETTING_TYPE_OLD_RECEIVE_LIKE),
|
imSettingType: IMSettingType.SETTING_TYPE_OLD_RECEIVE_LIKE),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
icon: const Icon(size: 22, Icons.settings),
|
icon: Icon(
|
||||||
|
size: 20,
|
||||||
|
Icons.settings,
|
||||||
|
color: theme.colorScheme.onSurfaceVariant.withOpacity(0.8),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 16),
|
const SizedBox(width: 10),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: refreshIndicator(
|
body: refreshIndicator(
|
||||||
@@ -52,8 +57,8 @@ class _LikeMePageState extends State<LikeMePage> {
|
|||||||
SliverPadding(
|
SliverPadding(
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
bottom: MediaQuery.paddingOf(context).bottom + 80),
|
bottom: MediaQuery.paddingOf(context).bottom + 80),
|
||||||
sliver:
|
sliver: Obx(() =>
|
||||||
Obx(() => _buildBody(_likeMeController.loadingState.value)),
|
_buildBody(theme, _likeMeController.loadingState.value)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -61,7 +66,7 @@ class _LikeMePageState extends State<LikeMePage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildBody(LoadingState loadingState) {
|
Widget _buildBody(ThemeData theme, LoadingState loadingState) {
|
||||||
return switch (loadingState) {
|
return switch (loadingState) {
|
||||||
Loading() => SliverList.builder(
|
Loading() => SliverList.builder(
|
||||||
itemCount: 12,
|
itemCount: 12,
|
||||||
@@ -70,7 +75,6 @@ class _LikeMePageState extends State<LikeMePage> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
Success(:var response) => () {
|
Success(:var response) => () {
|
||||||
final theme = Theme.of(context);
|
|
||||||
Pair<List<LikeMeItems>, List<LikeMeItems>> pair = response;
|
Pair<List<LikeMeItems>, List<LikeMeItems>> pair = response;
|
||||||
List<LikeMeItems> latest = pair.first;
|
List<LikeMeItems> latest = pair.first;
|
||||||
List<LikeMeItems> total = pair.second;
|
List<LikeMeItems> total = pair.second;
|
||||||
|
|||||||
@@ -39,9 +39,13 @@ class _ReplyMePageState extends State<ReplyMePage> {
|
|||||||
imSettingType: IMSettingType.SETTING_TYPE_OLD_REPLY_ME),
|
imSettingType: IMSettingType.SETTING_TYPE_OLD_REPLY_ME),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
icon: const Icon(size: 22, Icons.settings),
|
icon: Icon(
|
||||||
|
size: 20,
|
||||||
|
Icons.settings,
|
||||||
|
color: theme.colorScheme.onSurfaceVariant.withOpacity(0.8),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 16),
|
const SizedBox(width: 10),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: refreshIndicator(
|
body: refreshIndicator(
|
||||||
|
|||||||
@@ -576,21 +576,26 @@ class VideoIntroController extends GetxController {
|
|||||||
SmartDialog.showToast('账号未登录');
|
SmartDialog.showToast('账号未登录');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
int? mid = videoDetail.value.owner?.mid;
|
||||||
|
if (mid == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
int attr = followStatus['attribute'] ?? 0;
|
int attr = followStatus['attribute'] ?? 0;
|
||||||
if (attr == 128) {
|
if (attr == 128) {
|
||||||
dynamic res = await VideoHttp.relationMod(
|
dynamic res = await VideoHttp.relationMod(
|
||||||
mid: videoDetail.value.owner?.mid ?? -1,
|
mid: mid,
|
||||||
act: 6,
|
act: 6,
|
||||||
reSrc: 11,
|
reSrc: 11,
|
||||||
);
|
);
|
||||||
if (res['status']) {
|
if (res['status']) {
|
||||||
|
GStorage.removeBlackMid(mid);
|
||||||
followStatus['attribute'] = 0;
|
followStatus['attribute'] = 0;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
RequestUtils.actionRelationMod(
|
RequestUtils.actionRelationMod(
|
||||||
context: context,
|
context: context,
|
||||||
mid: videoDetail.value.owner?.mid,
|
mid: mid,
|
||||||
isFollow: attr != 0,
|
isFollow: attr != 0,
|
||||||
followStatus: followStatus,
|
followStatus: followStatus,
|
||||||
callback: (attribute) {
|
callback: (attribute) {
|
||||||
|
|||||||
@@ -93,13 +93,13 @@ class WhisperSessionItem extends StatelessWidget {
|
|||||||
if (item.id.privateId.hasTalkerUid()) {
|
if (item.id.privateId.hasTalkerUid()) {
|
||||||
Get.toNamed(
|
Get.toNamed(
|
||||||
'/whisperDetail',
|
'/whisperDetail',
|
||||||
parameters: {
|
arguments: {
|
||||||
'talkerId': item.id.privateId.talkerUid.toString(),
|
'talkerId': item.id.privateId.talkerUid.toInt(),
|
||||||
'name': item.sessionInfo.sessionName,
|
'name': item.sessionInfo.sessionName,
|
||||||
'face': item.sessionInfo.avatar.fallbackLayers.layers.first
|
'face': item.sessionInfo.avatar.fallbackLayers.layers.first
|
||||||
.resource.resImage.imageSrc.remote.url,
|
.resource.resImage.imageSrc.remote.url,
|
||||||
if (item.sessionInfo.avatar.hasMid())
|
if (item.sessionInfo.avatar.hasMid())
|
||||||
'mid': item.sessionInfo.avatar.mid.toString(),
|
'mid': item.sessionInfo.avatar.mid.toInt(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -19,10 +19,10 @@ import 'package:get/get.dart';
|
|||||||
class WhisperDetailController extends CommonListController<RspSessionMsg, Msg> {
|
class WhisperDetailController extends CommonListController<RspSessionMsg, Msg> {
|
||||||
late final ownerMid = Accounts.main.mid;
|
late final ownerMid = Accounts.main.mid;
|
||||||
|
|
||||||
late int talkerId;
|
final int talkerId = Get.arguments['talkerId'];
|
||||||
late String name;
|
final String name = Get.arguments['name'];
|
||||||
late String face;
|
final String face = Get.arguments['face'];
|
||||||
int? mid;
|
final int? mid = Get.arguments['mid'];
|
||||||
|
|
||||||
Int64? msgSeqno;
|
Int64? msgSeqno;
|
||||||
|
|
||||||
@@ -32,14 +32,6 @@ class WhisperDetailController extends CommonListController<RspSessionMsg, Msg> {
|
|||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
|
|
||||||
talkerId = int.parse(Get.parameters['talkerId']!);
|
|
||||||
name = Get.parameters['name']!;
|
|
||||||
face = Get.parameters['face']!;
|
|
||||||
mid = Get.parameters['mid'] != null
|
|
||||||
? int.parse(Get.parameters['mid']!)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
queryData();
|
queryData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import 'package:PiliPlus/pages/common/common_publish_page.dart';
|
|||||||
import 'package:PiliPlus/pages/emote/view.dart';
|
import 'package:PiliPlus/pages/emote/view.dart';
|
||||||
import 'package:PiliPlus/pages/whisper_detail/controller.dart';
|
import 'package:PiliPlus/pages/whisper_detail/controller.dart';
|
||||||
import 'package:PiliPlus/pages/whisper_detail/widget/chat_item.dart';
|
import 'package:PiliPlus/pages/whisper_detail/widget/chat_item.dart';
|
||||||
|
import 'package:PiliPlus/pages/whisper_link_setting/view.dart';
|
||||||
import 'package:PiliPlus/utils/extension.dart';
|
import 'package:PiliPlus/utils/extension.dart';
|
||||||
import 'package:PiliPlus/utils/feed_back.dart';
|
import 'package:PiliPlus/utils/feed_back.dart';
|
||||||
import 'package:PiliPlus/utils/utils.dart';
|
import 'package:PiliPlus/utils/utils.dart';
|
||||||
@@ -95,6 +96,21 @@ class _WhisperDetailPageState
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
Get.to(WhisperLinkSettingPage(
|
||||||
|
talkerUid: _whisperDetailController.talkerId,
|
||||||
|
));
|
||||||
|
},
|
||||||
|
icon: Icon(
|
||||||
|
size: 20,
|
||||||
|
Icons.settings,
|
||||||
|
color: theme.colorScheme.onSurfaceVariant.withOpacity(0.8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
top: false,
|
top: false,
|
||||||
|
|||||||
179
lib/pages/whisper_link_setting/controller.dart
Normal file
179
lib/pages/whisper_link_setting/controller.dart
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:PiliPlus/common/widgets/dialog/dialog.dart';
|
||||||
|
import 'package:PiliPlus/common/widgets/dialog/report_member.dart';
|
||||||
|
import 'package:PiliPlus/grpc/bilibili/app/im/v1.pb.dart';
|
||||||
|
import 'package:PiliPlus/grpc/im.dart';
|
||||||
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
|
import 'package:PiliPlus/http/msg.dart';
|
||||||
|
import 'package:PiliPlus/http/video.dart';
|
||||||
|
import 'package:PiliPlus/models/msg/im_user_infos/datum.dart';
|
||||||
|
import 'package:PiliPlus/models/msg/msg_dnd/uid_setting.dart';
|
||||||
|
import 'package:PiliPlus/models/msg/session_ss/data.dart';
|
||||||
|
import 'package:PiliPlus/utils/storage.dart';
|
||||||
|
import 'package:fixnum/fixnum.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
class WhisperLinkSettingController extends GetxController {
|
||||||
|
WhisperLinkSettingController({
|
||||||
|
required this.talkerUid,
|
||||||
|
});
|
||||||
|
|
||||||
|
final int talkerUid;
|
||||||
|
RxBool isPinned = false.obs;
|
||||||
|
late final sessionId =
|
||||||
|
SessionId(privateId: PrivateId(talkerUid: Int64(talkerUid)));
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
super.onInit();
|
||||||
|
getUserInfo();
|
||||||
|
getSessionSs();
|
||||||
|
getMsgDnd();
|
||||||
|
getIsPinned();
|
||||||
|
}
|
||||||
|
|
||||||
|
final Rx<LoadingState<List<ImUserInfosData>?>> userState =
|
||||||
|
LoadingState<List<ImUserInfosData>?>.loading().obs;
|
||||||
|
final Rx<LoadingState<SessionSsData>> sessionSs =
|
||||||
|
LoadingState<SessionSsData>.loading().obs;
|
||||||
|
final Rx<LoadingState<List<UidSetting>?>> msgDnd =
|
||||||
|
LoadingState<List<UidSetting>?>.loading().obs;
|
||||||
|
|
||||||
|
Future<void> getUserInfo() async {
|
||||||
|
userState.value = await MsgHttp.imUserInfos(uids: [talkerUid]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> getSessionSs() async {
|
||||||
|
sessionSs.value = await MsgHttp.getSessionSs(talkerUid: talkerUid);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> getMsgDnd() async {
|
||||||
|
msgDnd.value = await MsgHttp.getMsgDnd(uidsStr: talkerUid);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> getIsPinned() async {
|
||||||
|
var res = await ImGrpc.sessionUpdate(sessionId: sessionId);
|
||||||
|
if (res.isSuccess) {
|
||||||
|
isPinned.value = res.data.session.isPinned;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPush(bool isPush) {
|
||||||
|
if (isPush) {
|
||||||
|
showConfirmDialog(
|
||||||
|
context: Get.context!,
|
||||||
|
title: '确认关闭内容推送吗?',
|
||||||
|
content: '若关闭此开关,你将不再收到该账号的图文消息与稿件推送,但通知类消息不受影响',
|
||||||
|
onConfirm: () => _setPush(isPush),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_setPush(isPush);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _setPush(bool isPush) async {
|
||||||
|
int setting = isPush ? 1 : 0;
|
||||||
|
var res = await MsgHttp.setPushSs(
|
||||||
|
setting: setting,
|
||||||
|
talkerUid: talkerUid,
|
||||||
|
);
|
||||||
|
if (res['status']) {
|
||||||
|
sessionSs
|
||||||
|
..value.data.pushSetting = setting
|
||||||
|
..refresh();
|
||||||
|
SmartDialog.showToast('操作成功');
|
||||||
|
} else {
|
||||||
|
SmartDialog.showToast(res['msg']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> setPin() async {
|
||||||
|
var res = isPinned.value
|
||||||
|
? await ImGrpc.unpinSession(sessionId: sessionId)
|
||||||
|
: await ImGrpc.pinSession(sessionId: sessionId);
|
||||||
|
if (res.isSuccess) {
|
||||||
|
isPinned.value = !isPinned.value;
|
||||||
|
SmartDialog.showToast('操作成功');
|
||||||
|
} else {
|
||||||
|
res.toast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> setMute(bool isMuted) async {
|
||||||
|
int setting = isMuted ? 0 : 1;
|
||||||
|
var res = await MsgHttp.setMsgDnd(
|
||||||
|
uid: Accounts.main.mid,
|
||||||
|
setting: setting,
|
||||||
|
dndUid: talkerUid,
|
||||||
|
);
|
||||||
|
if (res['status']) {
|
||||||
|
msgDnd
|
||||||
|
..value.data!.first.setting = setting
|
||||||
|
..refresh();
|
||||||
|
SmartDialog.showToast('操作成功');
|
||||||
|
} else {
|
||||||
|
SmartDialog.showToast(res['msg']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> setBlock(bool isBlocked) async {
|
||||||
|
if (isBlocked) {
|
||||||
|
var res = await VideoHttp.relationMod(
|
||||||
|
mid: talkerUid,
|
||||||
|
act: 6,
|
||||||
|
reSrc: 11,
|
||||||
|
);
|
||||||
|
if (res['status']) {
|
||||||
|
sessionSs
|
||||||
|
..value.data.followStatus = null
|
||||||
|
..refresh();
|
||||||
|
GStorage.removeBlackMid(talkerUid);
|
||||||
|
SmartDialog.showToast('操作成功');
|
||||||
|
} else {
|
||||||
|
SmartDialog.showToast(res['msg']);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showConfirmDialog(
|
||||||
|
context: Get.context!,
|
||||||
|
title: '确认拉黑该用户',
|
||||||
|
content: '加入黑名单后,将自动解除关注关系和对该用户的合集订阅关系,禁止该用户与我互动或查看我的空间',
|
||||||
|
onConfirm: () async {
|
||||||
|
var res = await VideoHttp.relationMod(
|
||||||
|
mid: talkerUid,
|
||||||
|
act: 5,
|
||||||
|
reSrc: 11,
|
||||||
|
);
|
||||||
|
if (res['status']) {
|
||||||
|
sessionSs
|
||||||
|
..value.data.followStatus = 128
|
||||||
|
..refresh();
|
||||||
|
GStorage.setBlackMid(talkerUid);
|
||||||
|
SmartDialog.showToast('操作成功');
|
||||||
|
} else {
|
||||||
|
SmartDialog.showToast(res['msg']);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void report() {
|
||||||
|
showDialog(
|
||||||
|
context: Get.context!,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
clipBehavior: Clip.hardEdge,
|
||||||
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 20,
|
||||||
|
vertical: 16,
|
||||||
|
),
|
||||||
|
content: MemberReportPanel(
|
||||||
|
name: userState.value.dataOrNull?.firstOrNull?.name ?? '',
|
||||||
|
mid: talkerUid,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
283
lib/pages/whisper_link_setting/view.dart
Normal file
283
lib/pages/whisper_link_setting/view.dart
Normal file
@@ -0,0 +1,283 @@
|
|||||||
|
import 'package:PiliPlus/common/widgets/pendant_avatar.dart';
|
||||||
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
|
import 'package:PiliPlus/models/msg/im_user_infos/datum.dart';
|
||||||
|
import 'package:PiliPlus/models/msg/msg_dnd/uid_setting.dart';
|
||||||
|
import 'package:PiliPlus/models/msg/session_ss/data.dart';
|
||||||
|
import 'package:PiliPlus/pages/whisper_link_setting/controller.dart';
|
||||||
|
import 'package:PiliPlus/utils/extension.dart';
|
||||||
|
import 'package:PiliPlus/utils/utils.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
class WhisperLinkSettingPage extends StatefulWidget {
|
||||||
|
const WhisperLinkSettingPage({
|
||||||
|
super.key,
|
||||||
|
required this.talkerUid,
|
||||||
|
});
|
||||||
|
|
||||||
|
final int talkerUid;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<WhisperLinkSettingPage> createState() => _WhisperLinkSettingPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _WhisperLinkSettingPageState extends State<WhisperLinkSettingPage> {
|
||||||
|
late final WhisperLinkSettingController _controller = Get.put(
|
||||||
|
WhisperLinkSettingController(talkerUid: widget.talkerUid),
|
||||||
|
tag: Utils.generateRandomString(8),
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final ThemeData theme = Theme.of(context);
|
||||||
|
final divider = Divider(
|
||||||
|
height: 12,
|
||||||
|
thickness: 12,
|
||||||
|
color: theme.colorScheme.outline.withOpacity(0.1),
|
||||||
|
);
|
||||||
|
final divider2 = Divider(
|
||||||
|
height: 1,
|
||||||
|
indent: 16,
|
||||||
|
color: theme.colorScheme.outline.withOpacity(0.1),
|
||||||
|
);
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(title: const Text('聊天设置')),
|
||||||
|
body: ListView(
|
||||||
|
padding:
|
||||||
|
EdgeInsets.only(bottom: MediaQuery.paddingOf(context).bottom + 80),
|
||||||
|
children: [
|
||||||
|
divider,
|
||||||
|
Obx(() =>
|
||||||
|
_buildUserInfo(theme, divider, _controller.userState.value)),
|
||||||
|
Obx(() => _buildSessionSs(
|
||||||
|
theme, divider, divider2, _controller.sessionSs.value)),
|
||||||
|
Obx(() => _controller.sessionSs.value.isSuccess
|
||||||
|
? _buildBlockItem(
|
||||||
|
_controller.sessionSs.value.data.followStatus == 128)
|
||||||
|
: const SizedBox.shrink()),
|
||||||
|
divider2,
|
||||||
|
ListTile(
|
||||||
|
dense: true,
|
||||||
|
onTap: _controller.report,
|
||||||
|
title: const Text('举报', style: TextStyle(fontSize: 14)),
|
||||||
|
trailing: Icon(
|
||||||
|
Icons.keyboard_arrow_right,
|
||||||
|
color: theme.colorScheme.outline,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
divider,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildBlockItem(bool isBlocked) {
|
||||||
|
return ListTile(
|
||||||
|
dense: true,
|
||||||
|
onTap: () => _controller.setBlock(isBlocked),
|
||||||
|
title: const Text('加入黑名单', style: TextStyle(fontSize: 14)),
|
||||||
|
trailing: Transform.scale(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
scale: 0.8,
|
||||||
|
child: Switch(
|
||||||
|
thumbIcon:
|
||||||
|
WidgetStateProperty.resolveWith<Icon?>((Set<WidgetState> states) {
|
||||||
|
if (states.isNotEmpty && states.first == WidgetState.selected) {
|
||||||
|
return const Icon(Icons.done);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}),
|
||||||
|
value: isBlocked,
|
||||||
|
onChanged: (value) => _controller.setBlock(isBlocked),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildUserInfo(
|
||||||
|
ThemeData theme,
|
||||||
|
Widget divider,
|
||||||
|
LoadingState<List<ImUserInfosData>?> loadingState,
|
||||||
|
) {
|
||||||
|
return switch (loadingState) {
|
||||||
|
Loading() => const SizedBox.shrink(),
|
||||||
|
Success(:var response) => response?.isNotEmpty == true
|
||||||
|
? Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Builder(
|
||||||
|
builder: (context) {
|
||||||
|
final ImUserInfosData item = response!.first;
|
||||||
|
return ListTile(
|
||||||
|
onTap: () {
|
||||||
|
Get.toNamed('/member?mid=${item.mid}');
|
||||||
|
},
|
||||||
|
leading: PendantAvatar(
|
||||||
|
avatar: item.face,
|
||||||
|
size: 42,
|
||||||
|
badgeSize: 14,
|
||||||
|
isVip: item.vip?.status != null && item.vip!.status > 0,
|
||||||
|
garbPendantImage: item.pendant?.image,
|
||||||
|
officialType: item.official?.type,
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
item.name!,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: item.vip?.status != null &&
|
||||||
|
item.vip!.status > 0 &&
|
||||||
|
item.vip?.type == 2
|
||||||
|
? context.vipColor
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
subtitle: Text(
|
||||||
|
'UID: ${item.mid}${item.sign?.isNotEmpty == true ? '\n${item.sign}' : ''}',
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
color: theme.colorScheme.outline,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
trailing: Icon(
|
||||||
|
size: 22,
|
||||||
|
Icons.keyboard_arrow_right,
|
||||||
|
color: theme.colorScheme.outline,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
divider,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: const SizedBox.shrink(),
|
||||||
|
Error(:var errMsg) => _errWidget(errMsg, _controller.getUserInfo),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildSessionSs(
|
||||||
|
ThemeData theme,
|
||||||
|
Widget divider,
|
||||||
|
Widget divider2,
|
||||||
|
LoadingState<SessionSsData> loadingState,
|
||||||
|
) {
|
||||||
|
return switch (loadingState) {
|
||||||
|
Loading() => const SizedBox.shrink(),
|
||||||
|
Success(:var response) => Builder(
|
||||||
|
builder: (context) {
|
||||||
|
late final subTitleS =
|
||||||
|
TextStyle(fontSize: 13, color: theme.colorScheme.outline);
|
||||||
|
return Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
if (response.showPushSetting == 1)
|
||||||
|
ListTile(
|
||||||
|
dense: true,
|
||||||
|
onTap: () => _controller.setPush(response.pushSetting == 0),
|
||||||
|
title: const Text('接收消息推送', style: TextStyle(fontSize: 14)),
|
||||||
|
subtitle: Text(
|
||||||
|
'若关闭此开关,你将不再收到该账号的图文消息与稿件推送,但通知类消息不受影响',
|
||||||
|
style: subTitleS,
|
||||||
|
),
|
||||||
|
trailing: Transform.scale(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
scale: 0.8,
|
||||||
|
child: Switch(
|
||||||
|
thumbIcon: WidgetStateProperty.resolveWith<Icon?>(
|
||||||
|
(Set<WidgetState> states) {
|
||||||
|
if (states.isNotEmpty &&
|
||||||
|
states.first == WidgetState.selected) {
|
||||||
|
return const Icon(Icons.done);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}),
|
||||||
|
value: response.pushSetting == 0,
|
||||||
|
onChanged: (value) =>
|
||||||
|
_controller.setPush(response.pushSetting == 0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
divider2,
|
||||||
|
Obx(
|
||||||
|
() => ListTile(
|
||||||
|
dense: true,
|
||||||
|
onTap: _controller.setPin,
|
||||||
|
title: const Text('置顶聊天', style: TextStyle(fontSize: 14)),
|
||||||
|
trailing: Transform.scale(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
scale: 0.8,
|
||||||
|
child: Switch(
|
||||||
|
thumbIcon: WidgetStateProperty.resolveWith<Icon?>(
|
||||||
|
(Set<WidgetState> states) {
|
||||||
|
if (states.isNotEmpty &&
|
||||||
|
states.first == WidgetState.selected) {
|
||||||
|
return const Icon(Icons.done);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}),
|
||||||
|
value: _controller.isPinned.value,
|
||||||
|
onChanged: (value) => _controller.setPin(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
divider2,
|
||||||
|
Obx(() => _buildMuteItem(_controller.msgDnd.value)),
|
||||||
|
divider,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Error(:var errMsg) => _errWidget(errMsg, _controller.getSessionSs),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildMuteItem(LoadingState<List<UidSetting>?> loadingState) {
|
||||||
|
return switch (loadingState) {
|
||||||
|
Loading() => const SizedBox.shrink(),
|
||||||
|
Success(:var response) => response?.isNotEmpty == true
|
||||||
|
? ListTile(
|
||||||
|
dense: true,
|
||||||
|
onTap: () => _controller.setMute(response.first.setting == 1),
|
||||||
|
title: const Text('消息免打扰', style: TextStyle(fontSize: 14)),
|
||||||
|
trailing: Transform.scale(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
scale: 0.8,
|
||||||
|
child: Switch(
|
||||||
|
thumbIcon: WidgetStateProperty.resolveWith<Icon?>(
|
||||||
|
(Set<WidgetState> states) {
|
||||||
|
if (states.isNotEmpty &&
|
||||||
|
states.first == WidgetState.selected) {
|
||||||
|
return const Icon(Icons.done);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}),
|
||||||
|
value: response!.first.setting == 1,
|
||||||
|
onChanged: (value) =>
|
||||||
|
_controller.setMute(response.first.setting == 1),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: const SizedBox.shrink(),
|
||||||
|
Error(:var errMsg) => _errWidget(errMsg, _controller.getMsgDnd),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _errWidget(String? errMsg, VoidCallback onTap) {
|
||||||
|
return GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
onTap: onTap,
|
||||||
|
child: Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||||
|
child: Text(
|
||||||
|
errMsg ?? '',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:PiliPlus/grpc/bilibili/app/im/v1.pb.dart'
|
import 'package:PiliPlus/grpc/bilibili/app/im/v1.pb.dart'
|
||||||
show SelectItem, Setting, SettingSwitch;
|
show SelectItem, Setting, SettingSwitch;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -120,43 +122,55 @@ class ImSettingsItem extends StatelessWidget {
|
|||||||
|
|
||||||
if (item.hasSelect()) {
|
if (item.hasSelect()) {
|
||||||
String? selected;
|
String? selected;
|
||||||
|
late final divider = Divider(
|
||||||
|
height: 1,
|
||||||
|
indent: 16,
|
||||||
|
color: outline.withOpacity(0.1),
|
||||||
|
);
|
||||||
return Column(
|
return Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: item.select.item.map((e) {
|
children: List.generate(
|
||||||
if (e.selected) {
|
max(0, item.select.item.length * 2 - 1),
|
||||||
selected ??= e.text;
|
(index) {
|
||||||
}
|
if (index.isOdd) {
|
||||||
return ListTile(
|
return divider;
|
||||||
dense: true,
|
}
|
||||||
onTap: () async {
|
final e = item.select.item[index ~/ 2];
|
||||||
if (!e.selected) {
|
if (e.selected) {
|
||||||
for (var i in item.select.item) {
|
selected ??= e.text;
|
||||||
i.selected = false;
|
}
|
||||||
}
|
return ListTile(
|
||||||
e.selected = true;
|
dense: true,
|
||||||
rebuild();
|
onTap: () async {
|
||||||
|
if (!e.selected) {
|
||||||
if (await onSet()) {
|
|
||||||
selected = e.text;
|
|
||||||
} else {
|
|
||||||
for (var i in item.select.item) {
|
for (var i in item.select.item) {
|
||||||
i.selected = i.text == selected;
|
i.selected = false;
|
||||||
}
|
}
|
||||||
|
e.selected = true;
|
||||||
rebuild();
|
rebuild();
|
||||||
|
|
||||||
|
if (await onSet()) {
|
||||||
|
selected = e.text;
|
||||||
|
} else {
|
||||||
|
for (var i in item.select.item) {
|
||||||
|
i.selected = i.text == selected;
|
||||||
|
}
|
||||||
|
rebuild();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
title: Text(e.text, style: titleStyle),
|
||||||
title: Text(e.text, style: titleStyle),
|
trailing: e.selected
|
||||||
trailing: e.selected
|
? Icon(
|
||||||
? Icon(
|
size: 20,
|
||||||
size: 20,
|
Icons.check,
|
||||||
Icons.check,
|
color: theme.colorScheme.primary,
|
||||||
color: theme.colorScheme.primary,
|
)
|
||||||
)
|
: null,
|
||||||
: null,
|
);
|
||||||
);
|
},
|
||||||
}).toList(),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -172,8 +172,8 @@ extension ThreeDotItemTypeExt on ThreeDotItemType {
|
|||||||
case ThreeDotItemType.THREE_DOT_ITEM_TYPE_UP_HELPER:
|
case ThreeDotItemType.THREE_DOT_ITEM_TYPE_UP_HELPER:
|
||||||
Get.toNamed(
|
Get.toNamed(
|
||||||
'/whisperDetail',
|
'/whisperDetail',
|
||||||
parameters: {
|
arguments: {
|
||||||
'talkerId': '844424930131966',
|
'talkerId': 844424930131966,
|
||||||
'name': 'UP主小助手',
|
'name': 'UP主小助手',
|
||||||
'face':
|
'face':
|
||||||
'https://message.biliimg.com/bfs/im/489a63efadfb202366c2f88853d2217b5ddc7a13.png',
|
'https://message.biliimg.com/bfs/im/489a63efadfb202366c2f88853d2217b5ddc7a13.png',
|
||||||
|
|||||||
@@ -496,6 +496,10 @@ class GStorage {
|
|||||||
GStorage.localCache.put(LocalCacheKey.blackMids, blackMids..add(mid));
|
GStorage.localCache.put(LocalCacheKey.blackMids, blackMids..add(mid));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void removeBlackMid(int mid) {
|
||||||
|
GStorage.localCache.put(LocalCacheKey.blackMids, blackMids..remove(mid));
|
||||||
|
}
|
||||||
|
|
||||||
static MemberTabType get memberTab => MemberTabType
|
static MemberTabType get memberTab => MemberTabType
|
||||||
.values[setting.get(SettingBoxKey.memberTab, defaultValue: 0)];
|
.values[setting.get(SettingBoxKey.memberTab, defaultValue: 0)];
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user