service: Allow sending multiple files

This commit is contained in:
PapaTutuWawa 2022-07-24 14:38:13 +02:00
parent 0acd812a60
commit 660c56e27a
4 changed files with 93 additions and 69 deletions

View File

@ -220,13 +220,13 @@ files:
quotedMessage: quotedMessage:
type: Message? type: Message?
deserialise: true deserialise: true
- name: SendFileCommand - name: SendFilesCommand
extends: BackgroundCommand extends: BackgroundCommand
implements: implements:
- JsonImplementation - JsonImplementation
attributes: attributes:
jid: String jid: String
path: String paths: List<String>
- name: BlockJidCommand - name: BlockJidCommand
extends: BackgroundCommand extends: BackgroundCommand
implements: implements:

View File

@ -49,7 +49,7 @@ void setupBackgroundEventHandler() {
EventTypeMatcher<SendChatStateCommand>(performSendChatState), EventTypeMatcher<SendChatStateCommand>(performSendChatState),
EventTypeMatcher<GetFeaturesCommand>(performGetFeatures), EventTypeMatcher<GetFeaturesCommand>(performGetFeatures),
EventTypeMatcher<SignOutCommand>(performSignOut), EventTypeMatcher<SignOutCommand>(performSignOut),
EventTypeMatcher<SendFileCommand>(performSendFile), EventTypeMatcher<SendFilesCommand>(performSendFiles),
]); ]);
GetIt.I.registerSingleton<EventHandler>(handler); GetIt.I.registerSingleton<EventHandler>(handler);
@ -409,6 +409,6 @@ Future<void> performSignOut(SignOutCommand command, { dynamic extra }) async {
); );
} }
Future<void> performSendFile(SendFileCommand command, { dynamic extra }) async { Future<void> performSendFiles(SendFilesCommand command, { dynamic extra }) async {
await GetIt.I.get<XmppService>().sendFile(command.path, command.jid); await GetIt.I.get<XmppService>().sendFiles(command.paths, command.jid);
} }

View File

@ -321,81 +321,105 @@ class XmppService {
return GetIt.I.get<XmppConnection>().connectAwaitable(lastResource: lastResource); return GetIt.I.get<XmppConnection>().connectAwaitable(lastResource: lastResource);
} }
Future<void> sendFile(String path, String recipient) async { Future<void> sendFiles(List<String> paths, String recipient) async {
// Create a new message // Create a new message
final ms = GetIt.I.get<MessageService>(); final ms = GetIt.I.get<MessageService>();
final cs = GetIt.I.get<ConversationService>(); final cs = GetIt.I.get<ConversationService>();
final us = GetIt.I.get<UploadService>(); final us = GetIt.I.get<UploadService>();
final conn = GetIt.I.get<XmppConnection>(); final conn = GetIt.I.get<XmppConnection>();
final httpManager = conn.getManagerById<HttpFileUploadManager>(httpFileUploadManager)!; final httpManager = conn.getManagerById<HttpFileUploadManager>(httpFileUploadManager)!;
final sid = conn.generateId();
final originId = conn.generateId();
_log.finest('Requesting upload slot'); // Path -> Message
final stat = File(path).statSync(); final messages = <String, Message>{};
final result = await httpManager.requestUploadSlot(pathlib.basename(path), stat.size);
if (result.isError()) { // Create the messages
_log.severe('Failed to request slot'); for (final path in paths) {
// TODO(PapaTutuWawa): Do not let it end here final msg = await ms.addMessageFromData(
return; '',
DateTime.now().millisecondsSinceEpoch,
conn.getConnectionSettings().jid.toString(),
recipient,
true,
true,
conn.generateId(),
// TODO(Unknown): Maybe don't have the UI depend on srcUrl if we sent it.
srcUrl: 'https://server.example',
mediaUrl: path,
mediaType: lookupMimeType(path),
originId: conn.generateId(),
);
messages[path] = msg;
sendEvent(MessageAddedEvent(message: msg.copyWith(isUploading: true)));
} }
final slot = result.getValue(); // Requesting Upload slots and uploading
final fileMime = lookupMimeType(path);
var msg = await ms.addMessageFromData(
'',
DateTime.now().millisecondsSinceEpoch,
conn.getConnectionSettings().jid.toString(),
recipient,
true,
true,
sid,
srcUrl: slot.getUrl,
mediaUrl: path,
mediaType: fileMime,
originId: originId,
);
// Notify the UI
msg = msg.copyWith(isUploading: true);
sendEvent(MessageAddedEvent(message: msg));
final uploadResult = await us.uploadFile(path, slot.putUrl, slot.headers, msg.id); final conversationId = (await cs.getConversationByJid(recipient))!.id;
msg = msg.copyWith(isUploading: false); for (final path in paths) {
sendEvent(MessageUpdatedEvent(message: msg)); _log.finest('Requesting upload slot for $path');
final stat = File(path).statSync();
final result = await httpManager.requestUploadSlot(pathlib.basename(path), stat.size);
if (result.isError()) {
_log.severe('Failed to request slot for $path!');
// TODO(PapaTutuWawa): Do not let it end here
return;
}
if (!uploadResult) { final slot = result.getValue();
_log.severe('Upload failed'); final fileMime = lookupMimeType(path);
// TODO(PapaTutuWawa): Do not abort here
return;
}
final id = (await cs.getConversationByJid(recipient))!.id; final uploadResult = await us.uploadFile(
final updatedConversation = await cs.updateConversation( path,
id, slot.putUrl,
lastMessageBody: mimeTypeToConversationBody(fileMime), slot.headers,
lastChangeTimestamp: DateTime.now().millisecondsSinceEpoch, messages[path]!.id,
); );
sendEvent(ConversationUpdatedEvent(conversation: updatedConversation));
conn.getManagerById<MessageManager>(messageManager)!.sendMessage( if (!uploadResult) {
MessageDetails( _log.severe('Upload failed for $path!');
to: recipient, // TODO(PapaTutuWawa): Do not abort here
body: slot.getUrl, return;
requestDeliveryReceipt: true, }
id: sid,
originId: originId, // Notify UI of upload completion
sfs: StatelessFileSharingData( sendEvent(
url: slot.getUrl, MessageUpdatedEvent(
metadata: FileMetadataData( message: messages[path]!.copyWith(isUploading: false),
mediaType: fileMime, ),
size: stat.size, );
name: pathlib.basename(path),
thumbnails: [], // Update conversation
final updatedConversation = await cs.updateConversation(
conversationId,
lastMessageBody: mimeTypeToConversationBody(fileMime),
lastChangeTimestamp: DateTime.now().millisecondsSinceEpoch,
);
sendEvent(ConversationUpdatedEvent(conversation: updatedConversation));
// Send the url to the recipient
final message = messages[path]!;
conn.getManagerById<MessageManager>(messageManager)!.sendMessage(
MessageDetails(
to: recipient,
body: slot.getUrl,
requestDeliveryReceipt: true,
id: message.sid,
originId: message.originId,
sfs: StatelessFileSharingData(
url: slot.getUrl,
metadata: FileMetadataData(
mediaType: fileMime,
size: stat.size,
name: pathlib.basename(path),
thumbnails: [],
),
), ),
), ),
), );
); _log.finest('Sent message with file upload for $path');
_log.finest('Sent message with file upload'); }
_log.finest('File upload done');
} }
Future<void> _onConnectionStateChanged(ConnectionStateChangedEvent event, { dynamic extra }) async { Future<void> _onConnectionStateChanged(ConnectionStateChangedEvent event, { dynamic extra }) async {

View File

@ -290,12 +290,12 @@ class ConversationBloc extends Bloc<ConversationEvent, ConversationState> {
} }
Future<void> _onFileUploadRequested(FileUploadRequestedEvent event, Emitter<ConversationState> emit) async { Future<void> _onFileUploadRequested(FileUploadRequestedEvent event, Emitter<ConversationState> emit) async {
final result = await FilePicker.platform.pickFiles(type: FileType.image); final result = await FilePicker.platform.pickFiles(type: FileType.image, allowMultiple: true);
if (result != null) { if (result != null) {
await MoxplatformPlugin.handler.getDataSender().sendData( await MoxplatformPlugin.handler.getDataSender().sendData(
SendFileCommand( SendFilesCommand(
path: result.files.single.path!, paths: result.files.map((PlatformFile file) => file.path!).toList(),
jid: state.conversation!.jid, jid: state.conversation!.jid,
), ),
awaitable: false, awaitable: false,