implement slice transmitting of chat image

main
htylight 2023-10-01 18:40:14 +08:00
parent 849e7d5067
commit 830a16bd27
19 changed files with 904 additions and 521 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

@ -39,7 +39,7 @@ class ChatSetting {
@HiveType(typeId: 1)
class MessageT {
@HiveField(0)
int msgId;
String msgId;
@HiveField(1)
String senderId;
@ -70,9 +70,37 @@ class MessageT {
);
}
@HiveType(typeId: 2)
class AttachmentProgress {
// denote the number of chunk has sent or recieved.
// if 0, means no chunk has received or sent.
@HiveField(0)
int hasChunkNum;
@HiveField(1)
int totalChunkNum;
@HiveField(2)
double progress;
@HiveField(3)
bool isValid;
@HiveField(4)
bool isPause;
AttachmentProgress(
this.hasChunkNum,
this.totalChunkNum,
this.progress,
this.isValid,
this.isPause,
);
}
/// message type:
/// 1. text/multipart: text with/without images or videos
/// 2. voice
/// 3. video
/// 4. voice-call
/// 5. video-call
/// 5. video-call

View File

@ -69,7 +69,7 @@ class MessageTAdapter extends TypeAdapter<MessageT> {
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return MessageT(
fields[0] as int,
fields[0] as String,
fields[1] as String,
fields[2] == null ? '' : fields[2] as String,
fields[3] as String,
@ -109,3 +109,49 @@ class MessageTAdapter extends TypeAdapter<MessageT> {
runtimeType == other.runtimeType &&
typeId == other.typeId;
}
class AttachmentProgressAdapter extends TypeAdapter<AttachmentProgress> {
@override
final int typeId = 2;
@override
AttachmentProgress read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return AttachmentProgress(
fields[0] as int,
fields[1] as int,
fields[2] as double,
fields[3] as bool,
fields[4] as bool,
);
}
@override
void write(BinaryWriter writer, AttachmentProgress obj) {
writer
..writeByte(5)
..writeByte(0)
..write(obj.hasChunkNum)
..writeByte(1)
..write(obj.totalChunkNum)
..writeByte(2)
..write(obj.progress)
..writeByte(3)
..write(obj.isValid)
..writeByte(4)
..write(obj.isPause);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is AttachmentProgressAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

View File

@ -32,14 +32,19 @@ class HiveDatabase {
encryptionCipher: HiveAesCipher(encryptionKeyUint8List),
compactionStrategy: (entries, deletedEntries) => entries > 200,
);
await Hive.openBox<int>('msg_index_${chatBox.contactId}');
}
await Hive.openBox<AttachmentProgress>('attachment_send');
await Hive.openBox<AttachmentProgress>('attachment_receive');
_isInitialised = true;
}
static void registerAdapter() {
Hive.registerAdapter(ChatSettingAdapter());
Hive.registerAdapter(MessageTAdapter());
Hive.registerAdapter(AttachmentProgressAdapter());
}
static Future<Box<MessageT>> openMessageBox(String contactId) async {

View File

@ -1,20 +1,14 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:together_mobile/database/hive_database.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
import 'package:web_socket_channel/status.dart' as status;
import 'package:together_mobile/models/route_state_model.dart';
import 'package:together_mobile/database/box_type.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/user_model.dart';
import 'package:together_mobile/notification_api.dart';
import 'package:together_mobile/utils/ws_receive_callback.dart';
enum SocketStatus {
connected,
@ -23,6 +17,8 @@ enum SocketStatus {
closed,
}
// const chunkSize = 1024 * 1024;
class WebSocketManager extends ChangeNotifier {
late Uri wsUrl;
late WebSocketChannel channel;
@ -35,6 +31,7 @@ class WebSocketManager extends ChangeNotifier {
int reconnectTimes = 0;
Duration heartBeatTimeout = const Duration(seconds: 4);
Duration reconnectTimeout = const Duration(seconds: 3);
Map<String, Timer> sendImageTimer = {};
void connect(String userId, bool isReconnect) {
id = userId;
@ -66,7 +63,7 @@ class WebSocketManager extends ChangeNotifier {
reconnectTimes = 0;
}
void onData(jsonData) {
void onData(jsonData) async {
// If socket can receive msg, that means connection is estabilished
socketStatus = SocketStatus.connected;
notifyListeners();
@ -79,10 +76,12 @@ class WebSocketManager extends ChangeNotifier {
heartBeatInspect();
Map<String, dynamic> data = json.decode(jsonData);
// Map<String, dynamic> data = json.decode(jsonData);
Map<String, dynamic> data =
await compute((message) => json.decode(message), jsonData);
switch (data['event']) {
case 'friend-chat-msg':
receiveFriendMsg(data, true);
await receiveFriendMsg(data, true);
case 'apply-friend':
receiveApplyFriend(data);
case 'friend-added':
@ -95,6 +94,10 @@ class WebSocketManager extends ChangeNotifier {
receiveGroupChatCreation(data);
case 'group-chat-msg':
receiveGroupChatMsg(data, true);
case 'pull-chat-image':
receivePullChatImage(data);
case 'chat-image-send-ok':
receiveCheckChatImage(data);
}
}
@ -198,245 +201,50 @@ class WebSocketManager extends ChangeNotifier {
}
});
}
}
void receiveFriendMsg(Map<String, dynamic> msg, bool isShowNotification) async {
print('=================收到了好友信息事件==================');
print(msg);
print('=======================================');
String senderId = msg['senderId'] as String;
Box<ChatSetting> chatSettingBox = Hive.box<ChatSetting>('chat_setting');
Box<MessageT> messageTBox = await HiveDatabase.openMessageBox(senderId);
ChatSetting? chatSetting = chatSettingBox.get(senderId);
DateTime dateTime = DateTime.parse(msg['dateTime'] as String);
if (chatSetting == null) {
chatSettingBox.put(
senderId,
ChatSetting(
senderId,
0,
false,
true,
false,
dateTime,
1,
),
);
} else {
chatSetting.isOpen = true;
chatSetting.latestDateTime = dateTime;
chatSetting.unreadCount++;
chatSettingBox.put(senderId, chatSetting);
}
List<String> attachments = List.from(msg['attachments']);
final DateTime now = DateTime.parse(msg['dateTime'] as String);
messageTBox.add(
MessageT(
msg['msgId'] as int,
senderId,
msg['text'],
msg['type'],
now,
msg['isShowTime'],
attachments,
),
);
if (!isShowNotification) {
return;
}
String name = getIt.get<Contact>().friends[senderId]!.friendRemark.isEmpty
? getIt.get<ContactAccountProfile>().friends[senderId]!.nickname
: getIt.get<Contact>().friends[senderId]!.friendRemark;
String routeName = getIt.get<RouteState>().currentPathName;
String avatar = getIt.get<ContactAccountProfile>().friends[senderId]!.avatar;
if (!getIt.get<RouteState>().isVisible) {
NotificationAPI.showMessageNotifications(
senderId: senderId,
name: name,
avatar: avatar,
text: msg['text'] as String,
);
} else if (routeName == 'Message') {
if (!getIt.get<RouteState>().query.containsValue(senderId)) {
NotificationAPI.showMessageNotifications(
senderId: senderId,
name: name,
avatar: avatar,
text: msg['text'] as String,
);
void addSendImageTimer(String filename, int totalChunkNum) {
if (sendImageTimer.containsKey(filename)) {
sendImageTimer[filename]!.cancel();
sendImageTimer.remove(filename);
}
} else if (routeName != 'Chat') {
NotificationAPI.showMessageNotifications(
senderId: senderId,
name: name,
avatar: avatar,
text: msg['text'] as String,
sendImageTimer[filename] = Timer(
heartBeatTimeout,
() async {
late Box<AttachmentProgress> attachmentLoadingBox;
try {
attachmentLoadingBox =
Hive.box<AttachmentProgress>('attachment_receive');
} catch (_) {
attachmentLoadingBox =
await Hive.openBox<AttachmentProgress>('attachment_receive');
}
AttachmentProgress? at = attachmentLoadingBox.get(filename);
if (at == null) {
attachmentLoadingBox.put(
filename,
AttachmentProgress(
0,
totalChunkNum,
0,
false,
true,
),
);
} else {
at.isPause = true;
attachmentLoadingBox.put(filename, at);
}
},
);
}
}
void receiveApplyFriend(Map<String, dynamic> msg) {
print('=================收到了申请好友事件==================');
print(msg);
print('=======================================');
getIt.get<ApplyList>().addJson(msg);
}
void receiveFriendAdded(Map<String, dynamic> msg) {
print('=================收到了申请好友通过事件==================');
print(msg);
print('=======================================');
getIt.get<Contact>().addFriend(msg['friendId'], msg['setting']);
getIt
.get<ContactAccountProfile>()
.addFriendAccountProfile(msg['friendId'], msg['accountProfile']);
}
void receiveFriendDeleted(Map<String, dynamic> msg) {
print('=================收到了解除好友事件==================');
print(msg);
print('=======================================');
getIt.get<Contact>().removeFriend(msg['friendId']);
getIt.get<ContactAccountProfile>().removeFriend(msg['friendId']);
}
void receiveChatImages(Map<String, dynamic> msg) async {
print('=================收到了聊天图片事件==================');
print(msg);
print('=======================================');
String chatImageDir = getIt.get<UserProfile>().baseImageDir;
File file = File('$chatImageDir/${msg['filename']}');
if (await file.exists()) {
return;
} else {
await file.create(recursive: true);
await file.writeAsBytes(List<int>.from(msg['bytes']));
}
}
void receiveGroupChatCreation(Map<String, dynamic> msg) {
print('=================收到了群聊邀请事件==================');
print(msg);
print('=======================================');
String groupChatId = msg['groupChat']['id'];
getIt.get<Contact>().addGroupChat(groupChatId);
getIt.get<ContactAccountProfile>().addGroupChatProfile(groupChatId, msg);
}
void receiveGroupChatMsg(
Map<String, dynamic> msg, bool isShowNotification) async {
print('=================收到了群聊信息事件==================');
print(msg);
print('=======================================');
String senderId = msg['senderId'] as String;
String groupChatId = msg['groupChatId'] as String;
Box<ChatSetting> chatSettingBox = Hive.box<ChatSetting>('chat_setting');
Box<MessageT> messageTBox = await HiveDatabase.openMessageBox(groupChatId);
ChatSetting? chatSetting = chatSettingBox.get(groupChatId);
DateTime dateTime = DateTime.parse(msg['dateTime'] as String);
getIt.get<ContactAccountProfile>().addGroupChatMemberProfile(
groupChatId,
msg['senderId'],
{
'avatar': msg['avatar'],
'nickname': msg['nickname'],
'remarkInGroupChat': msg['remarkInGroupChat'],
},
);
if (chatSetting == null) {
chatSettingBox.put(
groupChatId,
ChatSetting(
groupChatId,
1,
false,
true,
false,
dateTime,
1,
),
);
} else {
chatSetting.isOpen = true;
chatSetting.latestDateTime = dateTime;
chatSetting.unreadCount++;
chatSettingBox.put(groupChatId, chatSetting);
}
List<String> attachments = List.from(msg['attachments']);
final DateTime now = DateTime.parse(msg['dateTime'] as String);
messageTBox.add(
MessageT(
msg['msgId'] as int,
senderId,
msg['text'],
msg['type'],
now,
msg['isShowTime'],
attachments,
),
);
if (!isShowNotification) {
return;
}
String avatar =
getIt.get<ContactAccountProfile>().groupChats[groupChatId]!.avatar;
late String name;
if (getIt.get<Contact>().friends.containsKey(senderId)) {
if (getIt.get<Contact>().friends[senderId]!.friendRemark.isNotEmpty) {
name = getIt.get<Contact>().friends[senderId]!.friendRemark;
} else if ((msg['remarkInGroupChat'] as String).isNotEmpty) {
name = msg['remarkInGroupChat'];
} else {
name = msg['nickname'];
void removeSendImageTimer(String filename) {
if (sendImageTimer.containsKey(filename)) {
sendImageTimer[filename]!.cancel();
}
} else {
name = (msg['remarkInGroupChat'] as String).isNotEmpty
? msg['remarkInGroupChat']
: msg['nickname'];
}
String routeName = getIt.get<RouteState>().currentPathName;
if (!getIt.get<RouteState>().isVisible) {
NotificationAPI.showMessageNotifications(
senderId: senderId,
name: name,
avatar: avatar,
text: msg['text'] as String,
groupChatId: groupChatId,
);
} else if (routeName == 'Message') {
if (!getIt.get<RouteState>().query.containsValue(senderId)) {
NotificationAPI.showMessageNotifications(
senderId: senderId,
name: name,
avatar: avatar,
text: msg['text'] as String,
groupChatId: groupChatId,
);
}
} else if (routeName != 'Chat') {
NotificationAPI.showMessageNotifications(
senderId: senderId,
name: name,
avatar: avatar,
text: msg['text'] as String,
groupChatId: groupChatId,
);
sendImageTimer.remove(filename);
}
}

View File

@ -12,3 +12,14 @@ Future<Map<String, dynamic>> getUnreceivedMsg(String userId) async {
return response.data;
}
Future<Map<String, dynamic>> uploadChatAttachment(
Map<String, dynamic> data,
) async {
Response response = await request.post(
'/message/attachment',
data: data,
);
return response.data;
}

View File

@ -39,12 +39,9 @@ final chatRouter = GoRoute(
parentNavigatorKey: rootNavigatorKey,
builder: (context, state) {
Map<String, Object> extra = state.extra as Map<String, Object>;
for (var a in extra['attachmentItems']! as List<AttachmentItem>) {
print(a.id);
}
return ImageViewScreen(
attachmentItems:
extra['attachmentItems']! as List<AttachmentItem>,
attachments:
extra['attachments']! as List<String>,
initialIndex: extra['index']! as int,
);
},

View File

@ -10,6 +10,7 @@ import 'package:together_mobile/database/hive_database.dart';
import 'package:together_mobile/request/message.dart';
import 'package:together_mobile/screens/chat/components/group_chat_chat_tile.dart';
import 'package:together_mobile/utils/ws_receive_callback.dart';
import 'components/friend_chat_tile.dart';
import 'components/add_menu.dart';
import 'package:together_mobile/database/box_type.dart';

View File

@ -1,10 +1,8 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:hive/hive.dart';
import 'package:together_mobile/common/constants.dart';
import 'package:together_mobile/database/box_type.dart';
import 'package:together_mobile/database/hive_database.dart';
class RowFloatingButtons extends StatelessWidget {
@ -75,7 +73,7 @@ class RowFloatingButtons extends StatelessWidget {
),
FloatingActionButton(
onPressed: () async {
HiveDatabase.openMessageBox(friendId);
await HiveDatabase.openMessageBox(friendId);
// ignore: use_build_context_synchronously
context.pushReplacementNamed(
'Message',

View File

@ -0,0 +1,119 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:together_mobile/common/constants.dart';
import 'package:together_mobile/database/box_type.dart';
import 'package:together_mobile/models/init_get_it.dart';
import 'package:together_mobile/models/user_model.dart';
class AttachmentContainer extends StatefulWidget {
const AttachmentContainer({
super.key,
required this.isMyself,
required this.filename,
required this.onTapImage,
});
final bool isMyself;
final String filename;
final Function(String) onTapImage;
@override
State<AttachmentContainer> createState() => _AttachmentContainerState();
}
class _AttachmentContainerState extends State<AttachmentContainer>
with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
bool get wantKeepAlive => true;
@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this);
_animation = Tween<double>(begin: 0.0, end: 1.0).animate(_controller);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
super.build(context);
return Container(
margin: const EdgeInsets.only(
bottom: 15,
),
constraints: const BoxConstraints(
maxHeight: 150,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.0),
),
child: ValueListenableBuilder(
valueListenable: widget.isMyself
? Hive.box<AttachmentProgress>('attachment_send')
.listenable(keys: [widget.filename])
: Hive.box<AttachmentProgress>('attachment_receive')
.listenable(keys: [widget.filename]),
builder: (context, apBox, _) {
double progress = apBox.get(widget.filename)!.progress;
String filePath =
'${getIt.get<UserProfile>().baseImageDir}/${widget.filename}';
bool isFileExists = File(filePath).existsSync();
_controller.animateTo(
progress,
duration: const Duration(milliseconds: 500),
);
return Stack(
children: [
GestureDetector(
onTap: () {
widget.onTapImage(widget.filename);
},
child: progress == 1.0 || isFileExists
? Image.file(File(filePath))
: Image.asset('assets/images/loading.gif'),
),
if (progress < 1)
Positioned.fill(
child: Stack(
alignment: Alignment.center,
children: [
Container(
alignment: Alignment.center,
constraints: const BoxConstraints.expand(),
color: const Color.fromARGB(255, 32, 32, 32)
.withOpacity(0.3),
child: Text(
'${(_animation.value * 100).toStringAsFixed(0)}%',
style: const TextStyle(
fontSize: 20,
color: kUnAvailableColor,
),
),
),
CircularProgressIndicator(
value: _animation.value,
color: kSecondaryColor,
strokeWidth: 4,
),
],
),
),
],
);
},
),
);
}
}

View File

@ -1,6 +1,3 @@
import 'dart:async';
import 'dart:io';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
@ -12,7 +9,7 @@ import 'package:together_mobile/models/contact_model.dart';
import 'package:together_mobile/models/init_get_it.dart';
import 'package:together_mobile/models/user_model.dart';
import 'package:together_mobile/request/server.dart';
import 'package:together_mobile/screens/message/image_view_screen/image_view_screen.dart';
import 'package:together_mobile/screens/message/components/attachment_container.dart';
class FriendMessageBubble extends StatefulWidget {
const FriendMessageBubble({
@ -26,14 +23,13 @@ class FriendMessageBubble extends StatefulWidget {
required this.isShowTime,
required this.text,
required this.attachments,
required this.attachmentItems,
required this.allAttachments,
});
final int index, length;
final String contactId, senderId, type, dateTime, text;
final bool isShowTime;
final List<String> attachments;
final List<AttachmentItem> attachmentItems;
final List<String> attachments, allAttachments;
@override
State<FriendMessageBubble> createState() => _FriendMessageBubbleState();
@ -41,65 +37,14 @@ class FriendMessageBubble extends StatefulWidget {
class _FriendMessageBubbleState extends State<FriendMessageBubble> {
// add late here so you can access widget instance
List<bool> _isImagesLoaded = [];
final List<Timer> _timerList = [];
late bool _isHideMsg;
@override
void initState() {
super.initState();
_isImagesLoaded = List.filled(widget.attachments.length, false);
for (var i = 0; i < widget.attachments.length; i++) {
String imagePath =
'${getIt.get<UserProfile>().baseImageDir}/${widget.attachments[i]}';
File file = File(imagePath);
if (file.existsSync()) {
_isImagesLoaded[i] = true;
} else {
_isImagesLoaded[i] = false;
_timerList.add(
Timer.periodic(
const Duration(milliseconds: 200),
(timer) {
if ((file.existsSync())) {
setState(() {
_isImagesLoaded[i] = true;
});
timer.cancel();
}
},
),
);
}
}
final chatSettingBox = Hive.box<ChatSetting>('chat_setting');
late final chatSetting = chatSettingBox.get(widget.contactId);
_isHideMsg = chatSetting!.isHideMsg;
if (_isHideMsg) {
if (widget.index + 1 == widget.length) {
_isHideMsg = false;
Timer(
const Duration(milliseconds: 3500),
() {
setState(() {
_isHideMsg = true;
});
},
);
}
}
}
@override
void dispose() {
for (var element in _timerList) {
element.cancel();
}
super.dispose();
}
@override
@ -170,7 +115,6 @@ class _FriendMessageBubbleState extends State<FriendMessageBubble> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (_isHideMsg) const Text('消息已隐藏'),
// text message content
if (widget.text.isNotEmpty && !_isHideMsg)
Text(
@ -180,58 +124,16 @@ class _FriendMessageBubbleState extends State<FriendMessageBubble> {
const SizedBox(
height: 10,
),
// image content if have
// image content if has
if (widget.attachments.isNotEmpty && !_isHideMsg)
...List.generate(
widget.attachments.length,
(int index) {
int attachmentItemIndex =
widget.attachmentItems.indexWhere(
(element) =>
element.resource ==
widget.attachments[index],
);
int heroTagId = widget
.attachmentItems[attachmentItemIndex].id;
return Container(
margin: const EdgeInsets.only(
bottom: 15,
),
constraints: const BoxConstraints(
maxHeight: 120,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.0),
),
child: _isImagesLoaded[index]
? GestureDetector(
onTap: () {
_onTapImage(
context,
widget.attachments[index],
);
},
child: Hero(
tag: heroTagId,
child: Image.file(
File(
'${getIt.get<UserProfile>().baseImageDir}/${widget.attachments[index]}',
),
),
),
)
: Container(
width: 20,
height: 40,
padding: const EdgeInsets.symmetric(
vertical: 10,
),
child:
const CircularProgressIndicator(
color: kSecondaryColor,
strokeWidth: 3.0,
),
),
String filename = widget.attachments[index];
return AttachmentContainer(
isMyself: !isFriend,
filename: filename,
onTapImage: _onTapImage,
);
},
),
@ -261,15 +163,15 @@ class _FriendMessageBubbleState extends State<FriendMessageBubble> {
);
}
void _onTapImage(BuildContext context, String attachment) {
int attachmentIndex = widget.attachmentItems.indexWhere(
(element) => element.resource == attachment,
void _onTapImage(String attachment) {
int attachmentIndex = widget.allAttachments.indexWhere(
(element) => element == attachment,
);
context.pushNamed(
'ImageView',
extra: {
'attachmentItems': widget.attachmentItems,
'attachments': widget.allAttachments,
'index': attachmentIndex,
},
);

View File

@ -16,23 +16,9 @@ import 'package:together_mobile/models/user_model.dart';
import 'package:together_mobile/models/websocket_model.dart';
import 'package:together_mobile/utils/app_dir.dart';
import 'package:together_mobile/utils/format_datetime.dart';
import 'package:together_mobile/utils/ws_receive_callback.dart';
import 'input_icon_button.dart';
class SendMessage {
final int type;
final List<String> receivers;
final List<String> attachments;
final String dir;
SendMessage(this.type, this.receivers, this.attachments, this.dir);
SendMessage.fromJson(Map<String, dynamic> json)
: type = json['type'],
receivers = json['receivers'],
attachments = json['attachments'],
dir = json['dir'];
}
class MessageInputBox extends StatefulWidget {
const MessageInputBox({
super.key,
@ -54,7 +40,7 @@ class _MessageInputBoxState extends State<MessageInputBox> {
final ImagePicker _picker = ImagePicker();
bool _hasMsg = false;
late Box<ChatSetting> _chatBox;
late Box<ChatSetting> _chatSettingBox;
late Box<MessageT> _messageTBox;
List<XFile> _imageFileList = [];
@ -62,7 +48,7 @@ class _MessageInputBoxState extends State<MessageInputBox> {
@override
void initState() {
super.initState();
_chatBox = Hive.box<ChatSetting>('chat_setting');
_chatSettingBox = Hive.box<ChatSetting>('chat_setting');
_messageTBox = Hive.box<MessageT>('message_${widget.contactId}');
}
@ -231,20 +217,43 @@ class _MessageInputBoxState extends State<MessageInputBox> {
String text = _controller.text;
String type = 'text/multipart';
List<String> attachments = [];
Box<AttachmentProgress> apsBox = Hive.box('attachment_send');
var chatSetting = _chatSettingBox.get(widget.contactId);
if (chatSetting == null) {
_chatSettingBox.put(
widget.contactId,
ChatSetting(
widget.contactId,
widget.chatType,
false,
true,
false,
now,
0,
),
);
} else {
chatSetting.latestDateTime = now;
chatSetting.unreadCount = 0;
chatSetting.isOpen = true;
_chatSettingBox.put(widget.contactId, chatSetting);
}
String dirTime = formatDirTime(now);
if (_imageFileList.isNotEmpty) {
String dirTime = formatDirTime(now);
for (var i = 0; i < _imageFileList.length; i++) {
attachments.add('$dirTime/${getRandomFilename()}');
String filename = '$dirTime/${getRandomFilename()}';
int totalChunkNum =
((await _imageFileList[i].length()) / chunkSize).ceil();
attachments.add(filename);
apsBox.put(
filename, AttachmentProgress(0, totalChunkNum, 0, true, false));
}
}
late int msgId;
if (_messageTBox.length == 0) {
msgId = 0;
} else {
msgId = _messageTBox.length - 1;
}
final msgId = formatMsgIDFromTime(now);
_messageTBox.add(
MessageT(
@ -258,6 +267,10 @@ class _MessageInputBoxState extends State<MessageInputBox> {
),
);
Box<int> msgIndexBox =
await Hive.openBox<int>('msg_index_${widget.contactId}');
msgIndexBox.put(msgId, _messageTBox.length - 1);
Future.delayed(
const Duration(milliseconds: 50),
() => widget.scrollController.animateTo(
@ -278,32 +291,31 @@ class _MessageInputBoxState extends State<MessageInputBox> {
};
if (widget.chatType == 0) {
if (attachments.isNotEmpty) {
String baseImageDir = getIt.get<UserProfile>().baseImageDir;
for (var i = 0; i < attachments.length; i++) {
final dir = Directory('$baseImageDir/$dirTime');
if (!(await dir.exists())) {
await dir.create(recursive: true);
}
await _imageFileList[i].saveTo('$baseImageDir/${attachments[i]}');
}
}
msg['event'] = 'friend-chat-msg';
msg['receiverId'] = widget.contactId;
getIt.get<WebSocketManager>().channel.sink.add(json.encode(msg));
if (attachments.isNotEmpty) {
String baseImageDir = getIt.get<UserProfile>().baseImageDir;
List<String> encodedDatas = await compute(
bytes2json,
(
0,
[widget.contactId],
attachments,
_imageFileList,
baseImageDir,
),
);
for (final data in encodedDatas) {
getIt.get<WebSocketManager>().channel.sink.add(data);
}
}
} else {
String baseImageDir = getIt.get<UserProfile>().baseImageDir;
List<String> receiverIds = getIt
.get<ContactAccountProfile>()
.groupChats[widget.contactId]!
.members;
if (attachments.isNotEmpty) {
String baseImageDir = getIt.get<UserProfile>().baseImageDir;
for (var i = 0; i < attachments.length; i++) {
await _imageFileList[i].saveTo('$baseImageDir/${attachments[i]}');
}
}
receiverIds.remove(senderId);
msg['event'] = 'group-chat-msg';
msg['groupChatId'] = widget.contactId;
@ -313,22 +325,6 @@ class _MessageInputBoxState extends State<MessageInputBox> {
getIt.get<Contact>().groupChats[widget.contactId]!.remarkInGroupChat;
msg['avatar'] = getIt.get<UserProfile>().avatar;
getIt.get<WebSocketManager>().channel.sink.add(json.encode(msg));
if (attachments.isNotEmpty) {
List<String> encodedDatas = await compute(
bytes2json,
(
1,
receiverIds,
attachments,
_imageFileList,
baseImageDir,
),
);
for (final data in encodedDatas) {
getIt.get<WebSocketManager>().channel.sink.add(data);
}
}
}
_controller.text = '';
@ -336,27 +332,6 @@ class _MessageInputBoxState extends State<MessageInputBox> {
_imageFileList = [];
_hasMsg = false;
});
var chatSetting = _chatBox.get(widget.contactId);
if (chatSetting == null) {
_chatBox.put(
widget.contactId,
ChatSetting(
widget.contactId,
widget.chatType,
false,
true,
false,
now,
0,
),
);
} else {
chatSetting.latestDateTime = now;
chatSetting.unreadCount = 0;
chatSetting.isOpen = true;
_chatBox.put(widget.contactId, chatSetting);
}
}
bool _isShowTime(DateTime now) {
@ -372,44 +347,3 @@ class _MessageInputBoxState extends State<MessageInputBox> {
}
}
}
Future<List<String>> bytes2json(
(
int,
List<String>,
List<String>,
List<XFile>,
String,
) args) async {
List<String> encodedJson = [];
for (var i = 0; i < args.$3.length; i++) {
Uint8List bytes = await args.$4[i].readAsBytes();
File file = File('${args.$5}/${args.$3[i]}');
await file.create(recursive: true);
await file.writeAsBytes(bytes);
if (args.$1 == 0) {
encodedJson.add(
json.encode(
{
'event': 'chat-image',
'receiverId': args.$2[0],
'filename': args.$3[i],
'bytes': bytes,
},
),
);
} else {
encodedJson.add(
json.encode(
{
'event': 'chat-image',
'receiverIds': args.$2,
'filename': args.$3[i],
'bytes': bytes,
},
),
);
}
}
return encodedJson;
}

View File

@ -12,7 +12,6 @@ import 'package:together_mobile/models/route_state_model.dart';
import 'package:together_mobile/utils/format_datetime.dart';
import 'components/friend_message_bubble.dart';
import 'components/message_input_box.dart';
import 'image_view_screen/image_view_screen.dart';
class FriendMessageScreen extends StatefulWidget {
const FriendMessageScreen({
@ -110,6 +109,7 @@ class _FriendMessageScreenState extends State<FriendMessageScreen> {
physics: const BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics(),
),
// addAutomaticKeepAlives: false,
controller: _controller,
shrinkWrap: true,
reverse: true,
@ -123,24 +123,13 @@ class _FriendMessageScreenState extends State<FriendMessageScreen> {
MessageT messageT = messageTBox.getAt(i)!;
List<String> allAttachments = [];
// Do not reverse the list, cause what i want is
// rigth slide to next image, left to last otherwise
for (var element in messageTBox.values) {
if (element.attachments.isNotEmpty) {
allAttachments.addAll(element.attachments);
}
}
// Do not reverse the list, cause what i want is
// rigth slide to next image, left to last otherwise
// allAttachments = List.from(allAttachments.reversed);
List<AttachmentItem> attachmentItems = List.generate(
allAttachments.length,
(int index) {
return AttachmentItem(
id: index,
resource: allAttachments[index],
);
},
);
return FriendMessageBubble(
key: ValueKey(messageT.msgId),
@ -153,7 +142,7 @@ class _FriendMessageScreenState extends State<FriendMessageScreen> {
type: messageT.type,
text: messageT.text,
attachments: messageT.attachments,
attachmentItems: attachmentItems,
allAttachments: allAttachments,
);
},
);

View File

@ -27,7 +27,7 @@ class ImageViewScreen extends StatefulWidget {
this.minScale,
this.maxScale,
this.initialIndex = 0,
required this.attachmentItems,
required this.attachments,
this.scrollDirection = Axis.horizontal,
}) : pageController = PageController(initialPage: initialIndex);
@ -36,7 +36,7 @@ class ImageViewScreen extends StatefulWidget {
final dynamic maxScale;
final int initialIndex;
final PageController pageController;
final List<AttachmentItem> attachmentItems;
final List<String> attachments;
final Axis scrollDirection;
@override
@ -70,7 +70,7 @@ class _ImageViewScreenState extends State<ImageViewScreen> {
PhotoViewGallery.builder(
scrollPhysics: const BouncingScrollPhysics(),
builder: _buildItem,
itemCount: widget.attachmentItems.length,
itemCount: widget.attachments.length,
loadingBuilder: widget.loadingBuilder,
backgroundDecoration: const BoxDecoration(color: Colors.black),
pageController: widget.pageController,
@ -96,15 +96,15 @@ class _ImageViewScreenState extends State<ImageViewScreen> {
}
PhotoViewGalleryPageOptions _buildItem(BuildContext context, int index) {
final AttachmentItem item = widget.attachmentItems[index];
final String item = widget.attachments[index];
return PhotoViewGalleryPageOptions(
imageProvider: FileImage(
File('${getIt.get<UserProfile>().baseImageDir}/${item.resource}'),
File('${getIt.get<UserProfile>().baseImageDir}/$item'),
),
initialScale: PhotoViewComputedScale.contained,
minScale: PhotoViewComputedScale.contained * (0.5 + index / 10),
minScale: PhotoViewComputedScale.contained * 0.5,
maxScale: PhotoViewComputedScale.covered * 4.1,
heroAttributes: PhotoViewHeroAttributes(tag: item.id),
// heroAttributes: PhotoViewHeroAttributes(tag: widget.heroTag),
);
}
}

View File

@ -0,0 +1,93 @@
import 'package:flutter/material.dart';
void main() {
runApp(const UseAnimation());
}
class UseAnimation extends StatefulWidget {
const UseAnimation({super.key});
@override
State<UseAnimation> createState() => _UseAnimationState();
}
class _UseAnimationState extends State<UseAnimation>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _tween;
@override
void initState() {
_controller = AnimationController(vsync: this);
_tween = Tween<double>(begin: 0, end: 1).animate(_controller)
..addStatusListener((status) {
// print(status);
})
..addListener(() {
setState(() {});
});
// _controller.forward();
super.initState();
}
@override
void dispose() {
super.dispose();
_controller.dispose();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Use Animation',
home: Scaffold(
appBar: AppBar(title: const Text('use animation')),
body: Center(
child: SizedBox(
height: 300,
width: 200,
child: Stack(
alignment: Alignment.center,
children: [
Image.asset('assets/images/user_2.png'),
Positioned.fill(
child: Container(
color: _tween.value == 1.0
? null
: const Color.fromARGB(255, 36, 36, 36)
.withOpacity(0.3),
child: Center(
child: Text(
(_tween.value * 100).toStringAsFixed(1),
style: const TextStyle(
color: Colors.green,
fontSize: 24,
),
),
),
),
),
CircularProgressIndicator(
value: _tween.value,
),
],
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
if (_tween.value == 1.0) {
_controller.reset();
}
print(_tween.value);
// print(_controller.value);
_controller.animateTo(_tween.value + 0.1,
duration: const Duration(milliseconds: 200));
print(_tween.value);
},
child: const Icon(Icons.plus_one),
),
),
);
}
}

View File

@ -94,6 +94,8 @@ String formatMsgIDFromTime(DateTime dateTime) {
dateTime.month < 10 ? '0${dateTime.month}' : '${dateTime.month}';
String day = dateTime.day < 10 ? '0${dateTime.day}' : '${dateTime.day}';
String hour = dateTime.hour < 10 ? '0${dateTime.hour}' : '${dateTime.hour}';
String second =
dateTime.second < 10 ? '0${dateTime.second}' : '${dateTime.second}';
String minute =
dateTime.minute < 10 ? '0${dateTime.minute}' : '${dateTime.minute}';
String millisecond = dateTime.millisecond >= 100
@ -102,5 +104,5 @@ String formatMsgIDFromTime(DateTime dateTime) {
? '0${dateTime.minute}'
: '00${dateTime.minute}';
return '$year$month$day$hour$minute$millisecond';
return '$year$month$day$hour$minute$second$millisecond';
}

View File

@ -0,0 +1,449 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:hive/hive.dart';
import 'package:together_mobile/database/box_type.dart';
import 'package:together_mobile/database/hive_database.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/route_state_model.dart';
import 'package:together_mobile/models/user_model.dart';
import 'package:together_mobile/models/websocket_model.dart';
import 'package:together_mobile/notification_api.dart';
import 'package:together_mobile/request/message.dart';
const int chunkSize = 1024 * 1024 * 2;
Future<void> receiveFriendMsg(
Map<String, dynamic> msg,
bool isShowNotification,
) async {
print('=================收到了好友信息事件==================');
print(msg);
print('=======================================');
String senderId = msg['senderId'] as String;
Box<ChatSetting> chatSettingBox = Hive.box<ChatSetting>('chat_setting');
Box<MessageT> messageTBox = await HiveDatabase.openMessageBox(senderId);
Box<int> msgIndexBox = await Hive.openBox<int>('msg_index_$senderId');
ChatSetting? chatSetting = chatSettingBox.get(senderId);
DateTime dateTime = DateTime.parse(msg['dateTime'] as String);
if (chatSetting == null) {
chatSettingBox.put(
senderId,
ChatSetting(
senderId,
0,
false,
true,
false,
dateTime,
1,
),
);
} else if (msgIndexBox.get(msg['msgId'] as String) != null) {
return;
} else {
chatSetting.isOpen = true;
chatSetting.latestDateTime = dateTime;
chatSetting.unreadCount++;
chatSettingBox.put(senderId, chatSetting);
}
List<String> attachments = List.from(msg['attachments']);
final DateTime now = DateTime.parse(msg['dateTime'] as String);
for (var attachment in attachments) {
Box<AttachmentProgress> apr =
Hive.box<AttachmentProgress>('attachment_receive');
apr.put(attachment, AttachmentProgress(0, 1000, 0, true, false));
}
messageTBox.add(
MessageT(
msg['msgId'] as String,
senderId,
msg['text'],
msg['type'],
now,
msg['isShowTime'],
attachments,
),
);
if (!isShowNotification) {
return;
}
String name = getIt.get<Contact>().friends[senderId]!.friendRemark.isEmpty
? getIt.get<ContactAccountProfile>().friends[senderId]!.nickname
: getIt.get<Contact>().friends[senderId]!.friendRemark;
String routeName = getIt.get<RouteState>().currentPathName;
String avatar = getIt.get<ContactAccountProfile>().friends[senderId]!.avatar;
if (!getIt.get<RouteState>().isVisible) {
NotificationAPI.showMessageNotifications(
senderId: senderId,
name: name,
avatar: avatar,
text: msg['text'] as String,
);
} else if (routeName == 'Message') {
if (!getIt.get<RouteState>().query.containsValue(senderId)) {
NotificationAPI.showMessageNotifications(
senderId: senderId,
name: name,
avatar: avatar,
text: msg['text'] as String,
);
}
} else if (routeName != 'Chat') {
NotificationAPI.showMessageNotifications(
senderId: senderId,
name: name,
avatar: avatar,
text: msg['text'] as String,
);
}
}
void receiveApplyFriend(Map<String, dynamic> msg) {
print('=================收到了申请好友事件==================');
print(msg);
print('=======================================');
getIt.get<ApplyList>().addJson(msg);
}
void receiveFriendAdded(Map<String, dynamic> msg) {
print('=================收到了申请好友通过事件==================');
print(msg);
print('=======================================');
getIt.get<Contact>().addFriend(msg['friendId'], msg['setting']);
getIt
.get<ContactAccountProfile>()
.addFriendAccountProfile(msg['friendId'], msg['accountProfile']);
}
void receiveFriendDeleted(Map<String, dynamic> msg) {
print('=================收到了解除好友事件==================');
print(msg);
print('=======================================');
getIt.get<Contact>().removeFriend(msg['friendId']);
getIt.get<ContactAccountProfile>().removeFriend(msg['friendId']);
}
void receiveGroupChatCreation(Map<String, dynamic> msg) {
print('=================收到了群聊邀请事件==================');
print(msg);
print('=======================================');
String groupChatId = msg['groupChat']['id'];
getIt.get<Contact>().addGroupChat(groupChatId);
getIt.get<ContactAccountProfile>().addGroupChatProfile(groupChatId, msg);
}
void receiveGroupChatMsg(
Map<String, dynamic> msg,
bool isShowNotification,
) async {
print('=================收到了群聊信息事件==================');
print(msg);
print('=======================================');
String senderId = msg['senderId'] as String;
String groupChatId = msg['groupChatId'] as String;
Box<ChatSetting> chatSettingBox = Hive.box<ChatSetting>('chat_setting');
Box<MessageT> messageTBox = await HiveDatabase.openMessageBox(groupChatId);
Box<int> msgIndexBox = await Hive.openBox('msg_index_$groupChatId');
ChatSetting? chatSetting = chatSettingBox.get(groupChatId);
DateTime dateTime = DateTime.parse(msg['dateTime'] as String);
getIt.get<ContactAccountProfile>().addGroupChatMemberProfile(
groupChatId,
msg['senderId'],
{
'avatar': msg['avatar'],
'nickname': msg['nickname'],
'remarkInGroupChat': msg['remarkInGroupChat'],
},
);
if (chatSetting == null) {
chatSettingBox.put(
groupChatId,
ChatSetting(
groupChatId,
1,
false,
true,
false,
dateTime,
1,
),
);
} else if (msgIndexBox.get(msg['msgId'] as String) != null) {
return;
} else {
chatSetting.isOpen = true;
chatSetting.latestDateTime = dateTime;
chatSetting.unreadCount++;
chatSettingBox.put(groupChatId, chatSetting);
}
List<String> attachments = List.from(msg['attachments']);
final DateTime now = DateTime.parse(msg['dateTime'] as String);
messageTBox.add(
MessageT(
msg['msgId'] as String,
senderId,
msg['text'],
msg['type'],
now,
msg['isShowTime'],
attachments,
),
);
if (!isShowNotification) {
return;
}
String avatar =
getIt.get<ContactAccountProfile>().groupChats[groupChatId]!.avatar;
late String name;
if (getIt.get<Contact>().friends.containsKey(senderId)) {
if (getIt.get<Contact>().friends[senderId]!.friendRemark.isNotEmpty) {
name = getIt.get<Contact>().friends[senderId]!.friendRemark;
} else if ((msg['remarkInGroupChat'] as String).isNotEmpty) {
name = msg['remarkInGroupChat'];
} else {
name = msg['nickname'];
}
} else {
name = (msg['remarkInGroupChat'] as String).isNotEmpty
? msg['remarkInGroupChat']
: msg['nickname'];
}
String routeName = getIt.get<RouteState>().currentPathName;
if (!getIt.get<RouteState>().isVisible) {
NotificationAPI.showMessageNotifications(
senderId: senderId,
name: name,
avatar: avatar,
text: msg['text'] as String,
groupChatId: groupChatId,
);
} else if (routeName == 'Message') {
if (!getIt.get<RouteState>().query.containsValue(senderId)) {
NotificationAPI.showMessageNotifications(
senderId: senderId,
name: name,
avatar: avatar,
text: msg['text'] as String,
groupChatId: groupChatId,
);
}
} else if (routeName != 'Chat') {
NotificationAPI.showMessageNotifications(
senderId: senderId,
name: name,
avatar: avatar,
text: msg['text'] as String,
groupChatId: groupChatId,
);
}
}
void receiveChatImages(Map<String, dynamic> msg) async {
print('=================收到了聊天图片事件==================');
// print(msg);
print('=======================================');
String chatImageDir = getIt.get<UserProfile>().baseImageDir;
String filename = msg['filename'];
String filePath = '$chatImageDir/$filename';
File file = File(filePath);
if (await file.exists()) {
return;
}
late Box<AttachmentProgress> aprBox;
try {
aprBox = Hive.box<AttachmentProgress>('attachment_receive');
} catch (_) {
aprBox = await Hive.openBox<AttachmentProgress>('attachment_receive');
}
String dirTime = filename.split('/')[0];
Directory dir = Directory('$chatImageDir/$dirTime');
if (!(await dir.exists())) {
await dir.create(recursive: true);
}
int totalChunkNum = msg['totalChunkNum'];
if (totalChunkNum == 1) {
await file.writeAsBytes(List<int>.from(msg['bytes']));
Future.delayed(
const Duration(milliseconds: 200),
() {
aprBox.put(
filename,
AttachmentProgress(1, 1, 1.0, true, false),
);
},
);
return;
}
File tempFile = File('$chatImageDir/${msg['tempFilename']}');
AttachmentProgress? ap = aprBox.get(filename);
if (!(await tempFile.exists())) {
await tempFile.writeAsBytes(List<int>.from(msg['bytes']));
}
if (ap == null) {
aprBox.put(
filename,
AttachmentProgress(
1,
totalChunkNum,
1 / totalChunkNum,
true,
false,
),
);
} else {
ap.hasChunkNum += 1;
ap.progress = ap.hasChunkNum / totalChunkNum;
ap.isValid = true;
List<File> tempFiles = List.generate(
totalChunkNum,
(index) {
String tempFilePath =
'$chatImageDir/temp/$filename-$totalChunkNum-$index';
return File(tempFilePath);
},
);
// assemble chunks when all the chunks are arrived
if (tempFiles.every((element) => element.existsSync())) {
final openedFile = file.openWrite(mode: FileMode.append);
for (var i = 0; i < totalChunkNum; i++) {
Uint8List bytes = await tempFiles[i].readAsBytes();
openedFile.add(bytes);
await tempFiles[i].delete();
}
openedFile.close();
ap.hasChunkNum = totalChunkNum;
ap.progress = 1.0;
ap.isValid = true;
}
aprBox.put(filename, ap);
}
}
Future<void> receivePullChatImage(Map<String, dynamic> msg) async {
print('=================收到了拉取图片请求==================');
print(msg);
print('=======================================');
String baseImageDir = getIt.get<UserProfile>().baseImageDir;
Map<String, dynamic> sentMsg = {
'event': 'chat-image',
'senderId': getIt.get<UserAccount>().id,
};
if (msg['chatType'] == 0) {
sentMsg['receiverId'] = msg['receiverId'];
} else {
sentMsg['receiverIds'] = msg['receiverIds'];
}
for (var filename in msg['attachments']) {
File file = File('$baseImageDir/$filename');
int fileSize = await file.length();
int totalChunkNum = (fileSize / chunkSize).ceil();
int start = 0;
int end = fileSize >= chunkSize ? start + chunkSize : start + fileSize;
List<int> bytes = [];
await file.openRead(start, end).forEach((element) => bytes.addAll(element));
sentMsg['filename'] = filename;
sentMsg['totalChunkNum'] = totalChunkNum;
sentMsg['tempFilename'] = 'temp/$filename-$totalChunkNum-0';
sentMsg['currentChunkNum'] = 0;
sentMsg['bytes'] = bytes;
await uploadChatAttachment(sentMsg);
getIt.get<WebSocketManager>().addSendImageTimer(filename, totalChunkNum);
}
}
Future<void> receiveCheckChatImage(Map<String, dynamic> msg) async {
print('=================收到了服务端确认收到图片事件==================');
// print(msg);
print('=======================================');
int nextChunkNum = msg['currentChunkNum'] + 1;
int totalChunkNum = msg['totalChunkNum'];
String baseImageDir = getIt.get<UserProfile>().baseImageDir;
String filename = msg['filename'];
Box<AttachmentProgress> asBox =
Hive.box<AttachmentProgress>('attachment_send');
AttachmentProgress aps = asBox.get(filename)!;
getIt.get<WebSocketManager>().removeSendImageTimer(filename);
aps.hasChunkNum += 1;
aps.progress = aps.hasChunkNum / aps.totalChunkNum;
asBox.put(filename, aps);
// print(aps.hasChunkNum);
// print(aps.totalChunkNum);
// print('已发送了: ${aps.progress}');
if (nextChunkNum == totalChunkNum) {
return;
}
// Start to send next chunk
int start = nextChunkNum * chunkSize;
File file = File('$baseImageDir/$filename');
List<int> bytes = [];
if (nextChunkNum + 1 == totalChunkNum) {
await file
.openRead(start)
.forEach((element) => bytes.addAll(element as List<int>));
} else {
await file
.openRead(start, start + chunkSize)
.forEach((element) => bytes.addAll(element as List<int>));
}
Map<String, dynamic> sentMsg = {
'event': 'chat-image',
'senderId': getIt.get<UserAccount>().id,
};
if (msg['chatType'] == 0) {
sentMsg['receiverId'] = msg['receiverId'];
} else {
sentMsg['receiverIds'] = msg['receiverIds'];
}
sentMsg['filename'] = filename;
sentMsg['totalChunkNum'] = totalChunkNum;
sentMsg['tempFilename'] = 'temp/$filename-$totalChunkNum-$nextChunkNum';
sentMsg['currentChunkNum'] = nextChunkNum;
sentMsg['bytes'] = bytes;
uploadChatAttachment(sentMsg);
getIt.get<WebSocketManager>().addSendImageTimer(filename, totalChunkNum);
}

View File

@ -1157,14 +1157,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.10.0"
sprintf:
dependency: transitive
description:
name: sprintf
sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
url: "https://pub.dev"
source: hosted
version: "7.0.0"
sqflite:
dependency: transitive
description:
@ -1265,10 +1257,10 @@ packages:
dependency: transitive
description:
name: uuid
sha256: e03928880bdbcbf496fb415573f5ab7b1ea99b9b04f669c01104d085893c3134
sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313"
url: "https://pub.dev"
source: hosted
version: "4.0.0"
version: "3.0.7"
vector_math:
dependency: transitive
description:
@ -1357,6 +1349,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.2"
worker_manager:
dependency: "direct main"
description:
name: worker_manager
sha256: caab0544cb95471e8d1417d21e25838ee4f614f4651a9e8e5a4f26bfeff5f312
url: "https://pub.dev"
source: hosted
version: "6.3.1"
xdg_directories:
dependency: transitive
description:

View File

@ -61,6 +61,7 @@ dependencies:
timezone: ^0.9.2
video_player: ^2.6.1
web_socket_channel: ^2.4.0
worker_manager: ^6.3.1
dev_dependencies:
build_runner: ^2.4.5