Improvement:

- Change logic of unread messages
This commit is contained in:
Linloir 2022-10-23 20:53:38 +08:00
parent 832982a415
commit 5834bcaa58
No known key found for this signature in database
GPG Key ID: 58EEB209A0F2C366
9 changed files with 419 additions and 394 deletions

View File

@ -12,7 +12,7 @@ import 'package:tcp_client/home/cubit/home_cubit.dart';
import 'package:tcp_client/home/cubit/home_state.dart';
import 'package:tcp_client/home/view/contact_page/contact_page.dart';
import 'package:tcp_client/home/view/contact_page/cubit/contact_cubit.dart';
import 'package:tcp_client/home/view/message_page/cubit/msg_list/msg_list_cubit.dart';
import 'package:tcp_client/home/view/message_page/cubit/msg_list_cubit.dart';
import 'package:tcp_client/home/view/message_page/mesage_page.dart';
import 'package:tcp_client/home/view/profile_page/profile_page.dart';
import 'package:tcp_client/repositories/local_service_repository/local_service_repository.dart';

View File

@ -16,7 +16,7 @@ import 'package:tcp_client/home/cubit/home_cubit.dart';
import 'package:tcp_client/home/cubit/home_state.dart';
import 'package:tcp_client/home/view/contact_page/cubit/contact_cubit.dart';
import 'package:tcp_client/home/view/contact_page/models/contact_model.dart';
import 'package:tcp_client/home/view/message_page/cubit/msg_list/msg_list_cubit.dart';
import 'package:tcp_client/home/view/message_page/cubit/msg_list_cubit.dart';
import 'package:tcp_client/repositories/local_service_repository/local_service_repository.dart';
import 'package:tcp_client/repositories/tcp_repository/models/tcp_request.dart';
import 'package:tcp_client/repositories/tcp_repository/tcp_repository.dart';

View File

@ -1,108 +0,0 @@
/*
* @Author : Linloir
* @Date : 2022-10-12 23:37:49
* @LastEditTime : 2022-10-17 13:10:49
* @Description :
*/
import 'package:equatable/equatable.dart';
import 'package:tcp_client/home/view/message_page/models/message_info.dart';
class MessageListState extends Equatable {
final List<MessageInfo> messageList;
const MessageListState({
required this.messageList
});
static MessageListState empty() => const MessageListState(messageList: []);
MessageListState updateWithSingle({
required MessageInfo messageInfo
}) {
var newList = <MessageInfo>[messageInfo];
for(var msgInfo in messageList) {
if(msgInfo.targetUser == messageInfo.targetUser) {
continue;
}
newList.add(msgInfo);
}
return MessageListState(messageList: newList);
}
MessageListState updateWithList({
required List<MessageInfo> orderedNewMessages
}) {
var newList = <MessageInfo>[];
Set<int> addedUsers = {};
var insertListIndex = 0;
var origListIndex = 0;
while(
insertListIndex < orderedNewMessages.length &&
origListIndex < messageList.length
) {
if(addedUsers.contains(orderedNewMessages[insertListIndex].targetUser)) {
insertListIndex += 1;
continue;
}
if(addedUsers.contains(messageList[origListIndex].targetUser)) {
origListIndex += 1;
continue;
}
if(
(messageList[origListIndex].message?.timeStamp ?? 0) >
(orderedNewMessages[insertListIndex].message?.timeStamp ?? 0)
) {
newList.add(messageList[origListIndex]);
addedUsers.add(messageList[origListIndex].targetUser);
origListIndex += 1;
continue;
}
else {
newList.add(orderedNewMessages[insertListIndex]);
addedUsers.add(orderedNewMessages[insertListIndex].targetUser);
insertListIndex += 1;
continue;
}
}
//Add the messages left
while(origListIndex < messageList.length) {
if(addedUsers.contains(messageList[origListIndex].targetUser)) {
origListIndex += 1;
continue;
}
newList.add(messageList[origListIndex]);
addedUsers.add(messageList[origListIndex].targetUser);
origListIndex += 1;
continue;
}
while(insertListIndex < orderedNewMessages.length) {
if(addedUsers.contains(orderedNewMessages[insertListIndex].targetUser)) {
insertListIndex += 1;
continue;
}
newList.add(orderedNewMessages[insertListIndex]);
addedUsers.add(orderedNewMessages[insertListIndex].targetUser);
insertListIndex += 1;
continue;
}
return MessageListState(messageList: newList);
}
MessageListState deleteOf({
required MessageInfo messageInfo
}) {
var newList = <MessageInfo>[];
for(var msgInfo in messageList) {
if(msgInfo == messageInfo) {
continue;
}
newList.add(msgInfo);
}
return MessageListState(messageList: newList);
}
@override
List<Object> get props => [...messageList];
}

View File

@ -1,7 +1,7 @@
/*
* @Author : Linloir
* @Date : 2022-10-12 23:38:31
* @LastEditTime : 2022-10-23 16:30:24
* @LastEditTime : 2022-10-23 20:43:56
* @Description :
*/
@ -9,7 +9,7 @@ import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:tcp_client/home/view/message_page/cubit/msg_list/msg_list_state.dart';
import 'package:tcp_client/home/view/message_page/cubit/msg_list_state.dart';
import 'package:tcp_client/home/view/message_page/models/message_info.dart';
import 'package:tcp_client/repositories/local_service_repository/local_service_repository.dart';
import 'package:tcp_client/repositories/tcp_repository/models/tcp_request.dart';
@ -40,7 +40,24 @@ class MessageListCubit extends Cubit<MessageListState> {
}
}
return msgList;
}).then((msgList) => emit(state.updateWithList(orderedNewMessages: msgList)));
}).then((msgList) async {
//get new message list
var newList = updateWithList(orderedNewMessages: msgList);
//get unread message count
var curUser = (await SharedPreferences.getInstance()).getInt('userid');
Map<int, int> unreadCnt = {};
for(var msg in newList) {
var cnt = await localServiceRepository.getUnreadCount(
userid: curUser!,
targetid: msg.targetUser
);
unreadCnt.update(msg.targetUser, (value) => cnt, ifAbsent: () => cnt,);
}
//Emit new state
if(!isClosed) {
emit(state.copyWith(messageList: newList, unreadCnt: unreadCnt));
}
});
}
final LocalServiceRepository localServiceRepository;
@ -52,7 +69,7 @@ class MessageListCubit extends Cubit<MessageListState> {
return;
}
var newList = [MessageInfo(targetUser: targetUser), ...state.messageList];
emit(MessageListState(messageList: newList));
emit(state.copyWith(messageList: newList));
var pref = await SharedPreferences.getInstance();
var currentUserID = pref.getInt('userid');
var msgUserList = pref.getStringList('${currentUserID}msg') ?? [];
@ -65,6 +82,12 @@ class MessageListCubit extends Cubit<MessageListState> {
tcpRepository.pushRequest(FetchMessageRequest(token: (await SharedPreferences.getInstance()).getInt('token')));
}
void clearUnread({required int targetID}) {
var unreadCnt = state.unreadCnt;
unreadCnt.update(targetID, (value) => 0, ifAbsent: () => 0,);
emit(state.copyWith(unreadCnt: unreadCnt));
}
Future<void> _onResponse(TCPResponse response) async {
switch(response.type) {
case TCPResponseType.sendMessage: {
@ -75,10 +98,11 @@ class MessageListCubit extends Cubit<MessageListState> {
var pref = await SharedPreferences.getInstance();
var currentUserID = pref.getInt('userid');
var targetUser = message.senderID == currentUserID ? message.recieverID : message.senderID;
emit(state.updateWithSingle(messageInfo: MessageInfo(
var newList = updateWithSingle(messageInfo: MessageInfo(
message: message,
targetUser: targetUser
)));
));
emit(state.copyWith(messageList: newList));
var msgUserList = state.messageList.map((e) => e.targetUser.toString()).toList();
pref.setStringList('${currentUserID}msg', msgUserList);
}
@ -92,6 +116,8 @@ class MessageListCubit extends Cubit<MessageListState> {
var pref = await SharedPreferences.getInstance();
var curUser = pref.getInt('userid');
Map<int, int> unreadCnt = state.unreadCnt;
for(var message in response.messages) {
//Since the message can be send to or from the current user
//it's neccessary to identify the other user's id of the message
@ -106,10 +132,22 @@ class MessageListCubit extends Cubit<MessageListState> {
message: message
));
}
//Add to unreadCnt
var lastReadTime = await localServiceRepository.fetchReadHistory(
userid: curUser!,
targetid: targetUser
);
if(lastReadTime < message.timeStamp) {
unreadCnt.update(targetUser, (value) => value + 1, ifAbsent: () => 1,);
}
}
//Use the meessage list to create new state
emit(state.updateWithList(orderedNewMessages: latestMessages));
var newMessageList = updateWithList(orderedNewMessages: latestMessages);
//Emit new state
if(!isClosed) {
emit(state.copyWith(messageList: newMessageList, unreadCnt: unreadCnt));
}
var msgUserList = state.messageList.map((e) => e.targetUser.toString()).toList();
pref.setStringList('${curUser}msg', msgUserList);
@ -124,12 +162,26 @@ class MessageListCubit extends Cubit<MessageListState> {
var targetUser = response.message.senderID == curUser ?
response.message.recieverID :
response.message.senderID;
emit(state.updateWithSingle(
var newList = updateWithSingle(
messageInfo: MessageInfo(
targetUser: targetUser,
message: response.message
)
));
);
var unreadCnt = state.unreadCnt;
var lastReadTime = await localServiceRepository.fetchReadHistory(
userid: curUser!,
targetid: targetUser
);
if(lastReadTime < response.message.timeStamp) {
unreadCnt.update(targetUser, (value) => value + 1, ifAbsent: () => 1,);
}
else {
unreadCnt.update(targetUser, (value) => 0, ifAbsent: () => 0,);
}
if(!isClosed) {
emit(state.copyWith(messageList: newList, unreadCnt: unreadCnt));
}
var msgUserList = pref.getStringList('${curUser}msg') ?? [];
msgUserList.remove('$targetUser');
msgUserList.insert(0, '$targetUser');
@ -140,6 +192,92 @@ class MessageListCubit extends Cubit<MessageListState> {
}
}
List<MessageInfo> updateWithSingle({
required MessageInfo messageInfo
}) {
var newList = <MessageInfo>[messageInfo];
for(var msgInfo in state.messageList) {
if(msgInfo.targetUser == messageInfo.targetUser) {
continue;
}
newList.add(msgInfo);
}
return newList;
}
List<MessageInfo> updateWithList({
required List<MessageInfo> orderedNewMessages
}) {
var newList = <MessageInfo>[];
Set<int> addedUsers = {};
var insertListIndex = 0;
var origListIndex = 0;
while(
insertListIndex < orderedNewMessages.length &&
origListIndex < state.messageList.length
) {
if(addedUsers.contains(orderedNewMessages[insertListIndex].targetUser)) {
insertListIndex += 1;
continue;
}
if(addedUsers.contains(state.messageList[origListIndex].targetUser)) {
origListIndex += 1;
continue;
}
if(
(state.messageList[origListIndex].message?.timeStamp ?? 0) >
(orderedNewMessages[insertListIndex].message?.timeStamp ?? 0)
) {
newList.add(state.messageList[origListIndex]);
addedUsers.add(state.messageList[origListIndex].targetUser);
origListIndex += 1;
continue;
}
else {
newList.add(orderedNewMessages[insertListIndex]);
addedUsers.add(orderedNewMessages[insertListIndex].targetUser);
insertListIndex += 1;
continue;
}
}
//Add the messages left
while(origListIndex < state.messageList.length) {
if(addedUsers.contains(state.messageList[origListIndex].targetUser)) {
origListIndex += 1;
continue;
}
newList.add(state.messageList[origListIndex]);
addedUsers.add(state.messageList[origListIndex].targetUser);
origListIndex += 1;
continue;
}
while(insertListIndex < orderedNewMessages.length) {
if(addedUsers.contains(orderedNewMessages[insertListIndex].targetUser)) {
insertListIndex += 1;
continue;
}
newList.add(orderedNewMessages[insertListIndex]);
addedUsers.add(orderedNewMessages[insertListIndex].targetUser);
insertListIndex += 1;
continue;
}
return newList;
}
List<MessageInfo> deleteOf({
required MessageInfo messageInfo
}) {
var newList = <MessageInfo>[];
for(var msgInfo in state.messageList) {
if(msgInfo == messageInfo) {
continue;
}
newList.add(msgInfo);
}
return newList;
}
@override
Future<void> close() {
subscription.cancel();

View File

@ -0,0 +1,120 @@
/*
* @Author : Linloir
* @Date : 2022-10-12 23:37:49
* @LastEditTime : 2022-10-23 20:50:19
* @Description :
*/
import 'package:equatable/equatable.dart';
import 'package:tcp_client/home/view/message_page/models/message_info.dart';
class MessageListState extends Equatable {
final List<MessageInfo> messageList;
final Map<int, int> unreadCnt;
const MessageListState({
required this.messageList,
required this.unreadCnt
});
static MessageListState empty() => const MessageListState(messageList: [], unreadCnt: {});
MessageListState copyWith({
List<MessageInfo>? messageList,
Map<int, int>? unreadCnt
}) {
return MessageListState(
messageList: messageList ?? this.messageList,
unreadCnt: unreadCnt ?? this.unreadCnt
);
}
// MessageListState updateWithSingle({
// required MessageInfo messageInfo
// }) {
// var newList = <MessageInfo>[messageInfo];
// for(var msgInfo in messageList) {
// if(msgInfo.targetUser == messageInfo.targetUser) {
// continue;
// }
// newList.add(msgInfo);
// }
// return MessageListState(messageList: newList);
// }
// MessageListState updateWithList({
// required List<MessageInfo> orderedNewMessages
// }) {
// var newList = <MessageInfo>[];
// Set<int> addedUsers = {};
// var insertListIndex = 0;
// var origListIndex = 0;
// while(
// insertListIndex < orderedNewMessages.length &&
// origListIndex < messageList.length
// ) {
// if(addedUsers.contains(orderedNewMessages[insertListIndex].targetUser)) {
// insertListIndex += 1;
// continue;
// }
// if(addedUsers.contains(messageList[origListIndex].targetUser)) {
// origListIndex += 1;
// continue;
// }
// if(
// (messageList[origListIndex].message?.timeStamp ?? 0) >
// (orderedNewMessages[insertListIndex].message?.timeStamp ?? 0)
// ) {
// newList.add(messageList[origListIndex]);
// addedUsers.add(messageList[origListIndex].targetUser);
// origListIndex += 1;
// continue;
// }
// else {
// newList.add(orderedNewMessages[insertListIndex]);
// addedUsers.add(orderedNewMessages[insertListIndex].targetUser);
// insertListIndex += 1;
// continue;
// }
// }
// //Add the messages left
// while(origListIndex < messageList.length) {
// if(addedUsers.contains(messageList[origListIndex].targetUser)) {
// origListIndex += 1;
// continue;
// }
// newList.add(messageList[origListIndex]);
// addedUsers.add(messageList[origListIndex].targetUser);
// origListIndex += 1;
// continue;
// }
// while(insertListIndex < orderedNewMessages.length) {
// if(addedUsers.contains(orderedNewMessages[insertListIndex].targetUser)) {
// insertListIndex += 1;
// continue;
// }
// newList.add(orderedNewMessages[insertListIndex]);
// addedUsers.add(orderedNewMessages[insertListIndex].targetUser);
// insertListIndex += 1;
// continue;
// }
// return MessageListState(messageList: newList);
// }
// MessageListState deleteOf({
// required MessageInfo messageInfo
// }) {
// var newList = <MessageInfo>[];
// for(var msgInfo in messageList) {
// if(msgInfo == messageInfo) {
// continue;
// }
// newList.add(msgInfo);
// }
// return MessageListState(messageList: newList);
// }
@override
List<Object> get props => [...unreadCnt.entries, ...messageList];
}

View File

@ -1,89 +0,0 @@
/*
* @Author : Linloir
* @Date : 2022-10-23 16:30:45
* @LastEditTime : 2022-10-23 17:46:28
* @Description :
*/
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:tcp_client/home/view/message_page/cubit/msg_tile/msg_tile_state.dart';
import 'package:tcp_client/repositories/local_service_repository/local_service_repository.dart';
import 'package:tcp_client/repositories/tcp_repository/models/tcp_response.dart';
import 'package:tcp_client/repositories/tcp_repository/tcp_repository.dart';
class MessageTileCubit extends Cubit<MessageTileState> {
MessageTileCubit({
required this.tcpRepository,
required this.localServiceRepository,
required this.targetID
}): super(const MessageTileState(unreadCount: 0)) {
Future<int>(() async {
return await localServiceRepository.getUnreadCount(
userid: (await SharedPreferences.getInstance()).getInt('userid')!,
targetid: targetID
);
}).then((value) => emit(state + value));
subscription = tcpRepository.responseStreamBroadcast.listen(_onResponse);
}
final TCPRepository tcpRepository;
final LocalServiceRepository localServiceRepository;
final int targetID;
late final StreamSubscription subscription;
Future<void> _onResponse(TCPResponse response) async {
var pref = await SharedPreferences.getInstance();
var userID = pref.getInt('userid');
if(userID == null) {
return;
}
var readHistoryTimestamp = await localServiceRepository.fetchReadHistory(
userid: userID,
targetid: targetID
);
if(response.type == TCPResponseType.fetchMessage) {
//Count unread incoming message count
response as FetchMessageResponse;
var addCnt = 0;
for(var message in response.messages) {
if(message.senderID == targetID && message.recieverID == userID) {
if(readHistoryTimestamp < message.timeStamp) {
addCnt += 1;
}
}
}
if(!isClosed) {
emit(state + addCnt);
}
}
else if(response.type == TCPResponseType.forwardMessage) {
//Count unread incoming message count
response as ForwardMessageResponse;
if(response.message.senderID == targetID && response.message.recieverID == userID) {
if(readHistoryTimestamp < response.message.timeStamp) {
if(!isClosed) {
emit(state + 1);
}
}
else {
if(!isClosed) {
emit(const MessageTileState(unreadCount: 0));
}
}
}
}
}
void clearUnread() {
emit(const MessageTileState(unreadCount: 0));
}
@override
Future<void> close() {
subscription.cancel();
return super.close();
}
}

View File

@ -1,21 +0,0 @@
/*
* @Author : Linloir
* @Date : 2022-10-23 16:30:52
* @LastEditTime : 2022-10-23 16:40:47
* @Description :
*/
import 'package:equatable/equatable.dart';
class MessageTileState extends Equatable {
final int unreadCount;
const MessageTileState({required this.unreadCount});
MessageTileState operator +(int other) {
return MessageTileState(unreadCount: unreadCount + other);
}
@override
List<Object> get props => [unreadCount];
}

View File

@ -1,15 +1,15 @@
/*
* @Author : Linloir
* @Date : 2022-10-11 11:05:18
* @LastEditTime : 2022-10-23 17:38:30
* @LastEditTime : 2022-10-23 20:47:33
* @Description :
*/
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:pull_to_refresh_flutter3/pull_to_refresh_flutter3.dart';
import 'package:tcp_client/home/view/message_page/cubit/msg_list/msg_list_cubit.dart';
import 'package:tcp_client/home/view/message_page/cubit/msg_list/msg_list_state.dart';
import 'package:tcp_client/home/view/message_page/cubit/msg_list_cubit.dart';
import 'package:tcp_client/home/view/message_page/cubit/msg_list_state.dart';
import 'package:tcp_client/home/view/message_page/view/message_tile.dart';
class MessagePage extends StatelessWidget {
@ -31,9 +31,9 @@ class MessagePage extends StatelessWidget {
physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()),
itemBuilder: (context, index) {
return MessageTile(
key: ValueKey(state.messageList[index].targetUser),
userID: state.messageList[index].targetUser,
message: state.messageList[index].message,
unreadCnt: state.unreadCnt[state.messageList[index].targetUser],
);
},
separatorBuilder: (context, index) {

View File

@ -1,7 +1,7 @@
/*
* @Author : Linloir
* @Date : 2022-10-13 13:17:52
* @LastEditTime : 2022-10-23 17:55:44
* @LastEditTime : 2022-10-23 20:49:21
* @Description :
*/
@ -10,8 +10,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:tcp_client/chat/chat_page.dart';
import 'package:tcp_client/common/avatar/avatar.dart';
import 'package:tcp_client/common/username/username.dart';
import 'package:tcp_client/home/view/message_page/cubit/msg_tile/msg_tile_cubit.dart';
import 'package:tcp_client/home/view/message_page/cubit/msg_tile/msg_tile_state.dart';
import 'package:tcp_client/home/view/message_page/cubit/msg_list_cubit.dart';
import 'package:tcp_client/repositories/common_models/message.dart';
import 'package:tcp_client/repositories/local_service_repository/local_service_repository.dart';
import 'package:tcp_client/repositories/tcp_repository/tcp_repository.dart';
@ -21,26 +20,17 @@ class MessageTile extends StatelessWidget {
const MessageTile({
required this.userID,
this.message,
required int? unreadCnt,
super.key
});
}): unreadCnt = unreadCnt ?? 0;
final int userID;
final Message? message;
final int unreadCnt;
@override
Widget build(BuildContext context) {
return BlocProvider<MessageTileCubit>(
key: ValueKey(userID),
create: (context) {
return MessageTileCubit(
tcpRepository: context.read<TCPRepository>(),
localServiceRepository: context.read<LocalServiceRepository>(),
targetID: userID
);
},
child: Builder(
key: ValueKey(userID),
builder: (context) => IntrinsicHeight(
return IntrinsicHeight(
child: Stack(
fit: StackFit.expand,
children: [
@ -53,7 +43,7 @@ class MessageTile extends StatelessWidget {
timestamp: message!.timeStamp
);
}
context.read<MessageTileCubit>().clearUnread();
context.read<MessageListCubit>().clearUnread(targetID: userID);
Navigator.of(context).push(ChatPage.route(
userRepository: context.read<UserRepository>(),
localServiceRepository: context.read<LocalServiceRepository>(),
@ -159,9 +149,8 @@ class MessageTile extends StatelessWidget {
),
),
),
BlocBuilder<MessageTileCubit, MessageTileState>(
builder: (context, state) {
return state.unreadCount == 0 ? Container() : Container(
if(unreadCnt != 0)
Container(
margin: const EdgeInsets.only(
bottom: 8.0
),
@ -174,15 +163,13 @@ class MessageTile extends StatelessWidget {
color: Colors.blue.withOpacity(0.9)
),
child: Text(
'${state.unreadCount > 99 ? '99+' : state.unreadCount}',
'${unreadCnt > 99 ? '99+' : unreadCnt}',
style: const TextStyle(
color: Colors.white,
fontSize: 12,
),
),
);
}
),
)
],
),
],
@ -190,8 +177,6 @@ class MessageTile extends StatelessWidget {
),
],
),
),
),
);
}