feat(android, ios): remove files field from files response
This commit is contained in:
@@ -64,15 +64,7 @@ sealed class NearbyMessageFilesContent extends NearbyMessageContentBase {
|
||||
/// Also [NearbyMessageFilesContent] contains [id] of the files pack and
|
||||
/// list of [NearbyFileInfo] to determine the files.
|
||||
///
|
||||
const NearbyMessageFilesContent({
|
||||
required this.id,
|
||||
required this.files,
|
||||
});
|
||||
|
||||
///
|
||||
/// Info about the files to be sent or received.
|
||||
///
|
||||
final List<NearbyFileInfo> files;
|
||||
const NearbyMessageFilesContent({required this.id});
|
||||
|
||||
///
|
||||
/// ID of this files pack
|
||||
@@ -83,51 +75,43 @@ sealed class NearbyMessageFilesContent extends NearbyMessageContentBase {
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'id': id,
|
||||
'files': [
|
||||
...files.map((e) => e.toJson()),
|
||||
],
|
||||
...super.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
bool get isValid =>
|
||||
files.isNotEmpty &&
|
||||
files.every(
|
||||
(element) => element.path.isNotEmpty,
|
||||
);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is NearbyMessageFilesContent &&
|
||||
runtimeType == other.runtimeType &&
|
||||
files == other.files &&
|
||||
id == other.id;
|
||||
|
||||
@override
|
||||
int get hashCode => files.hashCode ^ id.hashCode;
|
||||
int get hashCode => id.hashCode;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'NearbyMessageFilesContent{files: $files, id: $id}';
|
||||
return 'NearbyMessageFilesContent{id: $id}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool get isValid => id.isNotEmpty;
|
||||
}
|
||||
|
||||
///
|
||||
/// Nearby message File Request. Used for file sending requests.
|
||||
/// Does not contain files' bytes!
|
||||
/// Nearby message File Request. Used for files sending requests.
|
||||
/// Contains info about the [files].
|
||||
///
|
||||
final class NearbyMessageFilesRequest extends NearbyMessageFilesContent {
|
||||
const NearbyMessageFilesRequest._({
|
||||
required super.id,
|
||||
required super.files,
|
||||
required this.files,
|
||||
});
|
||||
|
||||
///
|
||||
/// Basic constructor with [files] to be sent or received.
|
||||
///
|
||||
NearbyMessageFilesRequest({required super.files})
|
||||
NearbyMessageFilesRequest({required this.files})
|
||||
: super(
|
||||
id: RandomUtils.instance.nextInt(1000000, 9999999).toString(),
|
||||
);
|
||||
@@ -146,6 +130,11 @@ final class NearbyMessageFilesRequest extends NearbyMessageFilesContent {
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
/// Info about the files to be sent or received.
|
||||
///
|
||||
final List<NearbyFileInfo> files;
|
||||
|
||||
@override
|
||||
NearbyMessageContentType get type => NearbyMessageContentType.filesRequest;
|
||||
|
||||
@@ -153,6 +142,35 @@ final class NearbyMessageFilesRequest extends NearbyMessageFilesContent {
|
||||
String toString() {
|
||||
return 'NearbyMessageFileRequest{id: $id, files: $files}';
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'files': [
|
||||
...files.map((e) => e.toJson()),
|
||||
],
|
||||
...super.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
bool get isValid =>
|
||||
super.isValid &&
|
||||
files.isNotEmpty &&
|
||||
files.every(
|
||||
(element) => element.path.isNotEmpty,
|
||||
);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
super == other &&
|
||||
other is NearbyMessageFilesRequest &&
|
||||
runtimeType == other.runtimeType &&
|
||||
files == other.files;
|
||||
|
||||
@override
|
||||
int get hashCode => super.hashCode ^ files.hashCode;
|
||||
}
|
||||
|
||||
///
|
||||
@@ -162,41 +180,19 @@ final class NearbyMessageFilesRequest extends NearbyMessageFilesContent {
|
||||
final class NearbyMessageFilesResponse extends NearbyMessageFilesContent {
|
||||
///
|
||||
/// Used to send a response to a previously received request.
|
||||
/// Provide [id] and [files] from [NearbyMessageFilesRequest] or
|
||||
/// Use the [NearbyMessageFilesResponse.fromRequest] factory to generate a
|
||||
/// response.
|
||||
/// Provide [id] from [NearbyMessageFilesRequest] or
|
||||
///
|
||||
NearbyMessageFilesResponse({
|
||||
required super.id,
|
||||
required super.files,
|
||||
required this.response,
|
||||
});
|
||||
|
||||
///
|
||||
/// Factory to quickly create a response to [NearbyMessageFilesRequest].
|
||||
///
|
||||
factory NearbyMessageFilesResponse.fromRequest(
|
||||
NearbyMessageFilesRequest request, {
|
||||
required bool response,
|
||||
}) {
|
||||
return NearbyMessageFilesResponse(
|
||||
id: request.id,
|
||||
files: request.files,
|
||||
response: response,
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
/// Gets [NearbyMessageFilesResponse] from [Map]
|
||||
///
|
||||
factory NearbyMessageFilesResponse.fromJson(Map<String, dynamic>? json) {
|
||||
return NearbyMessageFilesResponse(
|
||||
id: json?['id'] ?? '',
|
||||
files: [
|
||||
...?(json?['files'] as List?)?.map(
|
||||
(e) => NearbyFileInfo.fromJson(e),
|
||||
),
|
||||
],
|
||||
response: json?['response'] ?? false,
|
||||
);
|
||||
}
|
||||
@@ -219,6 +215,6 @@ final class NearbyMessageFilesResponse extends NearbyMessageFilesContent {
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'NearbyMessageFileResponse{response: $response, id: $id, files: $files}';
|
||||
return 'NearbyMessageFileResponse{response: $response, id: $id}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,15 +12,23 @@ class FileSocketsManager {
|
||||
|
||||
NearbyServiceFilesListener? _filesListener;
|
||||
NearbyDeviceInfo? _sender;
|
||||
NearbyMessageFilesRequest? _cachedFilesRequest;
|
||||
Future? _socketCreationFuture;
|
||||
var _connectionData = const NearbyAndroidCommunicationChannelData();
|
||||
|
||||
void setListener(NearbyServiceFilesListener? listener) {
|
||||
_filesListener = listener;
|
||||
}
|
||||
|
||||
void setConnectionData(NearbyAndroidCommunicationChannelData? data) {
|
||||
_connectionData = data ?? _connectionData;
|
||||
}
|
||||
|
||||
void onWsRequest(HttpRequest request) {
|
||||
final type = NearbySocketType.fromRequest(request);
|
||||
final filesPackId = NearbyFilesPackId.fromRequest(request);
|
||||
if (type == NearbySocketType.file && filesPackId != null) {
|
||||
Logger.debug('Save a connection request $filesPackId from client');
|
||||
_serverWaitingRequests[filesPackId] = request;
|
||||
}
|
||||
}
|
||||
@@ -28,42 +36,62 @@ class FileSocketsManager {
|
||||
Future<void> handleFileMessageContent(
|
||||
NearbyMessageFilesContent content, {
|
||||
required NearbyDeviceInfo? sender,
|
||||
required NearbyAndroidCommunicationChannelData androidData,
|
||||
required bool isReceived,
|
||||
}) async {
|
||||
if (sender != null) {
|
||||
_sender = sender;
|
||||
Logger.debug('Sender was set to $_sender');
|
||||
}
|
||||
final socketExists = _filesSockets[content.id] != null;
|
||||
|
||||
final shouldStartSocket = content.byType(
|
||||
onFilesResponse: (response) => response.response,
|
||||
onFilesRequest: (_) => true,
|
||||
) ??
|
||||
false;
|
||||
final isRequest = content is NearbyMessageFilesRequest;
|
||||
|
||||
final alreadyExists = _filesSockets[content.id] != null;
|
||||
final isPositiveResponse =
|
||||
content is NearbyMessageFilesResponse && content.response;
|
||||
|
||||
if (shouldStartSocket && !alreadyExists) {
|
||||
if (isRequest) {
|
||||
_cachedFilesRequest = content;
|
||||
Logger.debug('Files pack request ${content.id} was cached');
|
||||
}
|
||||
if (!socketExists) {
|
||||
final info = await _service.getConnectionInfo();
|
||||
if (info != null && info.groupFormed) {
|
||||
if (info.isGroupOwner) {
|
||||
await _startFilesServer(content);
|
||||
if (isReceived && content is NearbyMessageFilesResponse) {
|
||||
await _startDataTransfer(content);
|
||||
if (isPositiveResponse && _cachedFilesRequest != null) {
|
||||
_socketCreationFuture = _startFilesServerSocket(
|
||||
_cachedFilesRequest!,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
await _connectToFilesSocket(
|
||||
content,
|
||||
connectionData: androidData,
|
||||
ownerIpAddress: info.ownerIpAddress,
|
||||
);
|
||||
if (!isReceived && content is NearbyMessageFilesRequest) {
|
||||
await _startDataTransfer(content);
|
||||
NearbyMessageFilesRequest? request;
|
||||
|
||||
if (!isReceived &&
|
||||
isPositiveResponse &&
|
||||
_cachedFilesRequest != null) {
|
||||
request = _cachedFilesRequest!;
|
||||
} else if (isRequest) {
|
||||
request = content;
|
||||
}
|
||||
|
||||
if (request != null) {
|
||||
_socketCreationFuture = _connectToFilesSocket(
|
||||
request,
|
||||
ownerIpAddress: info.ownerIpAddress,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isReceived && isPositiveResponse && _cachedFilesRequest != null) {
|
||||
await _socketCreationFuture?.whenComplete(
|
||||
() async {
|
||||
await _startDataTransfer(_cachedFilesRequest!);
|
||||
_socketCreationFuture = null;
|
||||
_cachedFilesRequest = null;
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> closeAll() async {
|
||||
@@ -72,39 +100,39 @@ class FileSocketsManager {
|
||||
}
|
||||
_filesSockets.clear();
|
||||
_filesListener = null;
|
||||
_cachedFilesRequest = null;
|
||||
_socketCreationFuture = null;
|
||||
}
|
||||
|
||||
Future<void> _connectToFilesSocket(
|
||||
NearbyMessageFilesContent content, {
|
||||
required NearbyAndroidCommunicationChannelData connectionData,
|
||||
NearbyMessageFilesRequest filesRequest, {
|
||||
required String ownerIpAddress,
|
||||
}) async {
|
||||
try {
|
||||
final response = await _network.pingServer(
|
||||
address: ownerIpAddress,
|
||||
port: connectionData.port,
|
||||
port: _connectionData.port,
|
||||
);
|
||||
if (await _pingManager.checkPong(response)) {
|
||||
await _tryStartFileSocket(
|
||||
content,
|
||||
await _startFilesSocket(
|
||||
filesRequest,
|
||||
onCreateSocket: () => _network.connectToSocket(
|
||||
ownerIpAddress: ownerIpAddress,
|
||||
port: connectionData.port,
|
||||
port: _connectionData.port,
|
||||
socketType: NearbySocketType.file,
|
||||
headers: {
|
||||
NearbyFilesPackId.key: content.id,
|
||||
NearbyFilesPackId.key: filesRequest.id,
|
||||
},
|
||||
),
|
||||
);
|
||||
} else {
|
||||
Logger.debug(
|
||||
'Files server is unavailable, reconnect in ${connectionData.clientReconnectInterval}s',
|
||||
'Files server is unavailable, reconnect in ${_connectionData.clientReconnectInterval}s',
|
||||
);
|
||||
await Future.delayed(
|
||||
connectionData.clientReconnectInterval,
|
||||
_connectionData.clientReconnectInterval,
|
||||
() => _connectToFilesSocket(
|
||||
content,
|
||||
connectionData: connectionData,
|
||||
filesRequest,
|
||||
ownerIpAddress: ownerIpAddress,
|
||||
),
|
||||
);
|
||||
@@ -114,50 +142,52 @@ class FileSocketsManager {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _startFilesServer(NearbyMessageFilesContent content) async {
|
||||
final request = _serverWaitingRequests[content.id];
|
||||
Future<void> _startFilesServerSocket(
|
||||
NearbyMessageFilesRequest filesRequest,
|
||||
) async {
|
||||
final connectionRequest = _serverWaitingRequests[filesRequest.id];
|
||||
|
||||
if (request != null) {
|
||||
Logger.debug('Found cached server file request ${content.id}');
|
||||
if (connectionRequest != null) {
|
||||
Logger.debug('Found cached server files request ${filesRequest.id}');
|
||||
|
||||
final result = await _tryStartFileSocket(
|
||||
content,
|
||||
onCreateSocket: () => WebSocketTransformer.upgrade(request),
|
||||
final result = await _startFilesSocket(
|
||||
filesRequest,
|
||||
onCreateSocket: () => WebSocketTransformer.upgrade(connectionRequest),
|
||||
);
|
||||
if (result) {
|
||||
_serverWaitingRequests.remove(content.id);
|
||||
_serverWaitingRequests.remove(filesRequest.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> _tryStartFileSocket(
|
||||
NearbyMessageFilesContent content, {
|
||||
Future<bool> _startFilesSocket(
|
||||
NearbyMessageFilesRequest filesRequest, {
|
||||
required Future<WebSocket?> Function() onCreateSocket,
|
||||
}) async {
|
||||
final socket = await onCreateSocket();
|
||||
if (socket != null && _sender != null) {
|
||||
_filesSockets[content.id] = FilesSocket.startListening(
|
||||
_filesSockets[filesRequest.id] = FilesSocket.startListening(
|
||||
sender: _sender!,
|
||||
content: content,
|
||||
filesRequest: filesRequest,
|
||||
socket: socket,
|
||||
listener: _filesListener,
|
||||
onDestroy: _filesSockets.remove,
|
||||
);
|
||||
Logger.info('Created a socket for the files pack ${content.id}');
|
||||
Logger.info('Created a socket for the files pack ${filesRequest.id}');
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<void> _startDataTransfer(NearbyMessageFilesContent content) async {
|
||||
final filesSocket = _filesSockets[content.id];
|
||||
Future<void> _startDataTransfer(NearbyMessageFilesRequest request) async {
|
||||
final filesSocket = _filesSockets[request.id];
|
||||
if (filesSocket != null) {
|
||||
Logger.debug('Start transferring the files pack ${content.id}');
|
||||
for (var i = 0; i < content.files.length; i++) {
|
||||
Logger.debug('Start transferring the files pack ${request.id}');
|
||||
for (var i = 0; i < request.files.length; i++) {
|
||||
try {
|
||||
final fileInfo = content.files[i];
|
||||
final fileInfo = request.files[i];
|
||||
await _streamFile(
|
||||
content.id,
|
||||
request.id,
|
||||
filesSocket: filesSocket,
|
||||
file: File(fileInfo.path),
|
||||
)?.asFuture();
|
||||
@@ -170,7 +200,7 @@ class FileSocketsManager {
|
||||
}
|
||||
}
|
||||
filesSocket.sendData(FilesSocket.finishCommand);
|
||||
Logger.debug('Sent finish command for the pack ${content.id}');
|
||||
Logger.debug('Sent finish command for the pack ${request.id}');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,9 @@ class NearbySocketService {
|
||||
_androidData = data.androidData;
|
||||
_connectedDeviceId = data.connectedDeviceId;
|
||||
|
||||
_fileSocketsManager.setListener(data.filesListener);
|
||||
_fileSocketsManager
|
||||
..setListener(data.filesListener)
|
||||
..setConnectionData(data.androidData);
|
||||
|
||||
final info = await _service.getConnectionInfo();
|
||||
|
||||
@@ -239,7 +241,6 @@ class NearbySocketService {
|
||||
message.content as NearbyMessageFilesContent,
|
||||
isReceived: message is ReceivedNearbyMessage,
|
||||
sender: message is ReceivedNearbyMessage ? message.sender : null,
|
||||
androidData: _androidData,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import 'package:path_provider/path_provider.dart';
|
||||
class FilesSocket {
|
||||
FilesSocket.startListening({
|
||||
required this.sender,
|
||||
required this.content,
|
||||
required this.filesRequest,
|
||||
required this.listener,
|
||||
required this.onDestroy,
|
||||
required WebSocket socket,
|
||||
@@ -29,7 +29,7 @@ class FilesSocket {
|
||||
|
||||
static String separateCommandOf(int index) => '$separateCommand$index';
|
||||
|
||||
final NearbyMessageFilesContent content;
|
||||
final NearbyMessageFilesRequest filesRequest;
|
||||
final void Function(String) onDestroy;
|
||||
final NearbyServiceFilesListener? listener;
|
||||
final NearbyDeviceInfo sender;
|
||||
@@ -52,7 +52,7 @@ class FilesSocket {
|
||||
final logStep = min(pow(10, _chunksCount.toString().length - 1), 100);
|
||||
if (_chunksCount % logStep == 0) {
|
||||
Logger.debug(
|
||||
'Got $_chunksCount chunks for the file ${content.files[_currentFileIndex].name}',
|
||||
'Got $_chunksCount chunks for the file ${filesRequest.files[_currentFileIndex].name}',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -66,7 +66,7 @@ class FilesSocket {
|
||||
_bytesTable['$_currentFileIndex'] = [];
|
||||
} else if (event == finishCommand) {
|
||||
await Future.wait(_futures);
|
||||
Logger.info('Files pack ${content.id} was created');
|
||||
Logger.info('Files pack ${filesRequest.id} was created');
|
||||
|
||||
listener?.onData.call(
|
||||
ReceivedNearbyFilesPack(
|
||||
@@ -74,14 +74,14 @@ class FilesSocket {
|
||||
files: _files,
|
||||
),
|
||||
);
|
||||
onDestroy(content.id);
|
||||
onDestroy(filesRequest.id);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _createFile(int index) async {
|
||||
try {
|
||||
final bytes = _bytesTable['$index']!;
|
||||
final fileInfo = content.files[index];
|
||||
final fileInfo = filesRequest.files[index];
|
||||
final directory = await getTemporaryDirectory();
|
||||
final file = File('${directory.path}/${fileInfo.name}');
|
||||
await file.writeAsBytes(bytes);
|
||||
|
||||
Reference in New Issue
Block a user