diff --git a/lib/chat/cubit/chat_cubit.dart b/lib/chat/cubit/chat_cubit.dart index 306ea1f..3a0a0dc 100644 --- a/lib/chat/cubit/chat_cubit.dart +++ b/lib/chat/cubit/chat_cubit.dart @@ -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 { final Map messageSendSubscriptionMap = {}; final Map fileFetchSubscriptionMap = {}; + + Future 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(); + 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 fetchHistory() async { diff --git a/lib/chat/model/chat_history.dart b/lib/chat/model/chat_history.dart index ac3e02c..9f0f691 100644 --- a/lib/chat/model/chat_history.dart +++ b/lib/chat/model/chat_history.dart @@ -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; diff --git a/lib/chat/view/common/file_box.dart b/lib/chat/view/common/file_box.dart index 97e7f1c..18dc34d 100644 --- a/lib/chat/view/common/file_box.dart +++ b/lib/chat/view/common/file_box.dart @@ -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, diff --git a/lib/chat/view/input_box/cubit/input_cubit.dart b/lib/chat/view/input_box/cubit/input_cubit.dart index bfd99f1..452d7b7 100644 --- a/lib/chat/view/input_box/cubit/input_cubit.dart +++ b/lib/chat/view/input_box/cubit/input_cubit.dart @@ -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 { targetid: chatCubit.userID, contenttype: MessageType.plaintext, content: state.input.value, - token: (await SharedPreferences.getInstance()).getInt('token')! )); emit(state.copyWith( status: FormzStatus.pure, diff --git a/lib/chat/view/input_box/input_box.dart b/lib/chat/view/input_box/input_box.dart index a20c8c8..4ab5b5d 100644 --- a/lib/chat/view/input_box/input_box.dart +++ b/lib/chat/view/input_box/input_box.dart @@ -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); } diff --git a/lib/repositories/common_models/message.dart b/lib/repositories/common_models/message.dart index 039451b..677c8ce 100644 --- a/lib/repositories/common_models/message.dart +++ b/lib/repositories/common_models/message.dart @@ -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; diff --git a/lib/repositories/local_service_repository/local_service_repository.dart b/lib/repositories/local_service_repository/local_service_repository.dart index ed625c9..d02d981 100644 --- a/lib/repositories/local_service_repository/local_service_repository.dart +++ b/lib/repositories/local_service_repository/local_service_repository.dart @@ -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 pickFile(FileType fileType) async { + Future 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(); - 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(); + // 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 storeMessages(List messages) async {