feat(lib): create responses to text messages

This commit is contained in:
ksenia312
2024-02-06 22:24:58 +01:00
parent f71121bd92
commit 66c6f17b99
15 changed files with 285 additions and 233 deletions
+22 -19
View File
@@ -314,20 +314,14 @@ extension CommunicationChannelExtension on AppService {
} }
extension MessagingExtension on AppService { extension MessagingExtension on AppService {
void sendMessage(String message) { void sendTextRequest(String message) {
try { if (connectedDevice == null) return;
if (connectedDevice == null) return; _nearbyService.send(
_nearbyService.send( OutgoingNearbyMessage(
OutgoingNearbyMessage( content: NearbyMessageTextRequest.create(value: message),
content: NearbyMessageTextContent(value: message), receiver: connectedDevice!.info,
receiver: connectedDevice!.info, ),
), );
);
} catch (e) {
if (kDebugMode) {
print(e);
}
}
} }
void sendFilesRequest(List<String> paths) { void sendFilesRequest(List<String> paths) {
@@ -344,15 +338,24 @@ extension MessagingExtension on AppService {
); );
} }
void sendFilesResponse(String requestId, {required bool response}) { void sendTextResponse(String requestId) {
if (connectedDevice == null) return; if (connectedDevice == null) return;
_nearbyService.send( _nearbyService.send(
OutgoingNearbyMessage( OutgoingNearbyMessage(
receiver: connectedDevice!.info, receiver: connectedDevice!.info,
content: NearbyMessageFilesResponse( content: NearbyMessageTextResponse(id: requestId),
id: requestId, ),
response: response, );
), }
void sendFilesResponse(String requestId, {required bool response}) {
if (connectedDevice == null) return;
print('send');
_nearbyService.send(
OutgoingNearbyMessage(
receiver: connectedDevice!.info,
content: NearbyMessageFilesResponse(id: requestId, isAccepted: response),
), ),
); );
} }
@@ -0,0 +1,17 @@
import 'package:flutter/material.dart';
import 'package:nearby_service/nearby_service.dart';
import 'package:nearby_service_example_full/uikit/uikit.dart';
class FilesListener {
FilesListener._();
static void call(BuildContext context, ReceivedNearbyFilesPack pack) {
final senderSubtitle = 'From ${pack.sender.displayName} '
'(ID: ${pack.sender.id})';
AppShackBar.show(
Scaffold.of(context).context,
'${pack.files.length} files saved! \n${pack.files.map((e) => e.name).join('\n')}',
subtitle: senderSubtitle,
);
}
}
@@ -0,0 +1,56 @@
import 'package:flutter/material.dart';
import 'package:nearby_service/nearby_service.dart';
import 'package:nearby_service_example_full/domain/app_service.dart';
import 'package:nearby_service_example_full/uikit/uikit.dart';
import 'package:provider/provider.dart';
class MessagesListener {
MessagesListener._();
static void call(BuildContext context, ReceivedNearbyMessage message) {
final senderSubtitle = 'From ${message.sender.displayName} '
'(ID: ${message.sender.id})';
message.content.byType(
onTextRequest: (textRequest) {
AppShackBar.show(
Scaffold.of(context).context,
'${textRequest.value} (Message ID=${textRequest.id})',
subtitle: senderSubtitle,
)?.closed.whenComplete(() {
// mark as read
context.read<AppService>().sendTextResponse(textRequest.id);
});
},
onTextResponse: (textResponse) {
AppShackBar.show(
Scaffold.of(context).context,
'Your message with ID=${textResponse.id} was delivered to ${message.sender.displayName}',
);
},
onFilesRequest: (filesRequest) {
ActionDialog.show(
context,
title: 'Request to send ${filesRequest.files.length} files',
subtitle: senderSubtitle,
).then(
// accept or dismiss the files request
(isAccepted) => context.read<AppService>().sendFilesResponse(
filesRequest.id,
response: isAccepted ?? false,
),
);
},
onFilesResponse: (filesResponse) {
AppShackBar.show(
Scaffold.of(context).context,
filesResponse.isAccepted
? 'Request is accepted!'
: 'Request was denied :(',
subtitle: senderSubtitle,
actionType:
filesResponse.isAccepted ? ActionType.idle : ActionType.warning,
);
},
);
}
}
@@ -69,7 +69,7 @@ class _CommunicationViewState extends State<CommunicationView> {
Flexible( Flexible(
child: ActionButton( child: ActionButton(
title: 'Send', title: 'Send',
onTap: () => service.sendMessage(message), onTap: () => service.sendTextRequest(message),
), ),
), ),
], ],
@@ -1,6 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:nearby_service/nearby_service.dart'; import 'package:nearby_service/nearby_service.dart';
import 'package:nearby_service_example_full/domain/app_service.dart'; import 'package:nearby_service_example_full/domain/app_service.dart';
import 'package:nearby_service_example_full/presentation/listener/files_listener.dart';
import 'package:nearby_service_example_full/presentation/listener/messages_listener.dart';
import 'package:nearby_service_example_full/uikit/uikit.dart'; import 'package:nearby_service_example_full/uikit/uikit.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@@ -48,8 +50,14 @@ class ConnectedView extends StatelessWidget {
ActionButton( ActionButton(
title: 'Start communicate', title: 'Start communicate',
onTap: () => service.startCommunicationChannel( onTap: () => service.startCommunicationChannel(
listener: (event) => _listener(context, event), listener: (event) => MessagesListener.call(
onFilesSaved: (files) => _onFileSaved(context, files), context,
event,
),
onFilesSaved: (files) => FilesListener.call(
context,
files,
),
), ),
) )
else else
@@ -64,50 +72,4 @@ class ConnectedView extends StatelessWidget {
}, },
); );
} }
void _listener(BuildContext context, ReceivedNearbyMessage message) {
final senderSubtitle = 'From ${message.sender.displayName} '
'(ID: ${message.sender.id})';
message.content.byType(
onText: (content) {
AppShackBar.show(
Scaffold.of(context).context,
content.value,
subtitle: senderSubtitle,
);
},
onFilesRequest: (content) {
ActionDialog.show(
context,
title: 'Request to send ${content.files.length} files',
subtitle: senderSubtitle,
).then((value) {
if (value is bool) {
context.read<AppService>().sendFilesResponse(
content.id,
response: value,
);
}
});
},
onFilesResponse: (content) {
AppShackBar.show(
Scaffold.of(context).context,
content.response ? 'Request is accepted!' : 'Request was denied :(',
subtitle: senderSubtitle,
actionType: content.response ? ActionType.idle : ActionType.warning,
);
},
);
}
void _onFileSaved(BuildContext context, ReceivedNearbyFilesPack pack) {
final senderSubtitle = 'From ${pack.sender.displayName} '
'(ID: ${pack.sender.id})';
AppShackBar.show(
Scaffold.of(context).context,
'${pack.files.length} files saved! \n${pack.files.map((e) => e.name).join('\n')}',
subtitle: senderSubtitle,
);
}
} }
+2 -2
View File
@@ -15,12 +15,12 @@ class AppShackBar {
SnackBar( SnackBar(
content: RichText( content: RichText(
text: TextSpan( text: TextSpan(
text: '$title \n', text: title,
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16), style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
children: [ children: [
if (subtitle != null) if (subtitle != null)
TextSpan( TextSpan(
text: subtitle, text: '\n$subtitle',
style: const TextStyle( style: const TextStyle(
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
fontSize: 14, fontSize: 14,
+52 -45
View File
@@ -10,8 +10,9 @@ import MultipeerConnectivity
class NearbyMessageContent { class NearbyMessageContent {
init(type: MessageContentType) { init(id: String, type: MessageContentType) {
self.type = type self.type = type
self.id = id
} }
let type: MessageContentType let type: MessageContentType
@@ -19,8 +20,10 @@ class NearbyMessageContent {
static func typedFromJson(json: [String: Any]) -> NearbyMessageContent? { static func typedFromJson(json: [String: Any]) -> NearbyMessageContent? {
if let typeString: String = json["type"] as? String { if let typeString: String = json["type"] as? String {
let type = MessageContentType.fromString(value: typeString) let type = MessageContentType.fromString(value: typeString)
if (type == MessageContentType.text) { if (type == MessageContentType.textRequest) {
return NearbyMessageTextContent.fromJson(json: json) return NearbyMessageTextRequest.fromJson(json: json)
} else if (type == MessageContentType.textResponse) {
return NearbyMessageTextResponse.fromJson(json: json)
} else if (type == MessageContentType.filesRequest) { } else if (type == MessageContentType.filesRequest) {
return NearbyMessageFilesRequest.fromJson(json: json) return NearbyMessageFilesRequest.fromJson(json: json)
} else if (type == MessageContentType.filesResponse) { } else if (type == MessageContentType.filesResponse) {
@@ -30,21 +33,30 @@ class NearbyMessageContent {
return nil; return nil;
} }
static func fromJsonRaw(type: MessageContentType, json: [String: Any]) -> NearbyMessageContent? {
if let id: String = json["id"] as? String {
return NearbyMessageContent(id: id, type: type)
}
return nil;
}
func toJson() -> [String : Any] { func toJson() -> [String : Any] {
return ["type": type.name] return ["type": type.name, "id": id]
}
}
class NearbyMessageTextContent : NearbyMessageContent {
init(value: String) {
self.value = value
super.init(type: MessageContentType.text)
} }
static func fromJson(json:[String: Any]) -> NearbyMessageTextContent? { let id: String
if let message: String = json["value"] as? String { }
return NearbyMessageTextContent(value: message)
class NearbyMessageTextRequest : NearbyMessageContent {
init(id: String, value: String) {
self.value = value
super.init(id: id, type: MessageContentType.textRequest)
}
static func fromJson(json:[String: Any]) -> NearbyMessageTextRequest? {
if let value: String = json["value"] as? String,
let content = NearbyMessageContent.fromJsonRaw(type: MessageContentType.textRequest, json:json) {
return NearbyMessageTextRequest(id: content.id, value: value)
} }
return nil; return nil;
} }
@@ -56,42 +68,33 @@ class NearbyMessageTextContent : NearbyMessageContent {
let value: String let value: String
} }
class NearbyMessageFilesContent : NearbyMessageContent { class NearbyMessageTextResponse :NearbyMessageContent {
init(id: String, type: MessageContentType) { init(id: String) {
self.id = id super.init(id: id, type: MessageContentType.textResponse)
super.init(type: type)
} }
static func fromJsonRaw(type: MessageContentType, json: [String: Any]) -> NearbyMessageFilesContent? { static func fromJson( json: [String: Any]) -> NearbyMessageTextResponse? {
if let id: String = json["id"] as? String{ let content = NearbyMessageContent.fromJsonRaw(type: MessageContentType.textResponse, json: json)
return NearbyMessageFilesContent(id: id, type: type) if let requireContent = content {
return NearbyMessageTextResponse(id: requireContent.id)
} }
return nil; return nil
} }
override func toJson() -> [String : Any] {
return [
"id": id,
].merging( super.toJson()) { (current, _) in current}
}
let id: String
} }
class NearbyMessageFilesRequest : NearbyMessageFilesContent { class NearbyMessageFilesRequest : NearbyMessageContent {
init(files: Array<String>, id: String) { init(files: Array<String>, id: String) {
self.files = files self.files = files
super.init(id: id, type: MessageContentType.filesRequest) super.init(id: id, type: MessageContentType.filesRequest)
} }
static func fromJson(json: [String: Any]) -> NearbyMessageFilesRequest? { static func fromJson(json: [String: Any]) -> NearbyMessageFilesRequest? {
let message = NearbyMessageFilesContent.fromJsonRaw(type: MessageContentType.filesRequest, json: json) let content = NearbyMessageContent.fromJsonRaw(type: MessageContentType.filesRequest, json: json)
if let filesObjects: Array = json["files"] as? Array<Dictionary<String, AnyObject>> { if let filesObjects: Array = json["files"] as? Array<Dictionary<String, AnyObject>> {
let files : [String]? = filesObjects.map({ $0["path"] as? String }).compactMap({$0}) let files : [String]? = filesObjects.map({ $0["path"] as? String }).compactMap({$0})
if let requireMessage = message { if let requireContent = content {
if let requireFiles = files { if let requireFiles = files {
return NearbyMessageFilesRequest(files: requireFiles, id: requireMessage.id) return NearbyMessageFilesRequest(files: requireFiles, id: requireContent.id)
} }
} }
} }
@@ -106,22 +109,22 @@ class NearbyMessageFilesRequest : NearbyMessageFilesContent {
} }
} }
class NearbyMessageFilesResponse : NearbyMessageFilesContent { class NearbyMessageFilesResponse : NearbyMessageContent {
init(id: String, response: Bool) { init(id: String, response: Bool) {
self.response = response self.response = response
super.init(id: id, type: MessageContentType.filesResponse) super.init(id: id, type: MessageContentType.filesResponse)
} }
static func fromJson( json: [String: Any]) -> NearbyMessageFilesResponse? { static func fromJson( json: [String: Any]) -> NearbyMessageFilesResponse? {
let message = NearbyMessageFilesContent.fromJsonRaw(type: MessageContentType.filesResponse, json: json) let message = NearbyMessageContent.fromJsonRaw(type: MessageContentType.filesResponse, json: json)
if let requireMessage = message, if let requireMessage = message,
let response = json["response"] as? Bool { let response = json["isAccepted"] as? Bool {
return NearbyMessageFilesResponse(id: requireMessage.id, response: response) return NearbyMessageFilesResponse(id: requireMessage.id, response: response)
} }
return nil return nil
} }
override func toJson() -> [String : Any] { override func toJson() -> [String : Any] {
return [ return [
"response": response "isAccepted": response
].merging( super.toJson()) { (current, _) in current} ].merging( super.toJson()) { (current, _) in current}
} }
@@ -130,25 +133,29 @@ class NearbyMessageFilesResponse : NearbyMessageFilesContent {
enum MessageContentType { enum MessageContentType {
case text case textRequest
case textResponse
case filesRequest case filesRequest
case filesResponse case filesResponse
static func fromString(value: String) -> MessageContentType { static func fromString(value: String) -> MessageContentType {
if (value == text.name) { if (value == textRequest.name) {
return text return textRequest
} else if (value == filesRequest.name) { } else if (value == textResponse.name) {
return textResponse
} else if (value == filesRequest.name) {
return filesRequest return filesRequest
} else if (value == filesResponse.name) { } else if (value == filesResponse.name) {
return filesResponse return filesResponse
} else { } else {
return text return textRequest
} }
} }
var name : String { var name : String {
switch self { switch self {
case .text: return "text" case .textRequest: return "textRequest"
case .textResponse: return "textResponse"
case .filesRequest: return "filesRequest" case .filesRequest: return "filesRequest"
case .filesResponse: return "filesResponse" case .filesResponse: return "filesResponse"
} }
@@ -13,11 +13,15 @@ extension NearbyServicePlugin {
@objc func onMessageReceived(notification: Notification) { @objc func onMessageReceived(notification: Notification) {
DispatchQueue.main.async { DispatchQueue.main.async {
if let userInfo = NearbyUserInfo.fromDictionary(userInfo: notification.userInfo) { if let userInfo = NearbyUserInfo.fromDictionary(userInfo: notification.userInfo) {
print(userInfo)
if let message = NearbyMessage.fromUserInfo(userInfo: userInfo) { if let message = NearbyMessage.fromUserInfo(userInfo: userInfo) {
print(message)
if message.content is NearbyMessageFilesResponse { if message.content is NearbyMessageFilesResponse {
print(message.content)
let response = message.content as! NearbyMessageFilesResponse let response = message.content as! NearbyMessageFilesResponse
let cachedRequest = NearbyRequestsStore.instance.find(for: response.id) let cachedRequest = NearbyRequestsStore.instance.find(for: response.id)
if (response.response && cachedRequest != nil) { if (response.response && cachedRequest != nil) {
print("send")
self.manager.sendFiles( self.manager.sendFiles(
id: cachedRequest!.id, id: cachedRequest!.id,
paths: cachedRequest!.files, paths: cachedRequest!.files,
+24 -12
View File
@@ -1,4 +1,5 @@
import 'package:nearby_service/nearby_service.dart'; import 'package:nearby_service/nearby_service.dart';
import 'package:nearby_service/src/utils/random.dart';
/// ///
/// Abstraction for the message content. /// Abstraction for the message content.
@@ -6,11 +7,14 @@ import 'package:nearby_service/nearby_service.dart';
/// determine, what type of content is it. /// determine, what type of content is it.
/// ///
abstract base class NearbyMessageContent { abstract base class NearbyMessageContent {
const NearbyMessageContent(); const NearbyMessageContent({required this.id});
NearbyMessageContent.create()
: id = RandomUtils.instance.nextInt(1000000, 9999999).toString();
/// ///
/// Contains the conditional logic of creating [NearbyMessageFilesRequest], /// Contains the conditional logic of creating [NearbyMessageFilesRequest],
/// [NearbyMessageFilesResponse] or [NearbyMessageTextContent] /// [NearbyMessageFilesResponse] or [NearbyMessageTextRequest]
/// by `type` field of [json]. /// by `type` field of [json].
/// ///
static C fromJson<C extends NearbyMessageContent>( static C fromJson<C extends NearbyMessageContent>(
@@ -20,8 +24,10 @@ abstract base class NearbyMessageContent {
final type = NearbyMessageContentType.values.firstWhere( final type = NearbyMessageContentType.values.firstWhere(
(e) => e.name == json?['type'], (e) => e.name == json?['type'],
); );
if (type.isText) { if (type.isTextRequest) {
return NearbyMessageTextContent.fromJson(json) as C; return NearbyMessageTextRequest.fromJson(json) as C;
} else if (type.isTextResponse) {
return NearbyMessageTextResponse.fromJson(json) as C;
} else if (type.isFilesRequest) { } else if (type.isFilesRequest) {
return NearbyMessageFilesRequest.fromJson(json) as C; return NearbyMessageFilesRequest.fromJson(json) as C;
} else if (type.isFilesResponse) { } else if (type.isFilesResponse) {
@@ -34,9 +40,12 @@ abstract base class NearbyMessageContent {
} }
} }
final String id;
NearbyMessageContentType get _type { NearbyMessageContentType get _type {
final type = byType( final type = byType(
onText: (_) => NearbyMessageContentType.text, onTextRequest: (_) => NearbyMessageContentType.textRequest,
onTextResponse: (_) => NearbyMessageContentType.textResponse,
onFilesRequest: (_) => NearbyMessageContentType.filesRequest, onFilesRequest: (_) => NearbyMessageContentType.filesRequest,
onFilesResponse: (_) => NearbyMessageContentType.filesResponse, onFilesResponse: (_) => NearbyMessageContentType.filesResponse,
); );
@@ -51,11 +60,11 @@ abstract base class NearbyMessageContent {
/// ///
/// Check for the content if it is valid for sending or receiving. /// Check for the content if it is valid for sending or receiving.
/// ///
bool get isValid; bool get isValid => id.isNotEmpty;
/// ///
/// * The [onText] callback returns this instance of [NearbyMessageContent], /// * The [onTextRequest] callback returns this instance of [NearbyMessageContent],
/// cast as [NearbyMessageTextContent] if is a text. /// cast as [NearbyMessageTextRequest] if is a text.
/// ///
/// * The [onFilesRequest] callback returns this instance of [NearbyMessageContent], /// * The [onFilesRequest] callback returns this instance of [NearbyMessageContent],
/// cast as [NearbyMessageFilesRequest] if is a files pack request. /// cast as [NearbyMessageFilesRequest] if is a files pack request.
@@ -64,12 +73,15 @@ abstract base class NearbyMessageContent {
/// cast as [NearbyMessageFilesResponse] if is a files pack response. /// cast as [NearbyMessageFilesResponse] if is a files pack response.
/// ///
T? byType<T>({ T? byType<T>({
T Function(NearbyMessageTextContent)? onText, T Function(NearbyMessageTextRequest)? onTextRequest,
T Function(NearbyMessageTextResponse)? onTextResponse,
T Function(NearbyMessageFilesRequest)? onFilesRequest, T Function(NearbyMessageFilesRequest)? onFilesRequest,
T Function(NearbyMessageFilesResponse)? onFilesResponse, T Function(NearbyMessageFilesResponse)? onFilesResponse,
}) { }) {
if (this is NearbyMessageTextContent && onText != null) { if (this is NearbyMessageTextRequest && onTextRequest != null) {
return onText(this as NearbyMessageTextContent); return onTextRequest(this as NearbyMessageTextRequest);
} else if (this is NearbyMessageTextResponse && onTextResponse != null) {
return onTextResponse(this as NearbyMessageTextResponse);
} else if (this is NearbyMessageFilesRequest && onFilesRequest != null) { } else if (this is NearbyMessageFilesRequest && onFilesRequest != null) {
return onFilesRequest(this as NearbyMessageFilesRequest); return onFilesRequest(this as NearbyMessageFilesRequest);
} else if (this is NearbyMessageFilesResponse && onFilesResponse != null) { } else if (this is NearbyMessageFilesResponse && onFilesResponse != null) {
@@ -82,6 +94,6 @@ abstract base class NearbyMessageContent {
/// Gets [Map] from [NearbyMessageContent] /// Gets [Map] from [NearbyMessageContent]
/// ///
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return {'type': _type.name}; return {'type': _type.name, 'id': id};
} }
} }
+28 -57
View File
@@ -1,20 +1,25 @@
import 'package:nearby_service/nearby_service.dart'; import 'package:nearby_service/nearby_service.dart';
import 'package:nearby_service/src/utils/random.dart';
/// ///
/// Nearby message Text content. /// Nearby message Text content.
/// ///
/// Contains [value] - the message to be sent or received. /// Contains [value] - the message to be sent or received.
/// ///
final class NearbyMessageTextContent extends NearbyMessageContent { final class NearbyMessageTextRequest extends NearbyMessageContent {
const NearbyMessageTextContent({required this.value}); const NearbyMessageTextRequest._({
required this.value,
required super.id,
});
NearbyMessageTextRequest.create({required this.value}) : super.create();
/// ///
/// Gets [NearbyMessageTextContent] from [json] /// Gets [NearbyMessageTextRequest] from [json]
/// ///
factory NearbyMessageTextContent.fromJson(Map<String, dynamic>? json) { factory NearbyMessageTextRequest.fromJson(Map<String, dynamic>? json) {
return NearbyMessageTextContent( return NearbyMessageTextRequest._(
value: json?['value'] ?? '', id: json?['id'],
value: json?['value'],
); );
} }
@@ -29,7 +34,7 @@ final class NearbyMessageTextContent extends NearbyMessageContent {
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
identical(this, other) || identical(this, other) ||
other is NearbyMessageTextContent && other is NearbyMessageTextRequest &&
runtimeType == other.runtimeType && runtimeType == other.runtimeType &&
value == other.value; value == other.value;
@@ -38,7 +43,7 @@ final class NearbyMessageTextContent extends NearbyMessageContent {
@override @override
String toString() { String toString() {
return 'NearbyMessageTextContent{value: $value}'; return 'NearbyMessageTextRequest{id: $id, value: $value}';
} }
@override @override
@@ -50,55 +55,24 @@ final class NearbyMessageTextContent extends NearbyMessageContent {
} }
} }
/// final class NearbyMessageTextResponse extends NearbyMessageContent {
/// Sealed class for files content in NearbyMessage. const NearbyMessageTextResponse({required super.id});
///
sealed class NearbyMessageFilesContent extends NearbyMessageContent {
///
/// Here type is [NearbyMessageContentType.filesResponse] or
/// [NearbyMessageContentType.filesRequest]
///
/// Also [NearbyMessageFilesContent] contains [id] of the files pack.
///
const NearbyMessageFilesContent({required this.id});
/// factory NearbyMessageTextResponse.fromJson(Map<String, dynamic>? json) {
/// ID of this files pack return NearbyMessageTextResponse(id: json?['id']);
///
final String id;
@override
Map<String, dynamic> toJson() {
return {
'id': id,
...super.toJson(),
};
} }
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is NearbyMessageFilesContent &&
runtimeType == other.runtimeType &&
id == other.id;
@override
int get hashCode => id.hashCode;
@override @override
String toString() { String toString() {
return 'NearbyMessageFilesContent{id: $id}'; return 'NearbyMessageTextResponse{id: $id}';
} }
@override
bool get isValid => id.isNotEmpty;
} }
/// ///
/// Nearby message File Request. Used for files sending requests. /// Nearby message File Request. Used for files sending requests.
/// Contains info about the [files]. /// Contains info about the [files].
/// ///
final class NearbyMessageFilesRequest extends NearbyMessageFilesContent { final class NearbyMessageFilesRequest extends NearbyMessageContent {
/// ///
/// Adds a [NearbyFileInfo] list to [id] to identify files. /// Adds a [NearbyFileInfo] list to [id] to identify files.
/// ///
@@ -111,17 +85,14 @@ final class NearbyMessageFilesRequest extends NearbyMessageFilesContent {
/// Basic constructor with [files] to be sent or received. /// Basic constructor with [files] to be sent or received.
/// Generates [id] in constructor. /// Generates [id] in constructor.
/// ///
NearbyMessageFilesRequest.create({required this.files}) NearbyMessageFilesRequest.create({required this.files}) : super.create();
: super(
id: RandomUtils.instance.nextInt(1000000, 9999999).toString(),
);
/// ///
/// Gets [NearbyMessageFilesRequest] from [json]. /// Gets [NearbyMessageFilesRequest] from [json].
/// ///
factory NearbyMessageFilesRequest.fromJson(Map<String, dynamic>? json) { factory NearbyMessageFilesRequest.fromJson(Map<String, dynamic>? json) {
return NearbyMessageFilesRequest._( return NearbyMessageFilesRequest._(
id: json?['id'] ?? '', id: json?['id'],
files: [ files: [
...?(json?['files'] as List?)?.map( ...?(json?['files'] as List?)?.map(
(e) => NearbyFileInfo.fromJson(e), (e) => NearbyFileInfo.fromJson(e),
@@ -171,14 +142,14 @@ final class NearbyMessageFilesRequest extends NearbyMessageFilesContent {
/// ///
/// Nearby message File Response. Used for file sending responses. /// Nearby message File Response. Used for file sending responses.
/// ///
final class NearbyMessageFilesResponse extends NearbyMessageFilesContent { final class NearbyMessageFilesResponse extends NearbyMessageContent {
/// ///
/// Used to send a response to a previously received request. /// Used to send a response to a previously received request.
/// Provide [id] from [NearbyMessageFilesRequest]. /// Provide [id] from [NearbyMessageFilesRequest].
/// ///
NearbyMessageFilesResponse({ NearbyMessageFilesResponse({
required super.id, required super.id,
required this.response, required this.isAccepted,
}); });
/// ///
@@ -187,25 +158,25 @@ final class NearbyMessageFilesResponse extends NearbyMessageFilesContent {
factory NearbyMessageFilesResponse.fromJson(Map<String, dynamic>? json) { factory NearbyMessageFilesResponse.fromJson(Map<String, dynamic>? json) {
return NearbyMessageFilesResponse( return NearbyMessageFilesResponse(
id: json?['id'] ?? '', id: json?['id'] ?? '',
response: json?['response'] ?? false, isAccepted: json?['isAccepted'] ?? false,
); );
} }
/// ///
/// The main response to the received [NearbyMessageFilesRequest]. /// The main response to the received [NearbyMessageFilesRequest].
/// ///
final bool response; final bool isAccepted;
@override @override
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return { return {
'response': response, 'isAccepted': isAccepted,
...super.toJson(), ...super.toJson(),
}; };
} }
@override @override
String toString() { String toString() {
return 'NearbyMessageFileResponse{response: $response, id: $id}'; return 'NearbyMessageFileResponse{response: $isAccepted, id: $id}';
} }
} }
+13 -5
View File
@@ -1,21 +1,29 @@
/// ///
/// Type of the message. /// Type of the message.
/// ///
/// If [text], it will be a text message. /// If [textRequest], it will be a text message.
/// If [filesRequest], it will be a files pack request. /// If [filesRequest], it will be a files pack request.
/// After accepting the request, /// After accepting the request,
/// user can get files bytes stream from the connected device. /// user can get files bytes stream from the connected device.
/// ///
enum NearbyMessageContentType { enum NearbyMessageContentType {
text, textRequest,
textResponse,
filesRequest, filesRequest,
filesResponse; filesResponse;
/// ///
/// Checks if this is [NearbyMessageContentType.text] /// Checks if this is [NearbyMessageContentType.textRequest]
/// ///
bool get isText { bool get isTextRequest {
return this == NearbyMessageContentType.text; return this == NearbyMessageContentType.textRequest;
}
///
/// Checks if this is [NearbyMessageContentType.textResponse]
///
bool get isTextResponse {
return this == NearbyMessageContentType.textResponse;
} }
/// ///
@@ -34,10 +34,15 @@ class FileSocketsManager {
} }
Future<void> handleFileMessageContent( Future<void> handleFileMessageContent(
NearbyMessageFilesContent content, { NearbyMessageContent content, {
required NearbyDeviceInfo? sender, required NearbyDeviceInfo? sender,
required bool isReceived, required bool isReceived,
}) async { }) async {
assert(
content is NearbyMessageFilesResponse ||
content is NearbyMessageFilesRequest,
"Provide NearbyMessageFilesResponse or NearbyMessageFilesRequest to handleFileMessageContent()",
);
if (sender != null) { if (sender != null) {
_sender = sender; _sender = sender;
Logger.debug('Sender was set to $_sender'); Logger.debug('Sender was set to $_sender');
@@ -47,7 +52,7 @@ class FileSocketsManager {
final isRequest = content is NearbyMessageFilesRequest; final isRequest = content is NearbyMessageFilesRequest;
final isPositiveResponse = final isPositiveResponse =
content is NearbyMessageFilesResponse && content.response; content is NearbyMessageFilesResponse && content.isAccepted;
if (isRequest) { if (isRequest) {
_cachedFilesRequest = content; _cachedFilesRequest = content;
@@ -98,7 +98,7 @@ class NearbySocketService {
}, },
), ),
); );
_handleMessage(message); _handleFilesMessage(message);
} }
return true; return true;
} }
@@ -203,28 +203,27 @@ class NearbySocketService {
?.where((e) => e != null) ?.where((e) => e != null)
.map(MessagesStreamMapper.toMessage) .map(MessagesStreamMapper.toMessage)
.cast<ReceivedNearbyMessage>() .cast<ReceivedNearbyMessage>()
.map((e) => MessagesStreamMapper.replaceId(e, _connectedDeviceId!)) .map((e) => MessagesStreamMapper.replaceId(e, _connectedDeviceId))
.listen( .listen(
(message) async { (message) async {
try { try {
_handleMessage(message); _handleFilesMessage(message);
socketListener.onData(message); socketListener.onData(message);
} catch (e) { } catch (e) {
Logger.error(e); Logger.error(e);
} }
// } },
}, onDone: () {
onDone: () { state.value = CommunicationChannelState.notConnected;
state.value = CommunicationChannelState.notConnected; socketListener.onDone?.call();
socketListener.onDone?.call(); },
}, onError: (e, s) {
onError: (e, s) { Logger.error(e);
Logger.error(e); state.value = CommunicationChannelState.notConnected;
state.value = CommunicationChannelState.notConnected; socketListener.onError?.call(e, s);
socketListener.onError?.call(e, s); },
}, cancelOnError: socketListener.cancelOnError,
cancelOnError: socketListener.cancelOnError, );
);
} }
if (_messagesSubscription != null) { if (_messagesSubscription != null) {
state.value = CommunicationChannelState.connected; state.value = CommunicationChannelState.connected;
@@ -235,10 +234,11 @@ class NearbySocketService {
} }
} }
void _handleMessage(NearbyMessage message) { void _handleFilesMessage(NearbyMessage message) {
if (message.content is NearbyMessageFilesContent) { if (message.content is NearbyMessageFilesRequest ||
message.content is NearbyMessageFilesResponse) {
_fileSocketsManager.handleFileMessageContent( _fileSocketsManager.handleFileMessageContent(
message.content as NearbyMessageFilesContent, message.content,
isReceived: message is ReceivedNearbyMessage, isReceived: message is ReceivedNearbyMessage,
sender: message is ReceivedNearbyMessage ? message.sender : null, sender: message is ReceivedNearbyMessage ? message.sender : null,
); );
+13 -9
View File
@@ -186,19 +186,23 @@ class NearbyIOSService extends NearbyService {
cancelOnError: eventListener.cancelOnError, cancelOnError: eventListener.cancelOnError,
); );
_resourcesSubscription = NearbyServiceIOSPlatform.instance.resourcesStream _resourcesSubscription = NearbyServiceIOSPlatform.instance.resourcesStream
.map((event) {
print(event);
return event;
})
.map(ResourcesStreamMapper.toFilesPack) .map(ResourcesStreamMapper.toFilesPack)
.where((event) => event != null) .where((event) => event != null)
.cast<ReceivedNearbyFilesPack>() .cast<ReceivedNearbyFilesPack>()
.listen( .listen(
(e) => filesListener?.onData.call(e), (e) => filesListener?.onData.call(e),
onDone: filesListener?.onDone, onDone: filesListener?.onDone,
onError: (e, s) { onError: (e, s) {
Logger.error(e); Logger.error(e);
_state.value = CommunicationChannelState.notConnected; _state.value = CommunicationChannelState.notConnected;
filesListener?.onError?.call(e, s); filesListener?.onError?.call(e, s);
}, },
cancelOnError: filesListener?.cancelOnError, cancelOnError: filesListener?.cancelOnError,
); );
if (_messagesSubscription != null) { if (_messagesSubscription != null) {
Logger.info('Messages subscription was created successfully'); Logger.info('Messages subscription was created successfully');
eventListener.onCreated?.call(); eventListener.onCreated?.call();
+11 -8
View File
@@ -4,15 +4,18 @@ import 'package:nearby_service/src/utils/json_decoder.dart';
abstract class MessagesStreamMapper { abstract class MessagesStreamMapper {
static ReceivedNearbyMessage replaceId( static ReceivedNearbyMessage replaceId(
ReceivedNearbyMessage message, ReceivedNearbyMessage message,
String id, String? id,
) { ) {
return ReceivedNearbyMessage( if (id != null) {
content: message.content, return ReceivedNearbyMessage(
sender: NearbyDeviceInfo( content: message.content,
id: id, sender: NearbyDeviceInfo(
displayName: message.sender.displayName, id: id,
), displayName: message.sender.displayName,
); ),
);
}
throw NearbyServiceException('The provided ID does not exist');
} }
static ReceivedNearbyMessage? toMessage(dynamic event) { static ReceivedNearbyMessage? toMessage(dynamic event) {