From 63671259611ad57c8c318d4e9e17e2eb1fac47af Mon Sep 17 00:00:00 2001 From: htylight Date: Sun, 27 Aug 2023 10:18:31 +0800 Subject: [PATCH] change group chat profile v1 --- lib/models/contact_model.dart | 81 +++- lib/request/group_chat.dart | 111 ++++- lib/router/contact_router.dart | 69 ++- .../invite_group_chat_member_screen.dart | 207 +++++++++ .../change_group_chat_intro_screen.dart | 134 ++++++ .../change_group_chat_name_screen.dart | 134 ++++++ .../change_group_chat_remark_screen.dart | 137 ++++++ .../change_my_remark_screen.dart | 135 ++++++ .../group_chat_profile_screen.dart | 433 +++++++++++++----- .../components/group_chat_message_bubble.dart | 20 +- .../message/group_chat_message_screen.dart | 9 +- .../change_sign_screen.dart | 16 +- 12 files changed, 1338 insertions(+), 148 deletions(-) create mode 100644 lib/screens/contact_add/invite_group_chat_member_screen.dart create mode 100644 lib/screens/group_chat_profile/change_group_chat_screen/change_group_chat_intro_screen.dart create mode 100644 lib/screens/group_chat_profile/change_group_chat_screen/change_group_chat_name_screen.dart create mode 100644 lib/screens/group_chat_profile/change_group_chat_screen/change_group_chat_remark_screen.dart create mode 100644 lib/screens/group_chat_profile/change_group_chat_screen/change_my_remark_screen.dart diff --git a/lib/models/contact_model.dart b/lib/models/contact_model.dart index 2b43d60..a1be77f 100644 --- a/lib/models/contact_model.dart +++ b/lib/models/contact_model.dart @@ -82,6 +82,29 @@ class Contact extends ChangeNotifier { groupChatCount += 1; notifyListeners(); } + + void changeGroupChatSetting( + String setting, + String groupChatId, + String newValue, + ) { + switch (setting) { + case 'nameRemark': + groupChats[groupChatId]!.nameRemark = newValue; + break; + case 'myRemark': + groupChats[groupChatId]!.myRemark = newValue; + break; + } + + notifyListeners(); + } + + void deleteGroupChat(String groupChatId) { + groupChats.remove(groupChatId); + groupChatCount--; + notifyListeners(); + } } class FriendAccountProfile { @@ -149,7 +172,8 @@ class GroupChatMemberNameAvatar { class ContactAccountProfile extends ChangeNotifier { Map friends = {}; Map groupChats = {}; - Map grouChatMemberProfiles = {}; + Map> grouChatMemberProfiles = + {}; void init(Map json) { json['friends'].forEach((key, value) { @@ -196,9 +220,56 @@ class ContactAccountProfile extends ChangeNotifier { notifyListeners(); } - void addGroupChatMemberProfile(String memberId, Map json) { - grouChatMemberProfiles.addAll( - {memberId: GroupChatMemberNameAvatar.fromJson(json)}, - ); + void addGroupChatMemberProfile( + String groupChatId, + String memberId, + Map json, + ) { + if (grouChatMemberProfiles.containsKey(groupChatId)) { + grouChatMemberProfiles[groupChatId]!.addAll( + {memberId: GroupChatMemberNameAvatar.fromJson(json)}, + ); + } else { + grouChatMemberProfiles[groupChatId] = { + memberId: GroupChatMemberNameAvatar.fromJson(json), + }; + } + } + + void refreshGroupChatMemberProfile( + String groupChatId, + Map json, + ) { + json.forEach((key, value) { + grouChatMemberProfiles[groupChatId]![key] = + GroupChatMemberNameAvatar.fromJson(value); + }); + } + + void addGroupChatMembers(String groupChatId, List members) { + groupChats[groupChatId]!.members.addAll(members); + notifyListeners(); + } + + void changeGroupChatProfile( + String profile, + String groupChatId, + String newValue, + ) { + switch (profile) { + case 'name': + groupChats[groupChatId]!.name = newValue; + break; + case 'intro': + groupChats[groupChatId]!.introduction = newValue; + break; + } + notifyListeners(); + } + + void deleteGroupChat(String groupChatId) { + groupChats.remove(groupChatId); + grouChatMemberProfiles.remove(groupChatId); + notifyListeners(); } } diff --git a/lib/request/group_chat.dart b/lib/request/group_chat.dart index d12ffba..e73c3d6 100644 --- a/lib/request/group_chat.dart +++ b/lib/request/group_chat.dart @@ -17,17 +17,116 @@ Future> createGroupChat( return response.data; } +Future> inviteMembers( + String groupChatId, + List members, +) async { + Response response = await request.post('/group_chat/invite_members', data: { + 'group_chat_id': groupChatId, + 'members': members, + }); + + return response.data; +} + Future> getGroupChatMemberNameAvatar( String groupChatId, String memberId, bool isFriend, ) async { - Response response = - await request.get('/group_chat/member_name_avatar', queryParameters: { - 'group_chat_id': groupChatId, - 'member_id': memberId, - 'is_friend': isFriend, - }); + Response response = await request.get( + '/group_chat/member_name_avatar', + queryParameters: { + 'group_chat_id': groupChatId, + 'member_id': memberId, + 'is_friend': isFriend, + }, + ); + + return response.data; +} + +Future> getGroupChatFullProfile(String groupChatId) async { + Response response = await request.get( + '/group_chat/full_profile', + queryParameters: {'group_chat_id': groupChatId}, + ); + + return response.data; +} + +Future> changeGroupChatName( + String groupChatId, + String newName, +) async { + Response response = await request.post( + '/group_chat/change_name', + queryParameters: { + 'group_chat_id': groupChatId, + 'new_name': newName, + }, + ); + + return response.data; +} + +Future> changeGroupChatIntro( + String groupChatId, + String newIntro, +) async { + Response response = await request.post( + '/group_chat/change_intro', + queryParameters: { + 'group_chat_id': groupChatId, + 'new_intro': newIntro, + }, + ); + + return response.data; +} + +Future> changeMyRemark( + String userId, + String groupChatId, + String newMyRemark, +) async { + Response response = await request.post( + '/group_chat/change_my_remark', + queryParameters: { + 'user_id': userId, + 'group_chat_id': groupChatId, + 'new_my_remark': newMyRemark, + }, + ); + + return response.data; +} + +Future> changeGroupChatRemark( + String userId, + String groupChatId, + String newRemark, +) async { + Response response = await request.post( + '/group_chat/change_remark', + queryParameters: { + 'user_id': userId, + 'group_chat_id': groupChatId, + 'new_remark': newRemark, + }, + ); + + return response.data; +} + +Future> deleteGroupChat( + String groupChatId, + List members, +) async { + Response response = await request.post( + '/group_chat/delete', + data: {'group_chat_id': groupChatId, 'members': members}, + ); return response.data; } diff --git a/lib/router/contact_router.dart b/lib/router/contact_router.dart index 44be648..331f521 100644 --- a/lib/router/contact_router.dart +++ b/lib/router/contact_router.dart @@ -5,7 +5,12 @@ import 'package:together_mobile/models/contact_model.dart'; import 'package:together_mobile/models/init_get_it.dart'; import 'package:together_mobile/screens/contact/contact_apply_screen/applicant_profile_screen/applicant_profile_screen.dart'; import 'package:together_mobile/screens/contact_add/create_group_chat_screen.dart'; +import 'package:together_mobile/screens/contact_add/invite_group_chat_member_screen.dart'; import 'package:together_mobile/screens/friend_profile/friend_setting_screen/friend_setting_screen.dart'; +import 'package:together_mobile/screens/group_chat_profile/change_group_chat_screen/change_group_chat_intro_screen.dart'; +import 'package:together_mobile/screens/group_chat_profile/change_group_chat_screen/change_group_chat_name_screen.dart'; +import 'package:together_mobile/screens/group_chat_profile/change_group_chat_screen/change_group_chat_remark_screen.dart'; +import 'package:together_mobile/screens/group_chat_profile/change_group_chat_screen/change_my_remark_screen.dart'; import 'router_key.dart'; import 'package:together_mobile/screens/contact/contact_apply_screen/apply_list_screen.dart'; @@ -22,8 +27,10 @@ final contactRouter = GoRoute( builder: (context, state) { if (state.queryParameters.isNotEmpty) { var deletedFriendId = state.queryParameters['deletedFriendId']; - getIt.get().removeFriend(deletedFriendId!); - getIt.get().removeFriend(deletedFriendId); + if (deletedFriendId != null) { + getIt.get().removeFriend(deletedFriendId); + getIt.get().removeFriend(deletedFriendId); + } } return ContactScreen(); }, @@ -118,7 +125,63 @@ final contactRouter = GoRoute( path: 'group_chat_profile', name: 'GroupChatProfile', parentNavigatorKey: rootNavigatorKey, - builder: (context, state) => const GroupChatProfileScreen(), + builder: (context, state) => GroupChatProfileScreen( + groupChatId: state.queryParameters['groupChatId']!, + ), + routes: [ + GoRoute( + path: 'invite_group_chat_member', + name: 'InviteGroupChatMember', + parentNavigatorKey: rootNavigatorKey, + builder: (context, state) => InviteGroupChatMemberScreen( + groupChatId: state.queryParameters['groupChatId']!, + ), + ), + GoRoute( + path: 'change_group_chat_name', + name: 'ChangeGroupChatName', + parentNavigatorKey: rootNavigatorKey, + builder: (context, state) { + return ChangeGroupChatNameScreen( + groupChatId: state.queryParameters['groupChatId']!, + name: state.queryParameters['name']!, + ); + }, + ), + GoRoute( + path: 'change_group_chat_intro', + name: 'ChangeGroupChatIntro', + parentNavigatorKey: rootNavigatorKey, + builder: (context, state) { + return ChangeGroupChatIntroScreen( + groupChatId: state.queryParameters['groupChatId']!, + intro: state.queryParameters['intro']!, + ); + }, + ), + GoRoute( + path: 'change_group_chat_remark', + name: 'ChangeGroupChatRemark', + parentNavigatorKey: rootNavigatorKey, + builder: (context, state) { + return ChangeGroupChatRemarkScreen( + groupChatId: state.queryParameters['groupChatId']!, + nameRemark: state.queryParameters['nameRemark']!, + ); + }, + ), + GoRoute( + path: 'change_my_remark', + name: 'ChangeMyRemark', + parentNavigatorKey: rootNavigatorKey, + builder: (context, state) { + return ChangeMyRemarkScreen( + groupChatId: state.queryParameters['groupChatId']!, + myRemark: state.queryParameters['myRemark']!, + ); + }, + ), + ], ), ], ); diff --git a/lib/screens/contact_add/invite_group_chat_member_screen.dart b/lib/screens/contact_add/invite_group_chat_member_screen.dart new file mode 100644 index 0000000..c9572a4 --- /dev/null +++ b/lib/screens/contact_add/invite_group_chat_member_screen.dart @@ -0,0 +1,207 @@ +import 'dart:convert'; + +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:cherry_toast/cherry_toast.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:together_mobile/common/constants.dart'; +import 'package:together_mobile/database/hive_database.dart'; +import 'package:together_mobile/models/contact_model.dart'; +import 'package:together_mobile/models/init_get_it.dart'; +import 'package:together_mobile/models/user_model.dart'; +import 'package:together_mobile/models/websocket_model.dart'; +import 'package:together_mobile/request/group_chat.dart'; +import 'package:together_mobile/request/server.dart'; +import 'package:together_mobile/screens/contact_add/components/custom_checkbox_tile.dart'; + +class InviteGroupChatMemberScreen extends StatefulWidget { + const InviteGroupChatMemberScreen({ + super.key, + required this.groupChatId, + }); + final String groupChatId; + + @override + State createState() => + _InviteGroupChatMemberScreenState(); +} + +class _InviteGroupChatMemberScreenState + extends State { + final List _checkedFriends = []; + final List _friendIds = getIt.get().friends.keys.toList(); + + @override + void initState() { + super.initState(); + var memberIds = getIt + .get() + .groupChats[widget.groupChatId]! + .members; + var myId = getIt.get().id; + _friendIds.removeWhere( + (element) => memberIds.contains(element) || element == myId); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + centerTitle: true, + title: const Text('邀请新成员'), + ), + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 10), + margin: const EdgeInsets.only(bottom: 20), + child: const TextField( + style: TextStyle(fontSize: 18), + textAlignVertical: TextAlignVertical.center, + decoration: InputDecoration( + isCollapsed: true, + prefixIcon: Icon(Icons.search), + hintText: '搜索好友', + ), + ), + ), + if (_checkedFriends.isNotEmpty) ...[ + const Padding( + padding: EdgeInsets.fromLTRB(15, 0, 0, 10), + child: Text('已选择的好友'), + ), + Container( + padding: const EdgeInsets.fromLTRB(15, 10, 0, 10), + height: 80, + child: ListView.builder( + itemBuilder: (context, index) { + String friendId = _checkedFriends[index]; + String avatar = getIt + .get() + .friends[friendId]! + .avatar; + String name = getIt + .get() + .friends[friendId]! + .friendRemark + .isNotEmpty + ? getIt.get().friends[friendId]!.friendRemark + : getIt + .get() + .friends[friendId]! + .nickname; + return GestureDetector( + onTap: () { + setState(() { + _checkedFriends.remove(friendId); + }); + }, + child: SizedBox( + width: 70, + child: Column( + children: [ + avatar.isEmpty + ? const CircleAvatar( + backgroundImage: + AssetImage('assets/images/user_2.png'), + ) + : CircleAvatar( + backgroundImage: CachedNetworkImageProvider( + '$avatarsUrl/$avatar'), + ), + Text( + name, + overflow: TextOverflow.ellipsis, + ), + ], + ), + ), + ); + }, + itemCount: _checkedFriends.length, + scrollDirection: Axis.horizontal, + ), + ), + ], + Expanded( + child: ListView.builder( + itemBuilder: (context, index) { + return CustomCheckBoxTile( + index: index, + friendId: _friendIds[index], + value: _checkedFriends.contains(_friendIds[index]), + onChange: _onchange, + ); + }, + itemCount: _friendIds.length, + ), + ), + Container( + alignment: Alignment.center, + height: 60, + decoration: BoxDecoration( + color: Colors.grey.withOpacity(0.2), + ), + child: FilledButton( + onPressed: () async { + _invite(context); + }, + style: FilledButton.styleFrom( + fixedSize: const Size(390, 45), + backgroundColor: _checkedFriends.isNotEmpty + ? kPrimaryColor + : kPrimaryColor.withAlpha(150), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + ), + child: Text( + '立即邀请(${_checkedFriends.length})', + style: const TextStyle( + fontSize: 18, + letterSpacing: 5, + ), + ), + ), + ), + ], + ), + ); + } + + void _onchange(bool value, String friendId) { + setState(() { + if (value) { + _checkedFriends.add(friendId); + } else { + _checkedFriends.remove(friendId); + } + }); + } + + void _invite(BuildContext context) async { + if (_checkedFriends.isEmpty) { + return; + } + + var res = await inviteMembers(widget.groupChatId, _checkedFriends); + + if (res['code'] == 10800) { + getIt.get().addGroupChatMembers( + widget.groupChatId, + _checkedFriends, + ); + // ignore: use_build_context_synchronously + CherryToast.success( + title: const Text('邀请成功'), + ).show(context); + + // Future.delayed( + // const Duration(milliseconds: 500), + // () => context.pop(), + // ); + // TODO: send a notification on Message screen that some users are invited + } + } +} diff --git a/lib/screens/group_chat_profile/change_group_chat_screen/change_group_chat_intro_screen.dart b/lib/screens/group_chat_profile/change_group_chat_screen/change_group_chat_intro_screen.dart new file mode 100644 index 0000000..8856331 --- /dev/null +++ b/lib/screens/group_chat_profile/change_group_chat_screen/change_group_chat_intro_screen.dart @@ -0,0 +1,134 @@ +import 'package:flutter/material.dart'; + +import 'package:cherry_toast/cherry_toast.dart'; + +import 'package:together_mobile/common/constants.dart'; +import 'package:together_mobile/models/contact_model.dart'; +import 'package:together_mobile/models/init_get_it.dart'; +import 'package:together_mobile/request/group_chat.dart'; + +class ChangeGroupChatIntroScreen extends StatefulWidget { + const ChangeGroupChatIntroScreen({ + super.key, + required this.groupChatId, + required this.intro, + }); + + final String groupChatId, intro; + + @override + State createState() => + _ChangeGroupChatIntroScreenState(); +} + +class _ChangeGroupChatIntroScreenState + extends State { + final TextEditingController _controller = TextEditingController(); + bool _isChanged = false; + + @override + void initState() { + super.initState(); + _controller.text = widget.intro; + } + + @override + void dispose() { + super.dispose(); + _controller.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + centerTitle: true, + title: const Text('群聊简介'), + ), + body: Column( + children: [ + Container( + // padding: const EdgeInsets.all(5), + margin: const EdgeInsets.fromLTRB(5, 20, 5, 40), + child: TextField( + cursorHeight: 24, + minLines: 5, + maxLines: 5, + controller: _controller, + onChanged: (value) { + if (value != widget.intro) { + setState(() { + _isChanged = true; + }); + } else { + setState(() { + _isChanged = false; + }); + } + }, + decoration: const InputDecoration( + border: OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(8)), + ), + ), + style: const TextStyle( + fontSize: 17, + height: 1.5, + ), + ), + ), + Container( + alignment: Alignment.center, + child: FilledButton( + onPressed: () async { + if (!_isChanged) { + return; + } + + Map res = await changeGroupChatIntro( + widget.groupChatId, + _controller.text, + ); + if (res['code'] == 10800) { + // ignore: use_build_context_synchronously + CherryToast.success( + title: const Text('更改群聊简介成功'), + animationDuration: const Duration(seconds: 1), + toastDuration: const Duration(seconds: 2), + ).show(context); + + getIt.get().changeGroupChatProfile( + 'intro', + widget.groupChatId, + _controller.text, + ); + + setState(() { + _isChanged = false; + }); + } + }, + style: FilledButton.styleFrom( + backgroundColor: _isChanged + ? kPrimaryColor + : Theme.of(context).brightness == Brightness.dark + ? kUnActivatedColor + : kUnAvailableColor, + fixedSize: const Size(150, 40), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + child: const Text( + '保存', + style: TextStyle( + fontSize: 16, + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/screens/group_chat_profile/change_group_chat_screen/change_group_chat_name_screen.dart b/lib/screens/group_chat_profile/change_group_chat_screen/change_group_chat_name_screen.dart new file mode 100644 index 0000000..fa10103 --- /dev/null +++ b/lib/screens/group_chat_profile/change_group_chat_screen/change_group_chat_name_screen.dart @@ -0,0 +1,134 @@ +import 'package:flutter/material.dart'; + +import 'package:cherry_toast/cherry_toast.dart'; + +import 'package:together_mobile/common/constants.dart'; +import 'package:together_mobile/models/contact_model.dart'; +import 'package:together_mobile/models/init_get_it.dart'; +import 'package:together_mobile/request/group_chat.dart'; + +class ChangeGroupChatNameScreen extends StatefulWidget { + const ChangeGroupChatNameScreen({ + super.key, + required this.groupChatId, + required this.name, + }); + + final String groupChatId, name; + + @override + State createState() => + _ChangeGroupChatNameScreenState(); +} + +class _ChangeGroupChatNameScreenState extends State { + final TextEditingController _controller = TextEditingController(); + bool _isChanged = false; + + @override + void initState() { + super.initState(); + _controller.text = widget.name; + } + + @override + void dispose() { + super.dispose(); + _controller.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + centerTitle: true, + title: const Text('群聊名称'), + ), + body: Container( + // height: 80, + padding: const EdgeInsets.symmetric(horizontal: 5), + decoration: const BoxDecoration( + border: Border( + bottom: BorderSide( + width: 1, + color: kUnActivatedColor, + ), + ), + ), + child: Row( + children: [ + Expanded( + child: TextField( + onChanged: (value) { + if (value != widget.name && value.isNotEmpty) { + setState(() { + _isChanged = true; + }); + } else { + setState(() { + _isChanged = false; + }); + } + }, + controller: _controller, + style: const TextStyle( + height: 1.5, + fontSize: 20, + ), + cursorHeight: 32, + decoration: const InputDecoration( + isCollapsed: true, + contentPadding: EdgeInsets.only(top: 10, bottom: 8), + border: UnderlineInputBorder(borderSide: BorderSide.none), + ), + ), + ), + const SizedBox( + width: 10, + ), + FilledButton( + onPressed: () async { + if (!_isChanged) { + return; + } + + final res = await changeGroupChatName( + widget.groupChatId, _controller.text); + if (res['code'] == 10800) { + // ignore: use_build_context_synchronously + CherryToast.success( + title: const Text('已更改群聊名称'), + ).show(context); + + getIt.get().changeGroupChatProfile( + 'name', + widget.groupChatId, + _controller.text, + ); + + // TODO: use websocket to notify members that group name has been change + + setState(() { + _isChanged = false; + }); + } + }, + style: FilledButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + padding: EdgeInsets.zero, + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + backgroundColor: _isChanged ? kPrimaryColor : kUnAvailableColor, + ), + child: const Text( + '保存', + style: TextStyle(fontSize: 16), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/screens/group_chat_profile/change_group_chat_screen/change_group_chat_remark_screen.dart b/lib/screens/group_chat_profile/change_group_chat_screen/change_group_chat_remark_screen.dart new file mode 100644 index 0000000..167a806 --- /dev/null +++ b/lib/screens/group_chat_profile/change_group_chat_screen/change_group_chat_remark_screen.dart @@ -0,0 +1,137 @@ +import 'package:flutter/material.dart'; + +import 'package:cherry_toast/cherry_toast.dart'; + +import 'package:together_mobile/common/constants.dart'; +import 'package:together_mobile/models/contact_model.dart'; +import 'package:together_mobile/models/init_get_it.dart'; +import 'package:together_mobile/models/user_model.dart'; +import 'package:together_mobile/request/group_chat.dart'; + +class ChangeGroupChatRemarkScreen extends StatefulWidget { + const ChangeGroupChatRemarkScreen({ + super.key, + required this.groupChatId, + required this.nameRemark, + }); + + final String groupChatId, nameRemark; + + @override + State createState() => + _ChangeGroupChatRemarkScreenState(); +} + +class _ChangeGroupChatRemarkScreenState + extends State { + final TextEditingController _controller = TextEditingController(); + bool _isChanged = false; + + @override + void initState() { + super.initState(); + _controller.text = widget.nameRemark; + } + + @override + void dispose() { + super.dispose(); + _controller.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + centerTitle: true, + title: const Text('群聊备注'), + ), + body: Container( + // height: 80, + padding: const EdgeInsets.symmetric(horizontal: 5), + decoration: const BoxDecoration( + border: Border( + bottom: BorderSide( + width: 1, + color: kUnActivatedColor, + ), + ), + ), + child: Row( + children: [ + Expanded( + child: TextField( + onChanged: (value) { + if (value != widget.nameRemark) { + setState(() { + _isChanged = true; + }); + } else { + setState(() { + _isChanged = false; + }); + } + }, + controller: _controller, + style: const TextStyle( + height: 1.5, + fontSize: 20, + ), + cursorHeight: 32, + decoration: const InputDecoration( + isCollapsed: true, + contentPadding: EdgeInsets.only(top: 10, bottom: 8), + border: UnderlineInputBorder(borderSide: BorderSide.none), + ), + ), + ), + const SizedBox( + width: 10, + ), + FilledButton( + onPressed: () async { + if (!_isChanged) { + return; + } + + final res = await changeGroupChatRemark( + getIt.get().id, + widget.groupChatId, + _controller.text, + ); + if (res['code'] == 10800) { + // ignore: use_build_context_synchronously + CherryToast.success( + title: const Text('已更改群聊备注'), + ).show(context); + + getIt.get().changeGroupChatSetting( + 'nameRemark', + widget.groupChatId, + _controller.text, + ); + + setState(() { + _isChanged = false; + }); + } + }, + style: FilledButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + padding: EdgeInsets.zero, + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + backgroundColor: _isChanged ? kPrimaryColor : kUnAvailableColor, + ), + child: const Text( + '保存', + style: TextStyle(fontSize: 16), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/screens/group_chat_profile/change_group_chat_screen/change_my_remark_screen.dart b/lib/screens/group_chat_profile/change_group_chat_screen/change_my_remark_screen.dart new file mode 100644 index 0000000..6a6adce --- /dev/null +++ b/lib/screens/group_chat_profile/change_group_chat_screen/change_my_remark_screen.dart @@ -0,0 +1,135 @@ +import 'package:flutter/material.dart'; + +import 'package:cherry_toast/cherry_toast.dart'; + +import 'package:together_mobile/common/constants.dart'; +import 'package:together_mobile/models/contact_model.dart'; +import 'package:together_mobile/models/init_get_it.dart'; +import 'package:together_mobile/models/user_model.dart'; +import 'package:together_mobile/request/group_chat.dart'; + +class ChangeMyRemarkScreen extends StatefulWidget { + const ChangeMyRemarkScreen({ + super.key, + required this.groupChatId, + required this.myRemark, + }); + + final String groupChatId, myRemark; + + @override + State createState() => _ChangeMyRemarkScreenState(); +} + +class _ChangeMyRemarkScreenState extends State { + final TextEditingController _controller = TextEditingController(); + bool _isChanged = false; + + @override + void initState() { + super.initState(); + _controller.text = widget.myRemark; + } + + @override + void dispose() { + super.dispose(); + _controller.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + centerTitle: true, + title: const Text('我的群聊昵称'), + ), + body: Container( + // height: 80, + padding: const EdgeInsets.symmetric(horizontal: 5), + decoration: const BoxDecoration( + border: Border( + bottom: BorderSide( + width: 1, + color: kUnActivatedColor, + ), + ), + ), + child: Row( + children: [ + Expanded( + child: TextField( + onChanged: (value) { + if (value != widget.myRemark && value.isNotEmpty) { + setState(() { + _isChanged = true; + }); + } else { + setState(() { + _isChanged = false; + }); + } + }, + controller: _controller, + style: const TextStyle( + height: 1.5, + fontSize: 20, + ), + cursorHeight: 32, + decoration: const InputDecoration( + isCollapsed: true, + contentPadding: EdgeInsets.only(top: 10, bottom: 8), + border: UnderlineInputBorder(borderSide: BorderSide.none), + ), + ), + ), + const SizedBox( + width: 10, + ), + FilledButton( + onPressed: () async { + if (!_isChanged) { + return; + } + + final res = await changeMyRemark( + getIt.get().id, + widget.groupChatId, + _controller.text, + ); + if (res['code'] == 10800) { + // ignore: use_build_context_synchronously + CherryToast.success( + title: const Text('已更我的群聊昵称'), + ).show(context); + + getIt.get().changeGroupChatSetting( + 'myRemark', + widget.groupChatId, + _controller.text, + ); + + setState(() { + _isChanged = false; + }); + } + }, + style: FilledButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + padding: EdgeInsets.zero, + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + backgroundColor: _isChanged ? kPrimaryColor : kUnAvailableColor, + ), + child: const Text( + '保存', + style: TextStyle(fontSize: 16), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/screens/group_chat_profile/group_chat_profile_screen.dart b/lib/screens/group_chat_profile/group_chat_profile_screen.dart index af228d2..1ed02ca 100755 --- a/lib/screens/group_chat_profile/group_chat_profile_screen.dart +++ b/lib/screens/group_chat_profile/group_chat_profile_screen.dart @@ -1,11 +1,45 @@ import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:hive_flutter/hive_flutter.dart'; import 'package:together_mobile/common/constants.dart'; +import 'package:together_mobile/database/box_type.dart'; +import 'package:together_mobile/models/contact_model.dart'; +import 'package:together_mobile/models/init_get_it.dart'; +import 'package:together_mobile/models/user_model.dart'; +import 'package:together_mobile/request/group_chat.dart'; import 'package:together_mobile/screens/group_chat_profile/components/group_chat_profile_header.dart'; import 'components/group_chat_profile_tile.dart'; -class GroupChatProfileScreen extends StatelessWidget { - const GroupChatProfileScreen({super.key}); +class GroupChatProfileScreen extends StatefulWidget { + const GroupChatProfileScreen({ + super.key, + required this.groupChatId, + }); + + final String groupChatId; + + @override + State createState() => _GroupChatProfileScreenState(); +} + +class _GroupChatProfileScreenState extends State { + Future _getGroupChatFullProfile() async { + Map res = + await getGroupChatFullProfile(widget.groupChatId); + + getIt.get().addGroupChatProfile( + widget.groupChatId, + res['groupChat'], + ); + + getIt.get().refreshGroupChatMemberProfile( + widget.groupChatId, + res['memberNameAvatar'], + ); + + return Future(() => true); + } @override Widget build(BuildContext context) { @@ -15,143 +49,304 @@ class GroupChatProfileScreen extends StatelessWidget { title: const Text('群聊信息'), actions: [ IconButton( - onPressed: () {}, + onPressed: () { + context.pushNamed( + 'InviteGroupChatMember', + queryParameters: {'groupChatId': widget.groupChatId}, + ); + }, icon: const Icon(Icons.group_add), splashRadius: 20, ), ], ), - body: CustomScrollView( - physics: const BouncingScrollPhysics( - parent: AlwaysScrollableScrollPhysics(), - ), - slivers: [ - const SliverPadding( - padding: EdgeInsets.symmetric(vertical: 5.0), - ), - SliverToBoxAdapter( - child: ListTile( - onTap: () {}, - leading: const CircleAvatar( - backgroundImage: AssetImage('assets/images/Logo_light.png'), - ), - title: const Text( - '群聊名称', - style: TextStyle(fontSize: 18), - ), - subtitle: Text('123478352'), - trailing: const Icon( - Icons.keyboard_arrow_right, - size: 28, - ), + body: FutureBuilder( + future: _getGroupChatFullProfile(), + builder: (context, snapshot) { + return CustomScrollView( + physics: const BouncingScrollPhysics( + parent: AlwaysScrollableScrollPhysics(), ), - ), - const GroupChatProfileHeader( - header: '群聊资料', - ), - GroupChatProfileTile( - onTap: () {}, - title: '群聊成员', - info: '共20人', - ), - GroupChatProfileTile( - onTap: () {}, - title: '群聊名称', - info: '的飞机的数量', - ), - GroupChatProfileTile( - onTap: () {}, - title: '群聊简介', - info: '凤凰男撒赖扩大就费解的是离开房间电视机分厘卡觉得是分开按时灯笼裤你发哪里看是否能', - ), - GroupChatProfileTile( - onTap: () {}, - title: '群聊标签', - info: 'Program|Python|Rust|前端', - ), - const GroupChatProfileHeader( - header: '管理群聊', - ), - GroupChatProfileTile(onTap: () {}, title: '管理员', info: ''), - SliverToBoxAdapter( - child: InkWell( - onTap: () {}, - child: const Padding( - padding: EdgeInsets.fromLTRB(20, 15, 15, 15), - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + slivers: [ + const SliverPadding( + padding: EdgeInsets.symmetric(vertical: 5.0), + ), + SliverToBoxAdapter( + child: ListTile( + onTap: () {}, + leading: const CircleAvatar( + backgroundImage: AssetImage('assets/images/Logo_light.png'), + ), + title: Text( + getIt + .get() + .groupChats[widget.groupChatId]! + .nameRemark + .isEmpty + ? getIt + .get() + .groupChats[widget.groupChatId]! + .name + : getIt + .get() + .groupChats[widget.groupChatId]! + .nameRemark, + style: const TextStyle(fontSize: 18), + ), + subtitle: Text(widget.groupChatId), + trailing: const Icon( + Icons.keyboard_arrow_right, + size: 28, + ), + ), + ), + const GroupChatProfileHeader( + header: '群聊资料', + ), + GroupChatProfileTile( + onTap: () {}, + title: '群聊成员', + info: + '共 ${getIt.get().groupChats[widget.groupChatId]!.members.length} 人', + ), + GroupChatProfileTile( + onTap: () { + final query = { + 'groupChatId': widget.groupChatId, + 'name': getIt + .get() + .groupChats[widget.groupChatId]! + .name, + }; + context.pushNamed( + 'ChangeGroupChatName', + queryParameters: query, + ); + }, + title: '群聊名称', + info: getIt + .get() + .groupChats[widget.groupChatId]! + .name, + ), + GroupChatProfileTile( + onTap: () { + final query = { + 'groupChatId': widget.groupChatId, + 'intro': getIt + .get() + .groupChats[widget.groupChatId]! + .introduction, + }; + context.pushNamed( + 'ChangeGroupChatIntro', + queryParameters: query, + ); + }, + title: '群聊简介', + info: getIt + .get() + .groupChats[widget.groupChatId]! + .introduction, + ), + GroupChatProfileTile( + onTap: () {}, + title: '群聊标签', + info: getIt + .get() + .groupChats[widget.groupChatId]! + .tags + .join('|'), + ), + const GroupChatProfileHeader( + header: '管理群聊', + ), + GroupChatProfileTile(onTap: () {}, title: '管理员', info: ''), + SliverToBoxAdapter( + child: InkWell( + onTap: () {}, + child: const Padding( + padding: EdgeInsets.fromLTRB(20, 15, 15, 15), + child: Column( children: [ - Text( - '群聊公告', - style: TextStyle( - fontSize: 18, - ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '群聊公告', + style: TextStyle( + fontSize: 18, + ), + ), + Icon( + Icons.keyboard_arrow_right, + size: 28, + color: kUnActivatedColor, + ), + ], ), - Icon( - Icons.keyboard_arrow_right, - size: 28, - color: kUnActivatedColor, + Text( + '', + maxLines: 4, + style: TextStyle( + overflow: TextOverflow.ellipsis, + color: kUnActivatedColor, + ), ), ], ), - Text( - '等哈说分段收费和得了是否回到覅会收到回复你尽快回到i丰厚的是念佛i和你说的更多十六分你回来第三方看来大家NSF刻录机了了解多少就发了多少积分看了觉得十分了解了多少积分;了解的JFK了多少积分多少积分雕刻技法打扫房间打扫房间多少积分撒旦解放劳动纠纷了多少积分地方第三方就多了几分鲁大师就打扫房间但是螺丝钉解放扣税的发第三方第三方但是考了几分的时间分厘卡电视机发凉快多了几分考虑到发', - maxLines: 4, - style: TextStyle( - overflow: TextOverflow.ellipsis, - color: kUnActivatedColor, - ), - ), - ], + ), ), ), - ), - ), - const GroupChatProfileHeader( - header: '群聊资料', - ), - GroupChatProfileTile( - onTap: () {}, - title: '我的群聊昵称', - info: '迪恩二级我', - ), - GroupChatProfileTile( - onTap: () {}, - title: '我的群聊标签', - info: 'dfsha', - ), - const GroupChatProfileHeader( - header: '聊天记录', - ), - GroupChatProfileTile( - onTap: () {}, - title: '查找聊天记录', - info: '图片、视频、音频、文件等', - ), - GroupChatProfileTile( - onTap: () {}, - title: '删除聊天记录', - info: '', - ), - SliverToBoxAdapter( - child: TextButton( - onPressed: () {}, - style: TextButton.styleFrom( - foregroundColor: kErrorColor, + const GroupChatProfileHeader( + header: '群聊资料', ), + GroupChatProfileTile( + onTap: () { + final query = { + 'groupChatId': widget.groupChatId, + 'myRemark': getIt + .get() + .groupChats[widget.groupChatId]! + .myRemark, + }; + context.pushNamed( + 'ChangeMyRemark', + queryParameters: query, + ); + }, + title: '我的群聊昵称', + info: getIt + .get() + .groupChats[widget.groupChatId]! + .myRemark, + ), + GroupChatProfileTile( + onTap: () { + final query = { + 'groupChatId': widget.groupChatId, + 'nameRemark': getIt + .get() + .groupChats[widget.groupChatId]! + .nameRemark, + }; + context.pushNamed( + 'ChangeGroupChatRemark', + queryParameters: query, + ); + }, + title: '我的群聊备注', + info: getIt + .get() + .groupChats[widget.groupChatId]! + .nameRemark, + ), + const GroupChatProfileHeader( + header: '聊天记录', + ), + GroupChatProfileTile( + onTap: () {}, + title: '查找聊天记录', + info: '图片、视频、音频、文件等', + ), + GroupChatProfileTile( + onTap: () {}, + title: '删除聊天记录', + info: '', + ), + if (getIt.get().id == + getIt + .get() + .groupChats[widget.groupChatId]! + .supervisor) + SliverToBoxAdapter( + child: TextButton( + onPressed: () async { + _deleteGroupChat(context); + }, + style: TextButton.styleFrom( + foregroundColor: kErrorColor, + ), + child: const Text( + '解散群聊', + style: TextStyle( + fontSize: 18, + color: kErrorColor, + ), + ), + ), + ), + ], + ); + }, + ), + ); + } + + void _deleteGroupChat(BuildContext context) async { + int choose = await showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: const Text('解散群聊'), + content: const Text('确认解散群聊吗?\n解散之后聊天记录将被删除'), + actions: [ + TextButton( + onPressed: () { + context.pop(0); + }, + child: const Text('取消'), + ), + TextButton( + onPressed: () { + context.pop(1); + }, child: const Text( - '解散群聊', + '解散', style: TextStyle( - fontSize: 18, color: kErrorColor, ), ), ), - ), - ], - ), + ], + ); + }, ); + + if (choose == 1) { + final res = await deleteGroupChat( + widget.groupChatId, + getIt + .get() + .groupChats[widget.groupChatId]! + .members, + ); + + if (res['code'] == 10800) { + // ignore: use_build_context_synchronously + context.go('/chat'); + + Future.delayed( + const Duration(milliseconds: 500), + () async { + getIt.get().deleteGroupChat(widget.groupChatId); + getIt + .get() + .deleteGroupChat(widget.groupChatId); + + late Box chatSettingBox; + + try { + chatSettingBox = Hive.box('chat_setting'); + } catch (_) { + chatSettingBox = await Hive.openBox('chatSetting'); + } + + chatSettingBox.delete(widget.groupChatId); + Hive.deleteBoxFromDisk('message_${widget.groupChatId}'); + }, + ); + } + } } } diff --git a/lib/screens/message/components/group_chat_message_bubble.dart b/lib/screens/message/components/group_chat_message_bubble.dart index 3184af7..8ec07be 100755 --- a/lib/screens/message/components/group_chat_message_bubble.dart +++ b/lib/screens/message/components/group_chat_message_bubble.dart @@ -42,7 +42,7 @@ class _GroupChatMessageBubbleState extends State { .get() .grouChatMemberProfiles .containsKey(widget.senderId)) { - // myself + // myself or already have profile return Future(() => true); } else { Map res; @@ -61,6 +61,7 @@ class _GroupChatMessageBubbleState extends State { ); } getIt.get().addGroupChatMemberProfile( + widget.contactId, widget.senderId, res['data'], ); @@ -216,8 +217,8 @@ class _GroupChatMessageBubbleState extends State { .isEmpty ? getIt .get() - .grouChatMemberProfiles[ - widget.senderId]! + .grouChatMemberProfiles[widget + .contactId]![widget.senderId]! .remark .isEmpty ? Text( @@ -230,6 +231,7 @@ class _GroupChatMessageBubbleState extends State { getIt .get() .grouChatMemberProfiles[ + widget.contactId]![ widget.senderId]! .remark, ) @@ -242,22 +244,22 @@ class _GroupChatMessageBubbleState extends State { } else { return getIt .get() - .grouChatMemberProfiles[ - widget.senderId]! + .grouChatMemberProfiles[widget + .contactId]![widget.senderId]! .remark .isEmpty ? Text( getIt .get() - .grouChatMemberProfiles[ - widget.senderId]! + .grouChatMemberProfiles[widget + .contactId]![widget.senderId]! .nickname, ) : Text( getIt .get() - .grouChatMemberProfiles[ - widget.senderId]! + .grouChatMemberProfiles[widget + .contactId]![widget.senderId]! .remark, ); } diff --git a/lib/screens/message/group_chat_message_screen.dart b/lib/screens/message/group_chat_message_screen.dart index ab0e3f7..a936780 100755 --- a/lib/screens/message/group_chat_message_screen.dart +++ b/lib/screens/message/group_chat_message_screen.dart @@ -61,7 +61,14 @@ class _GroupChatMessageScreenState extends State { centerTitle: true, actions: [ IconButton( - onPressed: () {}, + onPressed: () { + context.pushNamed( + 'GroupChatProfile', + queryParameters: { + 'groupChatId': widget.groupChatId, + }, + ); + }, icon: const Icon(Icons.menu), splashRadius: 18, ), diff --git a/lib/screens/more/profile_screen/change_profile_screens/change_sign_screen.dart b/lib/screens/more/profile_screen/change_profile_screens/change_sign_screen.dart index 4713f20..7389b6d 100755 --- a/lib/screens/more/profile_screen/change_profile_screens/change_sign_screen.dart +++ b/lib/screens/more/profile_screen/change_profile_screens/change_sign_screen.dart @@ -15,13 +15,19 @@ class ChangeSignScreen extends StatefulWidget { } class _ChangeSignScreenState extends State { - TextEditingController controller = TextEditingController(); + final TextEditingController _controller = TextEditingController(); bool _isChanged = false; @override void initState() { super.initState(); - controller.text = getIt.get().sign; + _controller.text = getIt.get().sign; + } + + @override + void dispose() { + super.dispose(); + _controller.dispose(); } @override @@ -40,7 +46,7 @@ class _ChangeSignScreenState extends State { cursorHeight: 24, minLines: 5, maxLines: 5, - controller: controller, + controller: _controller, onChanged: (value) { if (value.isEmpty || value == getIt.get().sign) { setState(() { @@ -73,7 +79,7 @@ class _ChangeSignScreenState extends State { Map res = await changeSign( getIt.get().id, - controller.text, + _controller.text, ); if (res['code'] == 10300) { // ignore: use_build_context_synchronously @@ -83,7 +89,7 @@ class _ChangeSignScreenState extends State { toastDuration: const Duration(seconds: 2), ).show(context); - getIt.get().updateSign(controller.text); + getIt.get().updateSign(_controller.text); setState(() { _isChanged = false;