More Codes

- Message page (untested)
This commit is contained in:
Linloir 2022-10-13 14:05:35 +08:00
parent 7b89d8ce14
commit 0f714c2633
No known key found for this signature in database
GPG Key ID: 58EEB209A0F2C366
26 changed files with 476 additions and 18 deletions

6
lib/chat/chat_page.dart Normal file
View File

@ -0,0 +1,6 @@
/*
* @Author : Linloir
* @Date : 2022-10-13 14:03:16
* @LastEditTime : 2022-10-13 14:03:16
* @Description :
*/

View File

@ -0,0 +1,6 @@
/*
* @Author : Linloir
* @Date : 2022-10-13 14:03:56
* @LastEditTime : 2022-10-13 14:03:56
* @Description :
*/

View File

@ -0,0 +1,6 @@
/*
* @Author : Linloir
* @Date : 2022-10-13 14:03:52
* @LastEditTime : 2022-10-13 14:03:52
* @Description :
*/

View File

@ -0,0 +1,6 @@
/*
* @Author : Linloir
* @Date : 2022-10-13 14:03:45
* @LastEditTime : 2022-10-13 14:03:46
* @Description :
*/

View File

@ -0,0 +1,6 @@
/*
* @Author : Linloir
* @Date : 2022-10-13 14:02:28
* @LastEditTime : 2022-10-13 14:02:28
* @Description :
*/

View File

@ -0,0 +1,6 @@
/*
* @Author : Linloir
* @Date : 2022-10-13 14:02:24
* @LastEditTime : 2022-10-13 14:02:24
* @Description :
*/

View File

@ -0,0 +1,6 @@
/*
* @Author : Linloir
* @Date : 2022-10-12 23:36:07
* @LastEditTime : 2022-10-12 23:36:08
* @Description :
*/

View File

@ -0,0 +1,6 @@
/*
* @Author : Linloir
* @Date : 2022-10-13 14:01:45
* @LastEditTime : 2022-10-13 14:01:46
* @Description :
*/

View File

@ -0,0 +1,6 @@
/*
* @Author : Linloir
* @Date : 2022-10-13 14:01:39
* @LastEditTime : 2022-10-13 14:01:40
* @Description :
*/

View File

@ -0,0 +1,6 @@
/*
* @Author : Linloir
* @Date : 2022-10-13 14:02:00
* @LastEditTime : 2022-10-13 14:02:00
* @Description :
*/

View File

@ -1,6 +0,0 @@
/*
* @Author : Linloir
* @Date : 2022-10-11 11:05:18
* @LastEditTime : 2022-10-11 11:05:18
* @Description :
*/

View File

@ -0,0 +1,83 @@
/*
* @Author : Linloir
* @Date : 2022-10-12 23:38:31
* @LastEditTime : 2022-10-13 11:14:54
* @Description :
*/
import 'package:bloc/bloc.dart';
import 'package:shared_preferences/shared_preferences.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/common_models/userinfo.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 MessageListCubit extends Cubit<MessageListState> {
MessageListCubit({
required this.localServiceRepository,
required this.tcpRepository
}): super(MessageListState.empty()) {
tcpRepository.responseStreamBroadcast.listen(onResponse);
}
final LocalServiceRepository localServiceRepository;
final TCPRepository tcpRepository;
Future<void> onResponse(TCPResponse response) async {
switch(response.type) {
case TCPResponseType.fetchMessage: {
response as FetchMessageResponse;
Set<int> addedUserSet = {};
List<MessageInfo> latestMessages = [];
var pref = await SharedPreferences.getInstance();
var curUser = pref.getInt('userid');
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
var targetUser = message.senderID == curUser ? message.recieverID : message.senderID;
//Since the list is ordered descending by timestamp
//therefore only insert to map if the target user does not exist
if(!addedUserSet.contains(targetUser)) {
addedUserSet.add(targetUser);
var targetUserInfo = await localServiceRepository.fetchUserInfoViaID(userid: targetUser);
//TODO: Maybe need to add API in tcp repository to fetch user info via id
targetUserInfo ??= UserInfo(userid: targetUser, username: targetUser.toString());
//Create message info
latestMessages.add(MessageInfo(
userInfo: targetUserInfo,
message: message
));
}
}
//Use the meessage list to create new state
emit(state.updateWithList(orderedNewMessages: latestMessages));
break;
}
case TCPResponseType.forwardMessage: {
response as ForwardMessageResponse;
var pref = await SharedPreferences.getInstance();
var curUser = pref.getInt('userid');
var targetUser = response.message.senderID == curUser ?
response.message.recieverID :
response.message.senderID;
var targetUserInfo = await localServiceRepository.fetchUserInfoViaID(userid: targetUser);
//TODO: Maybe need to add API in tcp repository to fetch user info via id
targetUserInfo ??= UserInfo(userid: targetUser, username: targetUser.toString());
emit(state.updateWithSingle(
messageInfo: MessageInfo(
userInfo: targetUserInfo,
message: response.message
)
));
break;
}
default: break;
}
}
}

View File

@ -0,0 +1,108 @@
/*
* @Author : Linloir
* @Date : 2022-10-12 23:37:49
* @LastEditTime : 2022-10-13 11:09:54
* @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.userInfo.userID == messageInfo.userInfo.userID) {
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].userInfo.userID)) {
insertListIndex += 1;
continue;
}
if(addedUsers.contains(messageList[origListIndex].userInfo.userID)) {
origListIndex += 1;
continue;
}
if(
(messageList[origListIndex].message?.timeStamp ?? 0) >
(orderedNewMessages[insertListIndex].message?.timeStamp ?? 0)
) {
newList.add(messageList[origListIndex]);
addedUsers.add(messageList[origListIndex].userInfo.userID);
origListIndex += 1;
continue;
}
else {
newList.add(orderedNewMessages[insertListIndex]);
addedUsers.add(orderedNewMessages[insertListIndex].userInfo.userID);
insertListIndex += 1;
continue;
}
}
//Add the messages left
while(origListIndex < messageList.length) {
if(addedUsers.contains(messageList[origListIndex].userInfo.userID)) {
origListIndex += 1;
continue;
}
newList.add(messageList[origListIndex]);
addedUsers.add(messageList[origListIndex].userInfo.userID);
origListIndex += 1;
continue;
}
while(insertListIndex < orderedNewMessages.length) {
if(addedUsers.contains(orderedNewMessages[insertListIndex].userInfo.userID)) {
origListIndex += 1;
continue;
}
newList.add(orderedNewMessages[insertListIndex]);
addedUsers.add(orderedNewMessages[insertListIndex].userInfo.userID);
origListIndex += 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

@ -0,0 +1,48 @@
/*
* @Author : Linloir
* @Date : 2022-10-11 11:05:18
* @LastEditTime : 2022-10-13 13:58:43
* @Description :
*/
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.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';
import 'package:tcp_client/repositories/local_service_repository/local_service_repository.dart';
import 'package:tcp_client/repositories/tcp_repository/tcp_repository.dart';
class MessagePage extends StatelessWidget {
const MessagePage({super.key});
@override
Widget build(BuildContext context) {
return BlocProvider<MessageListCubit>(
create: (context) {
return MessageListCubit(
localServiceRepository: context.read<LocalServiceRepository>(),
tcpRepository: context.read<TCPRepository>()
);
},
child: BlocBuilder<MessageListCubit, MessageListState>(
builder: (context, state) {
return ListView.separated(
itemBuilder: (context, index) {
return MessageTile(
userInfo: state.messageList[index].userInfo,
message: state.messageList[index].message,
);
},
separatorBuilder: (context, index) {
return const Divider(
height: 0.5,
);
},
itemCount: state.messageList.length
);
}
)
);
}
}

View File

@ -0,0 +1,23 @@
/*
* @Author : Linloir
* @Date : 2022-10-12 23:48:54
* @LastEditTime : 2022-10-12 23:50:17
* @Description :
*/
import 'package:equatable/equatable.dart';
import 'package:tcp_client/repositories/common_models/message.dart';
import 'package:tcp_client/repositories/common_models/userinfo.dart';
class MessageInfo extends Equatable {
final Message? message;
final UserInfo userInfo;
const MessageInfo({
this.message,
required this.userInfo
});
@override
List<Object?> get props => [message?.contentmd5, userInfo];
}

View File

@ -0,0 +1,136 @@
/*
* @Author : Linloir
* @Date : 2022-10-13 13:17:52
* @LastEditTime : 2022-10-13 14:00:12
* @Description :
*/
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:tcp_client/repositories/common_models/message.dart';
import 'package:tcp_client/repositories/common_models/userinfo.dart';
class MessageTile extends StatelessWidget {
const MessageTile({
required this.userInfo,
this.message,
super.key
});
final UserInfo userInfo;
final Message? message;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(
vertical: 16.0,
horizontal: 24.0
),
child: IntrinsicHeight(
child: Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if(userInfo.avatarEncoded != null && userInfo.avatarEncoded!.isEmpty)
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5.0),
border: Border.all(
color: Colors.grey[700]!,
width: 1.0
)
),
child: ClipRRect(
borderRadius: BorderRadius.circular(5.0),
child: OverflowBox(
alignment: Alignment.center,
child: FittedBox(
fit: BoxFit.fitWidth,
child: Image.memory(base64Decode(userInfo.avatarEncoded!)),
),
)
),
),
if(userInfo.avatarEncoded == null || userInfo.avatarEncoded!.isEmpty)
Container(
color: Colors.grey,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5.0),
border: Border.all(
color: Colors.grey[700]!,
width: 1.0
)
),
),
Expanded(
child: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(
vertical: 8.0,
horizontal: 0
),
child: Text(
userInfo.userName,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
),
const Spacer(),
Padding(
padding: const EdgeInsets.symmetric(
vertical: 4.0
),
child: Text(
message?.contentDecoded ?? '',
style: const TextStyle(
fontSize: 16,
),
),
)
],
),
),
if(message != null)
Padding(
padding: const EdgeInsets.symmetric(
vertical: 8.0,
horizontal: 0
),
child: Align(
alignment: Alignment.topCenter,
child: Text(
getTimeStamp(message!.timeStamp)
),
),
),
],
),
),
);
}
String getTimeStamp(int timeStamp) {
var date = DateTime.fromMillisecondsSinceEpoch(timeStamp);
var weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
//If date is today, return time
if(date.day == DateTime.now().day) {
return '${date.hour}:${date.minute}';
}
//If date is yesterday, return 'yesterday'
if(date.day == DateTime.now().day - 1) {
return 'yesterday';
}
//If date is within this week, return the weekday in english
if(date.weekday < DateTime.now().weekday) {
return weekdays[date.weekday - 1];
}
//Otherwise return the date in english
return '${date.month}/${date.day}';
}
}

View File

@ -0,0 +1,6 @@
/*
* @Author : Linloir
* @Date : 2022-10-12 23:36:12
* @LastEditTime : 2022-10-12 23:36:57
* @Description :
*/

View File

@ -8,7 +8,7 @@
import 'package:bloc/bloc.dart';
import 'package:formz/formz.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:tcp_client/login/bloc/login_state.dart';
import 'package:tcp_client/login/cubit/login_state.dart';
import 'package:tcp_client/login/models/password.dart';
import 'package:tcp_client/login/models/username.dart';
import 'package:tcp_client/repositories/common_models/useridentity.dart';

View File

@ -1,7 +1,7 @@
/*
* @Author : Linloir
* @Date : 2022-10-12 15:06:30
* @LastEditTime : 2022-10-12 18:03:29
* @LastEditTime : 2022-10-12 23:37:04
* @Description :
*/
@ -9,8 +9,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:formz/formz.dart';
import 'package:tcp_client/home/home_page.dart';
import 'package:tcp_client/login/bloc/login_cubit.dart';
import 'package:tcp_client/login/bloc/login_state.dart';
import 'package:tcp_client/login/cubit/login_cubit.dart';
import 'package:tcp_client/login/cubit/login_state.dart';
import 'package:tcp_client/login/view/login_form.dart';
import 'package:tcp_client/register/register_page.dart';
import 'package:tcp_client/repositories/local_service_repository/local_service_repository.dart';

View File

@ -8,8 +8,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:formz/formz.dart';
import 'package:tcp_client/login/bloc/login_cubit.dart';
import 'package:tcp_client/login/bloc/login_state.dart';
import 'package:tcp_client/login/cubit/login_cubit.dart';
import 'package:tcp_client/login/cubit/login_state.dart';
import 'package:tcp_client/login/models/password.dart';
import 'package:tcp_client/login/models/username.dart';

View File

@ -8,7 +8,7 @@
import 'package:bloc/bloc.dart';
import 'package:formz/formz.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:tcp_client/register/bloc/register_state.dart';
import 'package:tcp_client/register/cubit/register_state.dart';
import 'package:tcp_client/register/models/password.dart';
import 'package:tcp_client/register/models/username.dart';
import 'package:tcp_client/repositories/common_models/useridentity.dart';

View File

@ -15,8 +15,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:formz/formz.dart';
import 'package:tcp_client/home/home_page.dart';
import 'package:tcp_client/register/bloc/register_cubit.dart';
import 'package:tcp_client/register/bloc/register_state.dart';
import 'package:tcp_client/register/cubit/register_cubit.dart';
import 'package:tcp_client/register/cubit/register_state.dart';
import 'package:tcp_client/register/view/register_form.dart';
import 'package:tcp_client/repositories/local_service_repository/local_service_repository.dart';
import 'package:tcp_client/repositories/tcp_repository/tcp_repository.dart';

View File

@ -8,8 +8,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:formz/formz.dart';
import 'package:tcp_client/register/bloc/register_cubit.dart';
import 'package:tcp_client/register/bloc/register_state.dart';
import 'package:tcp_client/register/cubit/register_cubit.dart';
import 'package:tcp_client/register/cubit/register_state.dart';
import 'package:tcp_client/register/models/password.dart';
import 'package:tcp_client/register/models/username.dart';

View File

@ -1,7 +1,7 @@
/*
* @Author : Linloir
* @Date : 2022-10-11 09:42:05
* @LastEditTime : 2022-10-12 16:57:50
* @LastEditTime : 2022-10-12 23:35:10
* @Description : TCP repository
*/