fix group chat bubble bug: flash

main
htylight 2023-08-27 19:14:37 +08:00
parent 6367125961
commit 3f3f898d2c
6 changed files with 263 additions and 123 deletions

View File

@ -105,6 +105,15 @@ class Contact extends ChangeNotifier {
groupChatCount--;
notifyListeners();
}
void clean() {
friends = {};
friendGroups = [];
defaultGroup = '';
groupChats = {};
friendCount = 0;
groupChatCount = 0;
}
}
class FriendAccountProfile {
@ -272,4 +281,10 @@ class ContactAccountProfile extends ChangeNotifier {
grouChatMemberProfiles.remove(groupChatId);
notifyListeners();
}
void clean() {
friends = {};
groupChats = {};
grouChatMemberProfiles = {};
}
}

View File

@ -14,11 +14,17 @@ import 'package:together_mobile/models/user_model.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
import 'package:web_socket_channel/status.dart' as status;
enum SocketStatus {
connected,
closed,
error,
}
class WebSocketManager extends ChangeNotifier {
late Uri wsUrl;
late WebSocketChannel channel;
String id = '';
SocketStatus? socketStatus;
SocketStatus socketStatus = SocketStatus.closed;
Timer? heartBeatTimer;
Timer? serverTimer;
Timer? reconnectTimer;
@ -42,6 +48,20 @@ class WebSocketManager extends ChangeNotifier {
channel.stream.listen(onData, onError: onError, onDone: onDone);
}
void disconnect() {
channel.sink.close();
wsUrl = Uri();
id = '';
socketStatus = SocketStatus.closed;
heartBeatTimer?.cancel();
serverTimer?.cancel();
reconnectTimer?.cancel();
heartBeatTimer = null;
serverTimer = null;
reconnectTimer = null;
reconnectTimes = 0;
}
void onData(jsonData) {
heartBeatInspect();
Map<String, dynamic> data = json.decode(jsonData);
@ -65,6 +85,9 @@ class WebSocketManager extends ChangeNotifier {
void onDone() {
print('websocket disconnected <$channel>');
if (socketStatus == SocketStatus.closed) {
return;
}
reconnect();
}
@ -132,12 +155,6 @@ class WebSocketManager extends ChangeNotifier {
}
}
enum SocketStatus {
connected,
closed,
error,
}
void receiveFriendMsg(Map<String, dynamic> msg) async {
print('=================收到了好友信息事件==================');
print(msg);
@ -252,10 +269,10 @@ void receiveGroupChatMsg(Map<String, dynamic> msg) async {
if (chatSetting == null) {
chatSettingBox.put(
senderId,
groupChatId,
ChatSetting(
groupChatId,
0,
1,
false,
true,
false,
@ -271,7 +288,6 @@ void receiveGroupChatMsg(Map<String, dynamic> msg) async {
}
List<String> attachments = List.from(msg['attachments']);
messageTBox.add(
MessageT(
senderId,

View File

@ -162,6 +162,7 @@ class _ChatScreenState extends State<ChatScreen> {
messageTBox.getAt(length - 1)!;
if (openedChat[index].type == 0) {
return FriendChatTile(
key: ValueKey(contactId),
index: index,
contactId: contactId,
senderId: messageT.senderId,
@ -174,6 +175,7 @@ class _ChatScreenState extends State<ChatScreen> {
);
} else {
return GroupChatChatTile(
key: ValueKey(contactId),
index: index,
contactId: contactId,
senderId: messageT.senderId,

View File

@ -37,12 +37,18 @@ class _GroupChatMessageBubbleState extends State<GroupChatMessageBubble> {
final List<Timer> _timerList = [];
Future<bool> _getMemberGroupChatProfile() async {
if (widget.senderId == getIt.get<UserAccount>().id ||
getIt
if (widget.senderId == getIt.get<UserAccount>().id) {
// myself or already have profile
return Future(() => true);
}
if (getIt
.get<ContactAccountProfile>()
.grouChatMemberProfiles
.containsKey(widget.contactId) &&
getIt
.get<ContactAccountProfile>()
.grouChatMemberProfiles[widget.contactId]!
.containsKey(widget.senderId)) {
// myself or already have profile
return Future(() => true);
} else {
Map<String, dynamic> res;
@ -136,49 +142,18 @@ class _GroupChatMessageBubbleState extends State<GroupChatMessageBubble> {
? FutureBuilder(
future: _getMemberGroupChatProfile(),
builder: (context, snapshot) {
if (snapshot.hasData) {
if (isFriend) {
return getIt
.get<ContactAccountProfile>()
.friends[widget.senderId]!
.avatar
.isEmpty
? CircleAvatar(
child:
Image.asset('assets/images/user_2.png'),
)
: CircleAvatar(
backgroundImage: CachedNetworkImageProvider(
'$avatarsUrl/${getIt.get<ContactAccountProfile>().friends[widget.senderId]!.avatar}',
),
);
} else {
return getIt
.get<ContactAccountProfile>()
.groupChats[widget.senderId]!
.avatar
.isEmpty
? CircleAvatar(
child:
Image.asset('assets/images/user_2.png'),
)
: CircleAvatar(
backgroundImage: CachedNetworkImageProvider(
'$avatarsUrl/${getIt.get<ContactAccountProfile>().groupChats[widget.senderId]!.avatar}',
),
);
}
} else {
return CircleAvatar(
child: Image.asset('assets/images/user_2.png'),
);
}
return _showAvatar(snapshot, isFriend);
},
)
: CircleAvatar(
: getIt.get<UserProfile>().avatar.isNotEmpty
? CircleAvatar(
backgroundImage: CachedNetworkImageProvider(
'$avatarsUrl/${getIt.get<UserProfile>().avatar}',
),
)
: const CircleAvatar(
backgroundImage:
AssetImage('assets/images/user_4.png'),
),
const SizedBox(
width: 10,
@ -190,8 +165,6 @@ class _GroupChatMessageBubbleState extends State<GroupChatMessageBubble> {
? CrossAxisAlignment.start
: CrossAxisAlignment.end,
children: [
// nickname
// used in group chat only
widget.senderId == getIt.get<UserAccount>().id
? getIt
.get<Contact>()
@ -208,70 +181,12 @@ class _GroupChatMessageBubbleState extends State<GroupChatMessageBubble> {
: FutureBuilder(
future: _getMemberGroupChatProfile(),
builder: (context, snapshot) {
if (snapshot.hasData) {
if (isFriend) {
return getIt
.get<Contact>()
.friends[widget.senderId]!
.friendRemark
.isEmpty
? getIt
.get<ContactAccountProfile>()
.grouChatMemberProfiles[widget
.contactId]![widget.senderId]!
.remark
.isEmpty
? Text(
getIt
.get<ContactAccountProfile>()
.friends[widget.senderId]!
.nickname,
)
: Text(
getIt
.get<ContactAccountProfile>()
.grouChatMemberProfiles[
widget.contactId]![
widget.senderId]!
.remark,
)
: Text(
getIt
.get<Contact>()
.friends[widget.senderId]!
.friendRemark,
);
} else {
return getIt
.get<ContactAccountProfile>()
.grouChatMemberProfiles[widget
.contactId]![widget.senderId]!
.remark
.isEmpty
? Text(
getIt
.get<ContactAccountProfile>()
.grouChatMemberProfiles[widget
.contactId]![widget.senderId]!
.nickname,
)
: Text(
getIt
.get<ContactAccountProfile>()
.grouChatMemberProfiles[widget
.contactId]![widget.senderId]!
.remark,
);
}
} else {
return Text(widget.senderId);
}
return _showName(snapshot, isFriend);
},
),
const SizedBox(
height: 5,
),
if (widget.type == 'text/multipart')
// message box
Container(
@ -347,4 +262,191 @@ class _GroupChatMessageBubbleState extends State<GroupChatMessageBubble> {
),
);
}
CircleAvatar _showAvatar(AsyncSnapshot snapshot, bool isFriend) {
if (snapshot.hasData) {
if (isFriend) {
String avatar =
getIt.get<ContactAccountProfile>().friends[widget.senderId]!.avatar;
return avatar.isEmpty
? CircleAvatar(
child: Image.asset('assets/images/user_4.png'),
)
: CircleAvatar(
backgroundImage: CachedNetworkImageProvider(
'$avatarsUrl/$avatar',
),
);
} else {
String avatar = getIt
.get<ContactAccountProfile>()
.grouChatMemberProfiles[widget.contactId]![widget.senderId]!
.avatar;
return avatar.isEmpty
? CircleAvatar(
child: Image.asset('assets/images/user_4.png'),
)
: CircleAvatar(
backgroundImage: CachedNetworkImageProvider(
'$avatarsUrl/$avatar',
),
);
}
} else {
// fix the bug that when the future return, the avatar will flash
if (isFriend) {
String avatar =
getIt.get<ContactAccountProfile>().friends[widget.senderId]!.avatar;
return avatar.isEmpty
? CircleAvatar(
child: Image.asset('assets/images/user_4.png'),
)
: CircleAvatar(
backgroundImage: CachedNetworkImageProvider(
'$avatarsUrl/$avatar',
),
);
} else {
if (getIt
.get<ContactAccountProfile>()
.grouChatMemberProfiles
.containsKey(widget.contactId)) {
if (getIt
.get<ContactAccountProfile>()
.grouChatMemberProfiles[widget.contactId]!
.containsKey(widget.senderId)) {
String avatar = getIt
.get<ContactAccountProfile>()
.grouChatMemberProfiles[widget.contactId]![widget.senderId]!
.avatar;
return avatar.isNotEmpty
? CircleAvatar(
backgroundImage: CachedNetworkImageProvider(
'$avatarsUrl/$avatar',
),
)
: const CircleAvatar(
backgroundImage: AssetImage(
'assets/images/user_4.png',
),
);
} else {
return const CircleAvatar(
backgroundImage: AssetImage(
'assets/images/user_4.png',
),
);
}
} else {
return const CircleAvatar(
backgroundImage: AssetImage(
'assets/images/user_4.png',
),
);
}
}
}
}
Text _showName(AsyncSnapshot snapshot, bool isFriend) {
if (snapshot.hasData) {
if (isFriend) {
String friendRemark =
getIt.get<Contact>().friends[widget.senderId]!.friendRemark;
String remarkInGroupChat = getIt
.get<ContactAccountProfile>()
.grouChatMemberProfiles[widget.contactId]![widget.senderId]!
.remark;
return friendRemark.isEmpty
? remarkInGroupChat.isEmpty
? Text(
getIt
.get<ContactAccountProfile>()
.friends[widget.senderId]!
.nickname,
)
: Text(remarkInGroupChat)
: Text(friendRemark);
} else {
String remarkInGroupChat = getIt
.get<ContactAccountProfile>()
.grouChatMemberProfiles[widget.contactId]![widget.senderId]!
.remark;
return remarkInGroupChat.isEmpty
? Text(
getIt
.get<ContactAccountProfile>()
.grouChatMemberProfiles[widget.contactId]![widget.senderId]!
.nickname,
)
: Text(remarkInGroupChat);
}
} else {
// fix the bug that when the future return, the name will flash
if (isFriend) {
String friendRemark =
getIt.get<Contact>().friends[widget.senderId]!.friendRemark;
if (friendRemark.isNotEmpty) {
return Text(friendRemark);
} else {
if (getIt
.get<ContactAccountProfile>()
.grouChatMemberProfiles
.containsKey(widget.contactId) &&
getIt
.get<ContactAccountProfile>()
.grouChatMemberProfiles[widget.contactId]!
.containsKey(widget.senderId)) {
String remarkInGroupChat = getIt
.get<ContactAccountProfile>()
.grouChatMemberProfiles[widget.contactId]![widget.senderId]!
.remark;
String nickname = getIt
.get<ContactAccountProfile>()
.friends[widget.senderId]!
.nickname;
return remarkInGroupChat.isEmpty
? Text(nickname)
: Text(remarkInGroupChat);
} else {
return Text(widget.senderId.substring(0, 5));
}
}
} else {
if (getIt
.get<ContactAccountProfile>()
.grouChatMemberProfiles
.containsKey(widget.contactId)) {
if (getIt
.get<ContactAccountProfile>()
.grouChatMemberProfiles[widget.contactId]!
.containsKey(widget.senderId)) {
String remarkInGroupChat = getIt
.get<ContactAccountProfile>()
.grouChatMemberProfiles[widget.contactId]![widget.senderId]!
.remark;
String nickname = getIt
.get<ContactAccountProfile>()
.grouChatMemberProfiles[widget.contactId]![widget.senderId]!
.nickname;
return remarkInGroupChat.isNotEmpty
? Text(remarkInGroupChat)
: Text(nickname);
} else {
return Text(
widget.senderId.substring(0, 5),
);
}
} else {
return Text(
widget.senderId.substring(0, 5),
);
}
}
}
}
}

View File

@ -2,9 +2,11 @@ import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:together_mobile/models/apply_list_model.dart';
import 'package:together_mobile/models/contact_model.dart';
import 'package:together_mobile/models/init_get_it.dart';
import 'package:together_mobile/models/token_model.dart';
import 'package:together_mobile/models/user_model.dart';
import 'package:together_mobile/models/websocket_model.dart';
class SettingScreen extends StatelessWidget {
const SettingScreen({super.key});
@ -22,9 +24,12 @@ class SettingScreen extends StatelessWidget {
child: TextButton(
onPressed: () async {
await getIt.get<Token>().clean();
getIt.get<WebSocketManager>().disconnect();
getIt.get<UserAccount>().clean();
getIt.get<UserProfile>().clean();
getIt.get<ApplyList>().clean();
getIt.get<Contact>().clean();
getIt.get<ContactAccountProfile>().clean();
// Hive.deleteFromDisk();
Hive.close();
// ignore: use_build_context_synchronously

View File

@ -30,6 +30,13 @@ class _UsernameSigninBodyState extends State<UsernameSigninBody> {
'password': false,
};
@override
void dispose() {
usernameController.dispose();
passwordController.dispose();
super.dispose();
}
void _isInputError(String type, bool error) {
setState(() {
_isError[type] = error;
@ -139,11 +146,4 @@ class _UsernameSigninBodyState extends State<UsernameSigninBody> {
CherryToast.error(title: const Text('用户名或密码错误')).show(context);
}
}
@override
void dispose() {
usernameController.dispose();
passwordController.dispose();
super.dispose();
}
}