Commit acb9d7e1 authored by skeyboy's avatar skeyboy

百晓通聊天列表多选分享

parent 7209ed34
...@@ -12,6 +12,7 @@ class ChatMessage { ...@@ -12,6 +12,7 @@ class ChatMessage {
this.mentions, this.mentions,
this.status = MessageStatus.none, this.status = MessageStatus.none,
this.replyTo, this.replyTo,
this.showMenu = false
}); });
/// Create a ChatMessage instance from json data /// Create a ChatMessage instance from json data
...@@ -77,6 +78,8 @@ class ChatMessage { ...@@ -77,6 +78,8 @@ class ChatMessage {
/// If the message is a reply of another one TODO: /// If the message is a reply of another one TODO:
ChatMessage? replyTo; ChatMessage? replyTo;
bool? showMenu = false;
bool? selected = false;
/// Convert a ChatMessage into a json /// Convert a ChatMessage into a json
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
......
...@@ -33,7 +33,8 @@ class MessageOptions { ...@@ -33,7 +33,8 @@ class MessageOptions {
this.messageMediaBuilder, this.messageMediaBuilder,
this.onLongPressStart, this.onLongPressStart,
this.onLongPressEnd, this.onLongPressEnd,
this.shareMenuBuilder}); this.shareMenuBuilder,
this.multiSelectedBuilder});
//长按 //长按
...@@ -158,9 +159,14 @@ class MessageOptions { ...@@ -158,9 +159,14 @@ class MessageOptions {
/// Will not work with the default video player /// Will not work with the default video player
final void Function(ChatMedia media)? onTapMedia; final void Function(ChatMedia media)? onTapMedia;
/// 多选
final ChatRowSelected? Function(ChatMessage chatMessage)?
multiSelectedBuilder;
final Widget? Function( final Widget? Function(
ChatMessage message, ChatMessage message,
CustomPopupMenuController controller, CustomPopupMenuController controller,
Widget chatMessageBody, Widget chatMessageBody,
bool? showMenus)? shareMenuBuilder; bool? showMenus)? shareMenuBuilder;
} }
typedef ChatRowSelected = void Function(ChatMessage message, bool? value);
...@@ -74,12 +74,25 @@ class MessageRow extends StatelessWidget { ...@@ -74,12 +74,25 @@ class MessageRow extends StatelessWidget {
if (nextMessage != null && nextMessage!.user.id == message.user.id) { if (nextMessage != null && nextMessage!.user.id == message.user.id) {
isNextSameAuthor = true; isNextSameAuthor = true;
} }
if (messageOptions.multiSelectedBuilder != null) {
ChatRowSelected? chatRowSelected =
messageOptions.multiSelectedBuilder?.call(message);
return Padding( return Padding(
padding: EdgeInsets.only(top: isPreviousSameAuthor ? 2 : 15), padding: EdgeInsets.only(top: isPreviousSameAuthor ? 2 : 15),
child: Row(
children: [
if (message.showMenu == true)
Checkbox(
value: message.selected,
onChanged: (onChanged) {
chatRowSelected?.call(message, onChanged);
}),
Expanded(
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: mainAxisAlignment: isOwnMessage
isOwnMessage ? MainAxisAlignment.end : MainAxisAlignment.start, ? MainAxisAlignment.end
: MainAxisAlignment.start,
children: <Widget>[ children: <Widget>[
// if (messageOptions.showOtherUsersAvatar) // if (messageOptions.showOtherUsersAvatar)
// Opacity( // Opacity(
...@@ -110,7 +123,8 @@ class MessageRow extends StatelessWidget { ...@@ -110,7 +123,8 @@ class MessageRow extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[ children: <Widget>[
if (messageOptions.top != null) if (messageOptions.top != null)
messageOptions.top!(message, previousMessage, nextMessage), messageOptions.top!(
message, previousMessage, nextMessage),
if (!isOwnMessage && if (!isOwnMessage &&
messageOptions.showOtherUsersName && messageOptions.showOtherUsersName &&
(!isPreviousSameAuthor || isAfterDateSeparator)) (!isPreviousSameAuthor || isAfterDateSeparator))
...@@ -215,7 +229,154 @@ class MessageRow extends StatelessWidget { ...@@ -215,7 +229,154 @@ class MessageRow extends StatelessWidget {
if (!messageOptions.showCurrentUserAvatar) if (!messageOptions.showCurrentUserAvatar)
const Padding(padding: EdgeInsets.only(left: 10)) const Padding(padding: EdgeInsets.only(left: 10))
], ],
))
],
), ),
); );
} else {
return Padding(
padding: EdgeInsets.only(top: isPreviousSameAuthor ? 2 : 15),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment:
isOwnMessage ? MainAxisAlignment.end : MainAxisAlignment.start,
children: <Widget>[
// if (messageOptions.showOtherUsersAvatar)
// Opacity(
// opacity:
// !isOwnMessage && (!isNextSameAuthor || isBeforeDateSeparator)
// ? 1
// : 0,
// child: getAvatar(),
// ),
if (messageOptions.showOtherUsersAvatar)
const Padding(padding: EdgeInsets.only(left: 10)),
GestureDetector(
onLongPress: messageOptions.onLongPressMessage != null
? () => messageOptions.onLongPressMessage!(message)
: null,
onTap: messageOptions.onPressMessage != null
? () => messageOptions.onPressMessage!(message)
: null,
child: ConstrainedBox(
constraints: BoxConstraints(
maxWidth: messageOptions.maxWidth ??
MediaQuery.of(context).size.width * 0.8,
),
child: Column(
crossAxisAlignment: isOwnMessage
? CrossAxisAlignment.end
: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
if (messageOptions.top != null)
messageOptions.top!(
message, previousMessage, nextMessage),
if (!isOwnMessage &&
messageOptions.showOtherUsersName &&
(!isPreviousSameAuthor || isAfterDateSeparator))
messageOptions.userNameBuilder != null
? messageOptions.userNameBuilder!(message.user)
: DefaultUserName(user: message.user),
if (message.medias != null &&
message.medias!.isNotEmpty &&
messageOptions.textBeforeMedia)
messageOptions.messageMediaBuilder != null
? messageOptions.messageMediaBuilder!(
message, previousMessage, nextMessage)
: MediaContainer(
message: message,
isOwnMessage: isOwnMessage,
messageOptions: messageOptions,
messageLength: messageLength,
// messageLength: messageLength,
),
if (message.text.isNotEmpty)
(TextContainer(
index: index,
messageOptions: messageOptions,
message: message,
messageLength: messageLength,
previousMessage: previousMessage,
nextMessage: nextMessage,
isOwnMessage: isOwnMessage,
isNextSameAuthor: isNextSameAuthor,
isPreviousSameAuthor: isPreviousSameAuthor,
isAfterDateSeparator: isAfterDateSeparator,
isBeforeDateSeparator: isBeforeDateSeparator,
messageTextBuilder: messageOptions.messageTextBuilder,
)),
// (isOwnMessage
// ? TextContainer(
// index: index,
// messageOptions: messageOptions,
// message: message,
// messageLength: messageLength,
// previousMessage: previousMessage,
// nextMessage: nextMessage,
// isOwnMessage: isOwnMessage,
// isNextSameAuthor: isNextSameAuthor,
// isPreviousSameAuthor: isPreviousSameAuthor,
// isAfterDateSeparator: isAfterDateSeparator,
// isBeforeDateSeparator: isBeforeDateSeparator,
// messageTextBuilder:
// messageOptions.messageTextBuilder,
// )
// : Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Expanded(
// child: TextContainer(
// index: index,
// messageOptions: messageOptions,
// message: message,
// messageLength: messageLength,
// previousMessage: previousMessage,
// nextMessage: nextMessage,
// isOwnMessage: isOwnMessage,
// isNextSameAuthor: isNextSameAuthor,
// isPreviousSameAuthor: isPreviousSameAuthor,
// isAfterDateSeparator: isAfterDateSeparator,
// isBeforeDateSeparator: isBeforeDateSeparator,
// messageTextBuilder:
// messageOptions.messageTextBuilder,
// )),
// Padding(
// key: ValueKey(message.hashCode),
// padding: EdgeInsets.only(left: 10, top: 15),
// child: ChartTTSWave(text: message.text, message: message,)),
// ],
// )),
if (message.medias != null &&
message.medias!.isNotEmpty &&
!messageOptions.textBeforeMedia)
messageOptions.messageMediaBuilder != null
? messageOptions.messageMediaBuilder!(
message, previousMessage, nextMessage)
: MediaContainer(
message: message,
isOwnMessage: isOwnMessage,
messageOptions: messageOptions,
messageLength: messageLength,
),
if (messageOptions.bottom != null)
messageOptions.bottom!(
message, previousMessage, nextMessage),
],
),
),
),
if (messageOptions.showCurrentUserAvatar)
Opacity(
opacity: isOwnMessage && !isNextSameAuthor ? 1 : 0,
child: getAvatar(),
),
if (!messageOptions.showCurrentUserAvatar)
const Padding(padding: EdgeInsets.only(left: 10))
],
),
);
}
} }
} }
...@@ -3,6 +3,7 @@ import 'dart:async'; ...@@ -3,6 +3,7 @@ import 'dart:async';
import 'package:chart/common/apis/apis.dart'; import 'package:chart/common/apis/apis.dart';
import 'package:chart/common/routers/routes.dart'; import 'package:chart/common/routers/routes.dart';
import 'package:chart/common/store/store.dart'; import 'package:chart/common/store/store.dart';
import 'package:chart/common/values/colors.dart';
import 'package:chart/common/values/server.dart'; import 'package:chart/common/values/server.dart';
import 'package:chart/entity/user_entity.dart'; import 'package:chart/entity/user_entity.dart';
import 'package:chart/package/chat_dash/dash_chat_2.dart' as Chat; import 'package:chart/package/chat_dash/dash_chat_2.dart' as Chat;
...@@ -45,6 +46,10 @@ class HomeController extends GetxController with SingleGetTickerProviderMixin { ...@@ -45,6 +46,10 @@ class HomeController extends GetxController with SingleGetTickerProviderMixin {
late Animation<double> _animation; late Animation<double> _animation;
bool _isRotated = false; bool _isRotated = false;
/// 选中的分享条目
RxList<ChatMessage> selectedItems = <ChatMessage>[].obs;
RxBool chatRowCheckRadioShowed = false.obs;
// ignore: prefer_typing_uninitialized_variables // ignore: prefer_typing_uninitialized_variables
late var sse; late var sse;
...@@ -73,6 +78,61 @@ class HomeController extends GetxController with SingleGetTickerProviderMixin { ...@@ -73,6 +78,61 @@ class HomeController extends GetxController with SingleGetTickerProviderMixin {
/// 事件 /// 事件
/// 右上角分享管理相关菜单
List<Widget> topRightBarMenus() {
/// 选中数据不为空 或者 展示选择菜单右侧即显示 分享相关控制
return (selectedItems.isEmpty == true && chatRowCheckRadioShowed.value == false)
? [
RotationTransition(
turns: animation,
child: IconButton(
tooltip: '重置',
icon: const Icon(
Icons.settings_backup_restore_sharp,
color: AppColors.primaryElementText,
),
onPressed: () {
reset();
},
),
),
IconButton(
tooltip: '设置',
icon: const Icon(
Icons.settings,
color: AppColors.primaryElementText,
),
onPressed: () {
share();
},
)
]
: [
IconButton(
tooltip: "分享",
onPressed: () async {
selectedItems.sort((l, r) => r.createdAt
.difference(DateTime.now())
.compareTo(l.createdAt.difference(DateTime.now())));
String sharedMsg = selectedItems.reversed
.map((element) => element.text)
.join("\n\n");
ShareResult result = await Share.shareWithResult(sharedMsg);
if (result.status == ShareResultStatus.success) {
EasyLoading.showToast("分享成功");
} else {
EasyLoading.showToast("分享取消");
}
},
icon: const Icon(Icons.share)),
IconButton(
onPressed: () {
_hideChatMessageCheckbox();
},
icon: const Icon(Icons.cancel_outlined))
];
}
bool gotoLoginPage() { bool gotoLoginPage() {
if (UserStore.to.isLogin == false) { if (UserStore.to.isLogin == false) {
Get.toNamed(AppRoutes.SIGN_IN); Get.toNamed(AppRoutes.SIGN_IN);
...@@ -420,7 +480,7 @@ class HomeController extends GetxController with SingleGetTickerProviderMixin { ...@@ -420,7 +480,7 @@ class HomeController extends GetxController with SingleGetTickerProviderMixin {
// } // }
} }
List<ItemModel> menuItems = [ List<ItemModel> get menuItems => [
ItemModel( ItemModel(
'复制', '复制',
Icons.content_copy, Icons.content_copy,
...@@ -444,7 +504,8 @@ class HomeController extends GetxController with SingleGetTickerProviderMixin { ...@@ -444,7 +504,8 @@ class HomeController extends GetxController with SingleGetTickerProviderMixin {
); );
controller.hideMenu(); controller.hideMenu();
} else { } else {
EasyLoading.showToast("分享内容不能为空", maskType: EasyLoadingMaskType.none); EasyLoading.showToast("分享内容不能为空",
maskType: EasyLoadingMaskType.none);
} }
// FlutterClipboard.copy(message.text).then((value) { // FlutterClipboard.copy(message.text).then((value) {
...@@ -456,12 +517,31 @@ class HomeController extends GetxController with SingleGetTickerProviderMixin { ...@@ -456,12 +517,31 @@ class HomeController extends GetxController with SingleGetTickerProviderMixin {
), ),
// ItemModel('收藏', Icons.collections), // ItemModel('收藏', Icons.collections),
// ItemModel('删除', Icons.delete), // ItemModel('删除', Icons.delete),
// ItemModel('多选', Icons.playlist_add_check), ItemModel('多选', Icons.playlist_add_check, (message, controller) {
state.messageList.forEach((element) {
element.showMenu = true;
});
state.messageList
.replaceRange(0, state.messageList.length - 1, state.messageList);
controller.hideMenu();
chatRowCheckRadioShowed.value = true;
}),
// ItemModel('引用', Icons.format_quote), // ItemModel('引用', Icons.format_quote),
// ItemModel('提醒', Icons.add_alert), // ItemModel('提醒', Icons.add_alert),
// ItemModel('搜一搜', Icons.search), // ItemModel('搜一搜', Icons.search),
]; ];
_hideChatMessageCheckbox() {
state.messageList.forEach((element) {
element.showMenu = false;
element.selected = false;
});
state.messageList
.replaceRange(0, state.messageList.length - 1, state.messageList);
selectedItems.value.clear();
chatRowCheckRadioShowed.value = false;
}
Widget shareMenuBody( Widget shareMenuBody(
ChatMessage message, CustomPopupMenuController controller) { ChatMessage message, CustomPopupMenuController controller) {
return ClipRRect( return ClipRRect(
...@@ -506,6 +586,28 @@ class HomeController extends GetxController with SingleGetTickerProviderMixin { ...@@ -506,6 +586,28 @@ class HomeController extends GetxController with SingleGetTickerProviderMixin {
); );
} }
// 分享菜单回调事项
ChatRowSelected? multiSelectedBuilder(ChatMessage message) {
return (message, value) {
_update(message, value: value);
};
}
_update(ChatMessage message, {bool? value = false}) {
message.selected = value ?? false;
int index = state.messageList.indexWhere((element) => element == message);
state.messageList.replaceRange(index, index + 1, [message]);
if (value == false) {
selectedItems.remove(message);
} else {
selectedItems.add(message);
selectedItems.value.sort();
}
selectedItems.forEach((element) {
print(element.text);
});
}
/// 根据需要自己判断怎么构建长按菜单 /// 根据需要自己判断怎么构建长按菜单
Widget? shareMenuBuilder( Widget? shareMenuBuilder(
ChatMessage message, ChatMessage message,
......
...@@ -49,36 +49,11 @@ class _HomePageState extends State<_HomePage> ...@@ -49,36 +49,11 @@ class _HomePageState extends State<_HomePage>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final cc = Get.put(HomeController()); final cc = Get.put(HomeController());
return Obx(() => return Obx(() => Scaffold(
Scaffold(
resizeToAvoidBottomInset: false, resizeToAvoidBottomInset: false,
backgroundColor: Color.fromARGB(0, 0, 0, 0), backgroundColor: Color.fromARGB(0, 0, 0, 0),
appBar: transparentAppBar( appBar: transparentAppBar(
actions: [ actions: controller.topRightBarMenus(),
RotationTransition(
turns: controller.animation,
child: IconButton(
tooltip: '重置',
icon: const Icon(
Icons.settings_backup_restore_sharp,
color: AppColors.primaryElementText,
),
onPressed: () {
controller.reset();
},
),
),
IconButton(
tooltip: '设置',
icon: const Icon(
Icons.settings,
color: AppColors.primaryElementText,
),
onPressed: () {
controller.share();
},
)
],
title: RichText( title: RichText(
textAlign: TextAlign.center, textAlign: TextAlign.center,
text: TextSpan(children: [ text: TextSpan(children: [
...@@ -119,8 +94,7 @@ class _HomePageState extends State<_HomePage> ...@@ -119,8 +94,7 @@ class _HomePageState extends State<_HomePage>
), ),
Text("订阅", Text("订阅",
style: TextStyle( style: TextStyle(
color: Color.fromARGB(255, 95, 54, 0), color: Color.fromARGB(255, 95, 54, 0), fontSize: 16)),
fontSize: 16)),
]), ]),
callback: () => controller.virtualPay()), callback: () => controller.virtualPay()),
), ),
...@@ -148,7 +122,9 @@ class _HomePageState extends State<_HomePage> ...@@ -148,7 +122,9 @@ class _HomePageState extends State<_HomePage>
// GradientButton( // GradientButton(
// child: Icon(Icons.text_fields_sharp), callback: () {}), // child: Icon(Icons.text_fields_sharp), callback: () {}),
/*Android语音转换文字有问题暂时屏蔽掉*/ /*Android语音转换文字有问题暂时屏蔽掉*/
(Platform.isAndroid ? Container() : Container( (Platform.isAndroid
? Container()
: Container(
margin: const EdgeInsets.only(right: 10), margin: const EdgeInsets.only(right: 10),
child: InkWell( child: InkWell(
borderRadius: BorderRadius.circular(100), borderRadius: BorderRadius.circular(100),
...@@ -225,8 +201,8 @@ class _HomePageState extends State<_HomePage> ...@@ -225,8 +201,8 @@ class _HomePageState extends State<_HomePage>
), ),
inputToolbarMargin: EdgeInsets.all(0), inputToolbarMargin: EdgeInsets.all(0),
sendButtonBuilder: null, sendButtonBuilder: null,
inputToolbarPadding: EdgeInsets.only( inputToolbarPadding:
top: 15, right: 15, left: 15, bottom: 10), EdgeInsets.only(top: 15, right: 15, left: 15, bottom: 10),
), ),
currentUser: _user, currentUser: _user,
onSend: cc.sendMessage, onSend: cc.sendMessage,
...@@ -234,13 +210,13 @@ class _HomePageState extends State<_HomePage> ...@@ -234,13 +210,13 @@ class _HomePageState extends State<_HomePage>
messageOptions: Chat.MessageOptions( messageOptions: Chat.MessageOptions(
onPressMessage: cc.tabMessage, onPressMessage: cc.tabMessage,
onLongPressMessage: (mes) => {}, onLongPressMessage: (mes) => {},
shareMenuBuilder: cc.shareMenuBuilder shareMenuBuilder: cc.shareMenuBuilder,
multiSelectedBuilder: cc.multiSelectedBuilder
// containerColor: Colors.black, // containerColor: Colors.black,
), ),
), ),
), ),
) ));
);
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment