feat: 启用私信功能(初步)

This commit is contained in:
orz12
2024-06-17 01:19:23 +08:00
parent 052f81cf10
commit ff3484e4b5
7 changed files with 146 additions and 100 deletions

View File

@@ -1,4 +1,6 @@
import 'dart:math';
import 'package:dio/dio.dart';
import '../models/msg/account.dart';
import '../models/msg/session.dart';
import '../utils/wbi_sign.dart';
@@ -6,7 +8,6 @@ import 'api.dart';
import 'init.dart';
class MsgHttp {
static Future msgFeedReplyMe({int cursor = -1, int cursorTime = -1}) async {
var res = await Request().get(Api.msgFeedReply, data: {
'id': cursor == -1 ? null : cursor,
@@ -25,6 +26,7 @@ class MsgHttp {
};
}
}
static Future msgFeedAtMe({int cursor = -1, int cursorTime = -1}) async {
var res = await Request().get(Api.msgFeedAt, data: {
'id': cursor == -1 ? null : cursor,
@@ -43,6 +45,7 @@ class MsgHttp {
};
}
}
static Future msgFeedLikeMe({int cursor = -1, int cursorTime = -1}) async {
var res = await Request().get(Api.msgFeedLike, data: {
'id': cursor == -1 ? null : cursor,
@@ -61,6 +64,7 @@ class MsgHttp {
};
}
}
static Future msgFeedSysUserNotify() async {
String csrf = await Request.getCsrf();
var res = await Request().get(Api.msgSysUserNotify, data: {
@@ -81,6 +85,7 @@ class MsgHttp {
};
}
}
static Future msgFeedSysUnifiedNotify() async {
String csrf = await Request.getCsrf();
var res = await Request().get(Api.msgSysUnifiedNotify, data: {
@@ -136,6 +141,7 @@ class MsgHttp {
};
}
}
// 会话列表
static Future sessionList({int? endTs}) async {
Map<String, dynamic> params = {
@@ -271,7 +277,7 @@ class MsgHttp {
dynamic content,
}) async {
String csrf = await Request.getCsrf();
Map<String, dynamic> params = await WbiSign().makSign({
Map<String, dynamic> base = {
'msg[sender_uid]': senderUid,
'msg[receiver_id]': receiverId,
'msg[receiver_type]': receiverType ?? 1,
@@ -286,21 +292,17 @@ class MsgHttp {
'mobi_app': 'web',
'csrf_token': csrf,
'csrf': csrf,
});
var res =
await Request().post(Api.sendMsg, queryParameters: <String, dynamic>{
...params,
'csrf_token': csrf,
'csrf': csrf,
}, data: {
};
Map<String, dynamic> params = await WbiSign().makSign(base);
var res = await Request().post(Api.sendMsg,
queryParameters: <String, dynamic>{
'w_sender_uid': params['msg[sender_uid]'],
'w_receiver_id': params['msg[receiver_id]'],
'w_dev_id': params['msg[dev_id]'],
'w_rid': params['w_rid'],
'wts': params['wts'],
'csrf_token': csrf,
'csrf': csrf,
});
},
data: FormData.fromMap(base));
if (res.data['code'] == 0) {
return {
'status': true,

View File

@@ -195,6 +195,7 @@ class MessageItem {
this.msgStatus,
this.notifyCode,
this.newFaceVersion,
this.msgSource,
});
int? senderUid;
@@ -209,6 +210,7 @@ class MessageItem {
int? msgStatus;
String? notifyCode;
int? newFaceVersion;
int? msgSource;
MessageItem.fromJson(Map<String, dynamic> json) {
senderUid = json['sender_uid'];
@@ -226,5 +228,6 @@ class MessageItem {
msgStatus = json['msg_status'];
notifyCode = json['notify_code'];
newFaceVersion = json['new_face_version'];
msgSource = json['msg_source'];
}
}

View File

@@ -202,7 +202,17 @@ class ProfilePanel extends StatelessWidget {
const SizedBox(width: 8),
Expanded(
child: TextButton(
onPressed: () {},
onPressed: () {
Get.toNamed(
'/whisperDetail',
parameters: {
'talkerId': ctr.mid.toString(),
'name': memberInfo.name!,
'face': memberInfo.face!,
'mid': ctr.mid.toString(),
},
);
},
style: TextButton.styleFrom(
backgroundColor: Theme.of(context)
.colorScheme

View File

@@ -238,7 +238,7 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
width: double.infinity,
height: toolbarType == 'input' ? keyboardHeight : emoteHeight,
child: EmotePanel(
onChoose: (package, emote) => onChooseEmote(package, emote),
onChoose: onChooseEmote,
),
),
if (toolbarType == 'input' && keyboardHeight == 0.0)

View File

@@ -153,6 +153,17 @@ class _WhisperPageState extends State<WhisperPage> {
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (_, int i) {
dynamic content =
sessionList[i].lastMsg.content;
if (content == null || content == "") {
content = '不支持的消息类型';
} else {
content = content['text'] ??
content['content'] ??
content['title'] ??
content['reply_content'] ??
content.toString();
}
return ListTile(
onTap: () {
setState(() {
@@ -195,28 +206,7 @@ class _WhisperPageState extends State<WhisperPage> {
),
title:
Text(sessionList[i].accountInfo.name),
subtitle: Text(
sessionList[i].lastMsg.content !=
null &&
sessionList[i]
.lastMsg
.content !=
''
? (sessionList[i]
.lastMsg
.content['text'] ??
sessionList[i]
.lastMsg
.content['content'] ??
sessionList[i]
.lastMsg
.content['title'] ??
sessionList[i]
.lastMsg
.content[
'reply_content']) ??
sessionList[i].lastMsg.content
: '不支持的消息类型',
subtitle: Text(content,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context)

View File

@@ -32,7 +32,15 @@ class WhisperDetailController extends GetxController {
if (res['status']) {
messageList.value = res['data'].messages;
if (messageList.isNotEmpty) {
if (messageList.length == 1 &&
messageList.last.msgType == 18 &&
messageList.last.msgSource == 18) {
// print(messageList.last);
// print(messageList.last.content);
//{content: [{"text":"对方主动回复或关注你前最多发送1条消息","color_day":"#9499A0","color_nig":"#9499A0"}]}
} else {
ackSessionMsg();
}
if (res['data'].eInfos != null) {
eInfos = res['data'].eInfos;
}
@@ -68,13 +76,20 @@ class WhisperDetailController extends GetxController {
SmartDialog.showToast('请输入内容');
return;
}
if (mid == null) {
SmartDialog.showToast('这里不能发');
return;
}
var result = await MsgHttp.sendMsg(
senderUid: userInfo.mid,
receiverId: int.parse(mid),
content: {'content': message},
content: '{"content":"$message"}',
msgType: 1,
);
if (result['status']) {
print(result['data']);
querySessionMsg();
replyContentController.text = "";
SmartDialog.showToast('发送成功');
} else {
SmartDialog.showToast(result['msg']);

View File

@@ -7,6 +7,7 @@ import 'package:PiliPalaX/common/widgets/network_img_layer.dart';
import 'package:PiliPalaX/pages/emote/index.dart';
import 'package:PiliPalaX/pages/whisper_detail/controller.dart';
import 'package:PiliPalaX/utils/feed_back.dart';
import 'package:PiliPalaX/models/video/reply/emote.dart';
import '../../utils/storage.dart';
import 'widget/chat_item.dart';
@@ -26,7 +27,7 @@ class _WhisperDetailPageState extends State<WhisperDetailPage>
final _debouncer = Debouncer(milliseconds: 200); // 设置延迟时间
late double emoteHeight = 0.0;
double keyboardHeight = 0.0; // 键盘高度
String toolbarType = 'input';
String toolbarType = 'none';
Box userInfoCache = GStrorage.userInfo;
@override
@@ -35,15 +36,19 @@ class _WhisperDetailPageState extends State<WhisperDetailPage>
WidgetsBinding.instance.addObserver(this);
_whisperDetailController.querySessionMsg();
_replyContentController = _whisperDetailController.replyContentController;
_focuslistener();
_focusListener();
}
_focuslistener() {
_focusListener() {
replyContentFocusNode.addListener(() {
if (replyContentFocusNode.hasFocus) {
setState(() {
toolbarType = 'input';
});
} else if (toolbarType == 'input') {
setState(() {
toolbarType = 'none';
});
}
});
}
@@ -51,8 +56,7 @@ class _WhisperDetailPageState extends State<WhisperDetailPage>
@override
void didChangeMetrics() {
super.didChangeMetrics();
final String routePath = Get.currentRoute;
if (mounted && routePath.startsWith('/whisper_detail')) {
if (!mounted) return;
WidgetsBinding.instance.addPostFrameCallback((_) {
if (!mounted) return;
// 键盘高度
@@ -61,15 +65,17 @@ class _WhisperDetailPageState extends State<WhisperDetailPage>
_debouncer.run(() {
if (!mounted) return;
if (keyboardHeight == 0) {
setState(() {
emoteHeight = keyboardHeight =
keyboardHeight == 0.0 ? viewInsets.bottom : keyboardHeight;
});
if (emoteHeight == 0 || emoteHeight < keyboardHeight) {
emoteHeight = keyboardHeight;
}
if (emoteHeight < 200) emoteHeight = 200;
setState(() {});
}
});
});
}
}
@override
void dispose() {
@@ -79,6 +85,20 @@ class _WhisperDetailPageState extends State<WhisperDetailPage>
super.dispose();
}
void onChooseEmote(Packages package, Emote emote) {
int cursorPosition = _replyContentController.selection.baseOffset;
if (cursorPosition == -1) cursorPosition = 0;
final String currentText = _replyContentController.text;
final String newText = currentText.substring(0, cursorPosition) +
emote.text! +
currentText.substring(cursorPosition);
_replyContentController.value = TextEditingValue(
text: newText,
selection:
TextSelection.collapsed(offset: cursorPosition + emote.text!.length),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
@@ -147,10 +167,10 @@ class _WhisperDetailPageState extends State<WhisperDetailPage>
),
body: GestureDetector(
onTap: () {
FocusScope.of(context).unfocus();
setState(() {
keyboardHeight = 0;
toolbarType = 'none';
});
FocusScope.of(context).unfocus();
},
child: Obx(() {
List messageList = _whisperDetailController.messageList;
@@ -159,7 +179,9 @@ class _WhisperDetailPageState extends State<WhisperDetailPage>
child: CircularProgressIndicator(),
);
}
return ListView.builder(
return RefreshIndicator(
onRefresh: _whisperDetailController.querySessionMsg,
child: ListView.builder(
itemCount: messageList.length,
shrinkWrap: true,
reverse: true,
@@ -168,17 +190,22 @@ class _WhisperDetailPageState extends State<WhisperDetailPage>
item: messageList[i],
e_infos: _whisperDetailController.eInfos);
},
);
padding: const EdgeInsets.only(bottom: 20),
));
}),
),
// resizeToAvoidBottomInset: true,
bottomNavigationBar: Container(
width: double.infinity,
height: MediaQuery.of(context).padding.bottom + 70 + keyboardHeight,
height: MediaQuery.of(context).padding.bottom +
70 +
(toolbarType == 'none'
? 0
: (toolbarType == 'input' ? keyboardHeight : emoteHeight)),
padding: EdgeInsets.only(
left: 8,
right: 12,
top: 12,
top: 10,
bottom: MediaQuery.of(context).padding.bottom,
),
decoration: BoxDecoration(
@@ -205,16 +232,19 @@ class _WhisperDetailPageState extends State<WhisperDetailPage>
IconButton(
tooltip: '表情',
onPressed: () {
// if (toolbarType == 'input') {
// setState(() {
// toolbarType = 'emote';
// });
// }
// FocusScope.of(context).unfocus();
if (emoteHeight < 200) emoteHeight = 200;
if (toolbarType != 'emote') {
setState(() {
toolbarType = 'emote';
});
}
FocusScope.of(context).unfocus();
},
icon: Icon(
Icons.emoji_emotions_outlined,
color: Theme.of(context).colorScheme.outline,
Icons.emoji_emotions,
color: toolbarType == 'emote'
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.outline,
),
),
Expanded(
@@ -228,16 +258,15 @@ class _WhisperDetailPageState extends State<WhisperDetailPage>
borderRadius: BorderRadius.circular(40.0),
),
child: Semantics(
label: '私信输入框(开发中)',
label: '私信输入框',
child: TextField(
readOnly: true,
style: Theme.of(context).textTheme.titleMedium,
controller: _replyContentController,
autofocus: false,
focusNode: replyContentFocusNode,
decoration: const InputDecoration(
border: InputBorder.none, // 移除默认边框
hintText: '开发中 ...', // 提示文本
hintText: '发个消息聊聊呗~', // 提示文本
contentPadding: EdgeInsets.symmetric(
horizontal: 16.0, vertical: 12.0), // 内边距
),
@@ -246,25 +275,22 @@ class _WhisperDetailPageState extends State<WhisperDetailPage>
),
IconButton(
tooltip: '发送',
// onPressed: _whisperDetailController.sendMsg,
onPressed: null,
onPressed: _whisperDetailController.sendMsg,
icon: Icon(
Icons.send,
color: Theme.of(context).colorScheme.outline,
color: Theme.of(context).colorScheme.primary,
),
),
// const SizedBox(width: 16),
],
),
AnimatedSize(
curve: Curves.easeInOut,
duration: const Duration(milliseconds: 300),
child: SizedBox(
SizedBox(
width: double.infinity,
height: toolbarType == 'input' ? keyboardHeight : emoteHeight,
height: toolbarType == 'none'
? 0
: (toolbarType == 'input' ? keyboardHeight : emoteHeight),
child: EmotePanel(
onChoose: (package, emote) => {},
),
onChoose: onChooseEmote,
),
),
],