mirror of
https://github.com/Linloir/Simple-TCP-Client.git
synced 2025-12-17 00:38:11 +08:00
Bug Fix & Feature
- Fix the breakdown when sending huge files - Add md5 processing indicator when sending file
This commit is contained in:
parent
cf9e6b26b5
commit
01568cfe9b
@ -1,19 +1,24 @@
|
||||
/*
|
||||
* @Author : Linloir
|
||||
* @Date : 2022-10-13 14:03:56
|
||||
* @LastEditTime : 2022-10-18 11:25:04
|
||||
* @LastEditTime : 2022-10-18 17:09:55
|
||||
* @Description :
|
||||
*/
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:convert/convert.dart';
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:open_file/open_file.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:tcp_client/chat/cubit/chat_state.dart';
|
||||
import 'package:tcp_client/chat/model/chat_history.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/local_service_repository/models/local_file.dart';
|
||||
import 'package:tcp_client/repositories/tcp_repository/models/tcp_request.dart';
|
||||
import 'package:tcp_client/repositories/tcp_repository/models/tcp_response.dart';
|
||||
import 'package:tcp_client/repositories/tcp_repository/tcp_repository.dart';
|
||||
@ -34,23 +39,63 @@ class ChatCubit extends Cubit<ChatState> {
|
||||
final Map<String, StreamSubscription> messageSendSubscriptionMap = {};
|
||||
final Map<String, StreamSubscription> fileFetchSubscriptionMap = {};
|
||||
|
||||
|
||||
|
||||
Future<void> addMessage(Message message) async {
|
||||
var msg = message;
|
||||
if(msg.type == MessageType.file) {
|
||||
//wait until md5 is converted
|
||||
//Emit new state
|
||||
var newHistory = ChatHistory(
|
||||
message: msg,
|
||||
type: ChatHistoryType.outcome,
|
||||
status: ChatHistoryStatus.processing
|
||||
);
|
||||
var newHistoryList = [newHistory, ...state.chatHistory];
|
||||
emit(state.copyWith(chatHistory: newHistoryList));
|
||||
var file = msg.payload!.file;
|
||||
var md5Output = AccumulatorSink<Digest>();
|
||||
ByteConversionSink md5Input = md5.startChunkedConversion(md5Output);
|
||||
await for(var bytes in file.openRead()) {
|
||||
md5Input.add(bytes);
|
||||
}
|
||||
md5Input.close();
|
||||
var loadedFile = LocalFile(
|
||||
file: file,
|
||||
filemd5: md5Output.events.single.toString()
|
||||
);
|
||||
msg = msg.copyWith(
|
||||
payload: loadedFile
|
||||
);
|
||||
}
|
||||
//Store locally
|
||||
localServiceRepository.storeMessages([message]);
|
||||
localServiceRepository.storeMessages([msg]);
|
||||
//Send to server
|
||||
tcpRepository.pushRequest(SendMessageRequest(
|
||||
message: message,
|
||||
message: msg,
|
||||
token: (await SharedPreferences.getInstance()).getInt('token')
|
||||
));
|
||||
//Emit new state
|
||||
var newHistory = ChatHistory(
|
||||
message: message,
|
||||
message: msg,
|
||||
type: ChatHistoryType.outcome,
|
||||
status: ChatHistoryStatus.sending
|
||||
);
|
||||
var newHistoryList = [newHistory, ...state.chatHistory];
|
||||
emit(state.copyWith(chatHistory: newHistoryList));
|
||||
_bindSubscriptionForSending(messageMd5: message.contentmd5);
|
||||
if(msg.type == MessageType.file) {
|
||||
//Remove mock history
|
||||
var newHistoryList = [...state.chatHistory];
|
||||
var index = newHistoryList.indexWhere((element) => element.message.contentmd5 == msg.contentmd5);
|
||||
if(index == -1) {
|
||||
return;
|
||||
}
|
||||
newHistoryList[index] = newHistory;
|
||||
emit(state.copyWith(chatHistory: newHistoryList));
|
||||
}
|
||||
else {
|
||||
var newHistoryList = [newHistory, ...state.chatHistory];
|
||||
emit(state.copyWith(chatHistory: newHistoryList));
|
||||
}
|
||||
_bindSubscriptionForSending(messageMd5: msg.contentmd5);
|
||||
}
|
||||
|
||||
Future<void> fetchHistory() async {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* @Author : Linloir
|
||||
* @Date : 2022-10-14 14:55:20
|
||||
* @LastEditTime : 2022-10-14 15:26:25
|
||||
* @LastEditTime : 2022-10-18 15:19:46
|
||||
* @Description :
|
||||
*/
|
||||
|
||||
@ -9,7 +9,7 @@ import 'package:equatable/equatable.dart';
|
||||
import 'package:tcp_client/repositories/common_models/message.dart';
|
||||
|
||||
enum ChatHistoryType { outcome, income }
|
||||
enum ChatHistoryStatus { none, sending, downloading, done, failed }
|
||||
enum ChatHistoryStatus { none, processing, sending, downloading, done, failed }
|
||||
|
||||
class ChatHistory extends Equatable {
|
||||
final Message message;
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
/*
|
||||
* @Author : Linloir
|
||||
* @Date : 2022-10-14 17:07:13
|
||||
* @LastEditTime : 2022-10-15 11:40:47
|
||||
* @LastEditTime : 2022-10-18 15:44:34
|
||||
* @Description :
|
||||
*/
|
||||
|
||||
import 'package:easy_debounce/easy_debounce.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:loading_indicator/loading_indicator.dart';
|
||||
import 'package:tcp_client/chat/cubit/chat_cubit.dart';
|
||||
import 'package:tcp_client/chat/model/chat_history.dart';
|
||||
|
||||
@ -22,7 +23,7 @@ class FileBox extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
onTap: (history.status == ChatHistoryStatus.downloading || history.status == ChatHistoryStatus.sending) ? null : () {
|
||||
onTap: (history.status == ChatHistoryStatus.downloading || history.status == ChatHistoryStatus.sending || history.status == ChatHistoryStatus.processing) ? null : () {
|
||||
EasyDebounce.debounce(
|
||||
'findfile${history.message.contentmd5}',
|
||||
const Duration(milliseconds: 500),
|
||||
@ -49,7 +50,15 @@ class FileBox extends StatelessWidget {
|
||||
Icons.refresh_rounded,
|
||||
size: 24,
|
||||
color: history.type == ChatHistoryType.income ? Colors.red[800] : Colors.white.withOpacity(0.8),
|
||||
) :
|
||||
) : history.status == ChatHistoryStatus.processing ?
|
||||
SizedBox(
|
||||
height: 18.0,
|
||||
width: 18.0,
|
||||
child: LoadingIndicator(
|
||||
indicatorType: Indicator.ballPulseSync,
|
||||
colors: [Colors.white.withOpacity(0.8)],
|
||||
),
|
||||
) :
|
||||
SizedBox(
|
||||
height: 18.0,
|
||||
width: 18.0,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* @Author : Linloir
|
||||
* @Date : 2022-10-14 21:57:05
|
||||
* @LastEditTime : 2022-10-18 11:25:09
|
||||
* @LastEditTime : 2022-10-18 15:30:09
|
||||
* @Description :
|
||||
*/
|
||||
|
||||
@ -33,7 +33,6 @@ class MessageInputCubit extends Cubit<MessageInputState> {
|
||||
targetid: chatCubit.userID,
|
||||
contenttype: MessageType.plaintext,
|
||||
content: state.input.value,
|
||||
token: (await SharedPreferences.getInstance()).getInt('token')!
|
||||
));
|
||||
emit(state.copyWith(
|
||||
status: FormzStatus.pure,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* @Author : Linloir
|
||||
* @Date : 2022-10-14 17:54:30
|
||||
* @LastEditTime : 2022-10-18 11:25:12
|
||||
* @LastEditTime : 2022-10-18 15:30:05
|
||||
* @Description :
|
||||
*/
|
||||
|
||||
@ -16,6 +16,7 @@ import 'package:tcp_client/chat/view/input_box/cubit/input_cubit.dart';
|
||||
import 'package:tcp_client/chat/view/input_box/cubit/input_state.dart';
|
||||
import 'package:tcp_client/chat/view/input_box/model/input.dart';
|
||||
import 'package:tcp_client/repositories/common_models/message.dart';
|
||||
import 'package:tcp_client/repositories/local_service_repository/models/local_file.dart';
|
||||
|
||||
class InputBox extends StatelessWidget {
|
||||
InputBox({super.key});
|
||||
@ -71,10 +72,9 @@ class InputBox extends StatelessWidget {
|
||||
var newMessage = Message(
|
||||
userid: (await SharedPreferences.getInstance()).getInt('userid')!,
|
||||
targetid: chatCubit.userID,
|
||||
content: basename(file.file.path),
|
||||
content: basename(file.path),
|
||||
contenttype: MessageType.file,
|
||||
payload: file,
|
||||
token: (await SharedPreferences.getInstance()).getInt('token')!
|
||||
payload: LocalFile(file: file, filemd5: ""),
|
||||
);
|
||||
chatCubit.addMessage(newMessage);
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* @Author : Linloir
|
||||
* @Date : 2022-10-11 10:30:05
|
||||
* @LastEditTime : 2022-10-18 14:20:02
|
||||
* @LastEditTime : 2022-10-18 16:53:04
|
||||
* @Description :
|
||||
*/
|
||||
|
||||
@ -35,13 +35,31 @@ class Message extends JSONEncodable {
|
||||
late final String? _filemd5;
|
||||
final LocalFile? _payload;
|
||||
|
||||
Message._internal({
|
||||
required int userid,
|
||||
required int targetid,
|
||||
required MessageType contenttype,
|
||||
required String content,
|
||||
required int timestamp,
|
||||
required String contentmd5,
|
||||
required String? filemd5,
|
||||
required LocalFile? payload
|
||||
}):
|
||||
_userid = userid,
|
||||
_targetid = targetid,
|
||||
_contenttype = contenttype,
|
||||
_content = content,
|
||||
_timestamp = timestamp,
|
||||
_contentmd5 = contentmd5,
|
||||
_filemd5 = filemd5,
|
||||
_payload = payload;
|
||||
|
||||
Message({
|
||||
required int userid,
|
||||
required int targetid,
|
||||
required MessageType contenttype,
|
||||
required String content,
|
||||
LocalFile? payload,
|
||||
required int token
|
||||
LocalFile? payload
|
||||
}):
|
||||
_userid = userid,
|
||||
_targetid = targetid,
|
||||
@ -70,6 +88,32 @@ class Message extends JSONEncodable {
|
||||
_contentmd5 = jsonObject['md5encoded'] as String,
|
||||
_filemd5 = jsonObject['filemd5'] as String?,
|
||||
_payload = payload;
|
||||
|
||||
Message copyWith({
|
||||
int? userid,
|
||||
int? targetid,
|
||||
MessageType? contenttype,
|
||||
String? content,
|
||||
int? timestamp,
|
||||
LocalFile? payload
|
||||
}) {
|
||||
return Message._internal(
|
||||
userid: userid ?? _userid,
|
||||
targetid: targetid ?? _targetid,
|
||||
contenttype: contenttype ?? _contenttype,
|
||||
content: content == null ? _content : base64.encode(utf8.encode(content)),
|
||||
timestamp: timestamp ?? _timestamp,
|
||||
contentmd5: content != null || userid != null || targetid != null ?
|
||||
md5.convert(
|
||||
utf8.encode(content ?? contentDecoded).toList()
|
||||
..addAll(Uint8List(4)..buffer.asInt32List()[0] = userid ?? _userid)
|
||||
..addAll(Uint8List(4)..buffer.asInt32List()[0] = targetid ?? _targetid)
|
||||
..addAll(Uint8List(4)..buffer.asInt32List()[0] = timestamp ?? _timestamp)
|
||||
).toString() : _contentmd5,
|
||||
filemd5: payload?.filemd5 ?? _filemd5,
|
||||
payload: payload ?? _payload
|
||||
);
|
||||
}
|
||||
|
||||
int get senderID => _userid;
|
||||
int get recieverID => _targetid;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* @Author : Linloir
|
||||
* @Date : 2022-10-11 10:56:02
|
||||
* @LastEditTime : 2022-10-18 14:35:25
|
||||
* @LastEditTime : 2022-10-18 15:13:27
|
||||
* @Description : Local Service Repository
|
||||
*/
|
||||
|
||||
@ -109,23 +109,24 @@ class LocalServiceRepository {
|
||||
}
|
||||
|
||||
//Calls on the system to open the file
|
||||
Future<LocalFile?> pickFile(FileType fileType) async {
|
||||
Future<File?> pickFile(FileType fileType) async {
|
||||
var filePickResult = await FilePicker.platform.pickFiles(
|
||||
type: fileType,
|
||||
allowMultiple: false,
|
||||
);
|
||||
if (filePickResult == null) return null;
|
||||
var file = File(filePickResult.files.single.path!);
|
||||
var md5Output = AccumulatorSink<Digest>();
|
||||
ByteConversionSink md5Input = md5.startChunkedConversion(md5Output);
|
||||
await for(var bytes in file.openRead()) {
|
||||
md5Input.add(bytes);
|
||||
}
|
||||
md5Input.close();
|
||||
return LocalFile(
|
||||
file: file,
|
||||
filemd5: md5Output.events.single.toString()
|
||||
);
|
||||
// var md5Output = AccumulatorSink<Digest>();
|
||||
// ByteConversionSink md5Input = md5.startChunkedConversion(md5Output);
|
||||
// await for(var bytes in file.openRead()) {
|
||||
// md5Input.add(bytes);
|
||||
// }
|
||||
// md5Input.close();
|
||||
// return LocalFile(
|
||||
// file: file,
|
||||
// filemd5: md5Output.events.single.toString()
|
||||
// );
|
||||
return file;
|
||||
}
|
||||
|
||||
Future<void> storeMessages(List<Message> messages) async {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user