change group chat profile v1

main
htylight 2023-08-27 10:18:31 +08:00
parent 913ec02601
commit 6367125961
12 changed files with 1338 additions and 148 deletions

View File

@ -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<String, FriendAccountProfile> friends = {};
Map<String, GroupChatProfile> groupChats = {};
Map<String, GroupChatMemberNameAvatar> grouChatMemberProfiles = {};
Map<String, Map<String, GroupChatMemberNameAvatar>> grouChatMemberProfiles =
{};
void init(Map json) {
json['friends'].forEach((key, value) {
@ -196,9 +220,56 @@ class ContactAccountProfile extends ChangeNotifier {
notifyListeners();
}
void addGroupChatMemberProfile(String memberId, Map<String, dynamic> json) {
grouChatMemberProfiles.addAll(
{memberId: GroupChatMemberNameAvatar.fromJson(json)},
);
void addGroupChatMemberProfile(
String groupChatId,
String memberId,
Map<String, dynamic> json,
) {
if (grouChatMemberProfiles.containsKey(groupChatId)) {
grouChatMemberProfiles[groupChatId]!.addAll(
{memberId: GroupChatMemberNameAvatar.fromJson(json)},
);
} else {
grouChatMemberProfiles[groupChatId] = {
memberId: GroupChatMemberNameAvatar.fromJson(json),
};
}
}
void refreshGroupChatMemberProfile(
String groupChatId,
Map<String, dynamic> json,
) {
json.forEach((key, value) {
grouChatMemberProfiles[groupChatId]![key] =
GroupChatMemberNameAvatar.fromJson(value);
});
}
void addGroupChatMembers(String groupChatId, List<String> 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();
}
}

View File

@ -17,17 +17,116 @@ Future<Map<String, dynamic>> createGroupChat(
return response.data;
}
Future<Map<String, dynamic>> inviteMembers(
String groupChatId,
List<String> members,
) async {
Response response = await request.post('/group_chat/invite_members', data: {
'group_chat_id': groupChatId,
'members': members,
});
return response.data;
}
Future<Map<String, dynamic>> 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<Map<String, dynamic>> getGroupChatFullProfile(String groupChatId) async {
Response response = await request.get(
'/group_chat/full_profile',
queryParameters: {'group_chat_id': groupChatId},
);
return response.data;
}
Future<Map<String, dynamic>> 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<Map<String, dynamic>> 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<Map<String, dynamic>> 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<Map<String, dynamic>> 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<Map<String, dynamic>> deleteGroupChat(
String groupChatId,
List<String> members,
) async {
Response response = await request.post(
'/group_chat/delete',
data: {'group_chat_id': groupChatId, 'members': members},
);
return response.data;
}

View File

@ -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<Contact>().removeFriend(deletedFriendId!);
getIt.get<ContactAccountProfile>().removeFriend(deletedFriendId);
if (deletedFriendId != null) {
getIt.get<Contact>().removeFriend(deletedFriendId);
getIt.get<ContactAccountProfile>().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']!,
);
},
),
],
),
],
);

View File

@ -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<InviteGroupChatMemberScreen> createState() =>
_InviteGroupChatMemberScreenState();
}
class _InviteGroupChatMemberScreenState
extends State<InviteGroupChatMemberScreen> {
final List<String> _checkedFriends = [];
final List<String> _friendIds = getIt.get<Contact>().friends.keys.toList();
@override
void initState() {
super.initState();
var memberIds = getIt
.get<ContactAccountProfile>()
.groupChats[widget.groupChatId]!
.members;
var myId = getIt.get<UserAccount>().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<ContactAccountProfile>()
.friends[friendId]!
.avatar;
String name = getIt
.get<Contact>()
.friends[friendId]!
.friendRemark
.isNotEmpty
? getIt.get<Contact>().friends[friendId]!.friendRemark
: getIt
.get<ContactAccountProfile>()
.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<ContactAccountProfile>().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
}
}
}

View File

@ -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<ChangeGroupChatIntroScreen> createState() =>
_ChangeGroupChatIntroScreenState();
}
class _ChangeGroupChatIntroScreenState
extends State<ChangeGroupChatIntroScreen> {
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<String, dynamic> 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<ContactAccountProfile>().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,
),
),
),
),
],
),
);
}
}

View File

@ -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<ChangeGroupChatNameScreen> createState() =>
_ChangeGroupChatNameScreenState();
}
class _ChangeGroupChatNameScreenState extends State<ChangeGroupChatNameScreen> {
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<ContactAccountProfile>().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),
),
),
],
),
),
);
}
}

View File

@ -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<ChangeGroupChatRemarkScreen> createState() =>
_ChangeGroupChatRemarkScreenState();
}
class _ChangeGroupChatRemarkScreenState
extends State<ChangeGroupChatRemarkScreen> {
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<UserAccount>().id,
widget.groupChatId,
_controller.text,
);
if (res['code'] == 10800) {
// ignore: use_build_context_synchronously
CherryToast.success(
title: const Text('已更改群聊备注'),
).show(context);
getIt.get<Contact>().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),
),
),
],
),
),
);
}
}

View File

@ -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<ChangeMyRemarkScreen> createState() => _ChangeMyRemarkScreenState();
}
class _ChangeMyRemarkScreenState extends State<ChangeMyRemarkScreen> {
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<UserAccount>().id,
widget.groupChatId,
_controller.text,
);
if (res['code'] == 10800) {
// ignore: use_build_context_synchronously
CherryToast.success(
title: const Text('已更我的群聊昵称'),
).show(context);
getIt.get<Contact>().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),
),
),
],
),
),
);
}
}

View File

@ -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<GroupChatProfileScreen> createState() => _GroupChatProfileScreenState();
}
class _GroupChatProfileScreenState extends State<GroupChatProfileScreen> {
Future<bool> _getGroupChatFullProfile() async {
Map<String, dynamic> res =
await getGroupChatFullProfile(widget.groupChatId);
getIt.get<ContactAccountProfile>().addGroupChatProfile(
widget.groupChatId,
res['groupChat'],
);
getIt.get<ContactAccountProfile>().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<Contact>()
.groupChats[widget.groupChatId]!
.nameRemark
.isEmpty
? getIt
.get<ContactAccountProfile>()
.groupChats[widget.groupChatId]!
.name
: getIt
.get<Contact>()
.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<ContactAccountProfile>().groupChats[widget.groupChatId]!.members.length}',
),
GroupChatProfileTile(
onTap: () {
final query = {
'groupChatId': widget.groupChatId,
'name': getIt
.get<ContactAccountProfile>()
.groupChats[widget.groupChatId]!
.name,
};
context.pushNamed(
'ChangeGroupChatName',
queryParameters: query,
);
},
title: '群聊名称',
info: getIt
.get<ContactAccountProfile>()
.groupChats[widget.groupChatId]!
.name,
),
GroupChatProfileTile(
onTap: () {
final query = {
'groupChatId': widget.groupChatId,
'intro': getIt
.get<ContactAccountProfile>()
.groupChats[widget.groupChatId]!
.introduction,
};
context.pushNamed(
'ChangeGroupChatIntro',
queryParameters: query,
);
},
title: '群聊简介',
info: getIt
.get<ContactAccountProfile>()
.groupChats[widget.groupChatId]!
.introduction,
),
GroupChatProfileTile(
onTap: () {},
title: '群聊标签',
info: getIt
.get<ContactAccountProfile>()
.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<Contact>()
.groupChats[widget.groupChatId]!
.myRemark,
};
context.pushNamed(
'ChangeMyRemark',
queryParameters: query,
);
},
title: '我的群聊昵称',
info: getIt
.get<Contact>()
.groupChats[widget.groupChatId]!
.myRemark,
),
GroupChatProfileTile(
onTap: () {
final query = {
'groupChatId': widget.groupChatId,
'nameRemark': getIt
.get<Contact>()
.groupChats[widget.groupChatId]!
.nameRemark,
};
context.pushNamed(
'ChangeGroupChatRemark',
queryParameters: query,
);
},
title: '我的群聊备注',
info: getIt
.get<Contact>()
.groupChats[widget.groupChatId]!
.nameRemark,
),
const GroupChatProfileHeader(
header: '聊天记录',
),
GroupChatProfileTile(
onTap: () {},
title: '查找聊天记录',
info: '图片、视频、音频、文件等',
),
GroupChatProfileTile(
onTap: () {},
title: '删除聊天记录',
info: '',
),
if (getIt.get<UserAccount>().id ==
getIt
.get<ContactAccountProfile>()
.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<ContactAccountProfile>()
.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<Contact>().deleteGroupChat(widget.groupChatId);
getIt
.get<ContactAccountProfile>()
.deleteGroupChat(widget.groupChatId);
late Box<ChatSetting> chatSettingBox;
try {
chatSettingBox = Hive.box<ChatSetting>('chat_setting');
} catch (_) {
chatSettingBox = await Hive.openBox('chatSetting');
}
chatSettingBox.delete(widget.groupChatId);
Hive.deleteBoxFromDisk('message_${widget.groupChatId}');
},
);
}
}
}
}

View File

@ -42,7 +42,7 @@ class _GroupChatMessageBubbleState extends State<GroupChatMessageBubble> {
.get<ContactAccountProfile>()
.grouChatMemberProfiles
.containsKey(widget.senderId)) {
// myself
// myself or already have profile
return Future(() => true);
} else {
Map<String, dynamic> res;
@ -61,6 +61,7 @@ class _GroupChatMessageBubbleState extends State<GroupChatMessageBubble> {
);
}
getIt.get<ContactAccountProfile>().addGroupChatMemberProfile(
widget.contactId,
widget.senderId,
res['data'],
);
@ -216,8 +217,8 @@ class _GroupChatMessageBubbleState extends State<GroupChatMessageBubble> {
.isEmpty
? getIt
.get<ContactAccountProfile>()
.grouChatMemberProfiles[
widget.senderId]!
.grouChatMemberProfiles[widget
.contactId]![widget.senderId]!
.remark
.isEmpty
? Text(
@ -230,6 +231,7 @@ class _GroupChatMessageBubbleState extends State<GroupChatMessageBubble> {
getIt
.get<ContactAccountProfile>()
.grouChatMemberProfiles[
widget.contactId]![
widget.senderId]!
.remark,
)
@ -242,22 +244,22 @@ class _GroupChatMessageBubbleState extends State<GroupChatMessageBubble> {
} else {
return getIt
.get<ContactAccountProfile>()
.grouChatMemberProfiles[
widget.senderId]!
.grouChatMemberProfiles[widget
.contactId]![widget.senderId]!
.remark
.isEmpty
? Text(
getIt
.get<ContactAccountProfile>()
.grouChatMemberProfiles[
widget.senderId]!
.grouChatMemberProfiles[widget
.contactId]![widget.senderId]!
.nickname,
)
: Text(
getIt
.get<ContactAccountProfile>()
.grouChatMemberProfiles[
widget.senderId]!
.grouChatMemberProfiles[widget
.contactId]![widget.senderId]!
.remark,
);
}

View File

@ -61,7 +61,14 @@ class _GroupChatMessageScreenState extends State<GroupChatMessageScreen> {
centerTitle: true,
actions: [
IconButton(
onPressed: () {},
onPressed: () {
context.pushNamed(
'GroupChatProfile',
queryParameters: {
'groupChatId': widget.groupChatId,
},
);
},
icon: const Icon(Icons.menu),
splashRadius: 18,
),

View File

@ -15,13 +15,19 @@ class ChangeSignScreen extends StatefulWidget {
}
class _ChangeSignScreenState extends State<ChangeSignScreen> {
TextEditingController controller = TextEditingController();
final TextEditingController _controller = TextEditingController();
bool _isChanged = false;
@override
void initState() {
super.initState();
controller.text = getIt.get<UserProfile>().sign;
_controller.text = getIt.get<UserProfile>().sign;
}
@override
void dispose() {
super.dispose();
_controller.dispose();
}
@override
@ -40,7 +46,7 @@ class _ChangeSignScreenState extends State<ChangeSignScreen> {
cursorHeight: 24,
minLines: 5,
maxLines: 5,
controller: controller,
controller: _controller,
onChanged: (value) {
if (value.isEmpty || value == getIt.get<UserProfile>().sign) {
setState(() {
@ -73,7 +79,7 @@ class _ChangeSignScreenState extends State<ChangeSignScreen> {
Map<String, dynamic> res = await changeSign(
getIt.get<UserAccount>().id,
controller.text,
_controller.text,
);
if (res['code'] == 10300) {
// ignore: use_build_context_synchronously
@ -83,7 +89,7 @@ class _ChangeSignScreenState extends State<ChangeSignScreen> {
toastDuration: const Duration(seconds: 2),
).show(context);
getIt.get<UserProfile>().updateSign(controller.text);
getIt.get<UserProfile>().updateSign(_controller.text);
setState(() {
_isChanged = false;