feat(lib): create responses to text messages
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import 'package:nearby_service/nearby_service.dart';
|
||||
import 'package:nearby_service/src/utils/random.dart';
|
||||
|
||||
///
|
||||
/// Abstraction for the message content.
|
||||
@@ -6,11 +7,14 @@ import 'package:nearby_service/nearby_service.dart';
|
||||
/// determine, what type of content is it.
|
||||
///
|
||||
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],
|
||||
/// [NearbyMessageFilesResponse] or [NearbyMessageTextContent]
|
||||
/// [NearbyMessageFilesResponse] or [NearbyMessageTextRequest]
|
||||
/// by `type` field of [json].
|
||||
///
|
||||
static C fromJson<C extends NearbyMessageContent>(
|
||||
@@ -20,8 +24,10 @@ abstract base class NearbyMessageContent {
|
||||
final type = NearbyMessageContentType.values.firstWhere(
|
||||
(e) => e.name == json?['type'],
|
||||
);
|
||||
if (type.isText) {
|
||||
return NearbyMessageTextContent.fromJson(json) as C;
|
||||
if (type.isTextRequest) {
|
||||
return NearbyMessageTextRequest.fromJson(json) as C;
|
||||
} else if (type.isTextResponse) {
|
||||
return NearbyMessageTextResponse.fromJson(json) as C;
|
||||
} else if (type.isFilesRequest) {
|
||||
return NearbyMessageFilesRequest.fromJson(json) as C;
|
||||
} else if (type.isFilesResponse) {
|
||||
@@ -34,9 +40,12 @@ abstract base class NearbyMessageContent {
|
||||
}
|
||||
}
|
||||
|
||||
final String id;
|
||||
|
||||
NearbyMessageContentType get _type {
|
||||
final type = byType(
|
||||
onText: (_) => NearbyMessageContentType.text,
|
||||
onTextRequest: (_) => NearbyMessageContentType.textRequest,
|
||||
onTextResponse: (_) => NearbyMessageContentType.textResponse,
|
||||
onFilesRequest: (_) => NearbyMessageContentType.filesRequest,
|
||||
onFilesResponse: (_) => NearbyMessageContentType.filesResponse,
|
||||
);
|
||||
@@ -51,11 +60,11 @@ abstract base class NearbyMessageContent {
|
||||
///
|
||||
/// 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],
|
||||
/// cast as [NearbyMessageTextContent] if is a text.
|
||||
/// * The [onTextRequest] callback returns this instance of [NearbyMessageContent],
|
||||
/// cast as [NearbyMessageTextRequest] if is a text.
|
||||
///
|
||||
/// * The [onFilesRequest] callback returns this instance of [NearbyMessageContent],
|
||||
/// 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.
|
||||
///
|
||||
T? byType<T>({
|
||||
T Function(NearbyMessageTextContent)? onText,
|
||||
T Function(NearbyMessageTextRequest)? onTextRequest,
|
||||
T Function(NearbyMessageTextResponse)? onTextResponse,
|
||||
T Function(NearbyMessageFilesRequest)? onFilesRequest,
|
||||
T Function(NearbyMessageFilesResponse)? onFilesResponse,
|
||||
}) {
|
||||
if (this is NearbyMessageTextContent && onText != null) {
|
||||
return onText(this as NearbyMessageTextContent);
|
||||
if (this is NearbyMessageTextRequest && onTextRequest != null) {
|
||||
return onTextRequest(this as NearbyMessageTextRequest);
|
||||
} else if (this is NearbyMessageTextResponse && onTextResponse != null) {
|
||||
return onTextResponse(this as NearbyMessageTextResponse);
|
||||
} else if (this is NearbyMessageFilesRequest && onFilesRequest != null) {
|
||||
return onFilesRequest(this as NearbyMessageFilesRequest);
|
||||
} else if (this is NearbyMessageFilesResponse && onFilesResponse != null) {
|
||||
@@ -82,6 +94,6 @@ abstract base class NearbyMessageContent {
|
||||
/// Gets [Map] from [NearbyMessageContent]
|
||||
///
|
||||
Map<String, dynamic> toJson() {
|
||||
return {'type': _type.name};
|
||||
return {'type': _type.name, 'id': id};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,25 @@
|
||||
import 'package:nearby_service/nearby_service.dart';
|
||||
import 'package:nearby_service/src/utils/random.dart';
|
||||
|
||||
///
|
||||
/// Nearby message Text content.
|
||||
///
|
||||
/// Contains [value] - the message to be sent or received.
|
||||
///
|
||||
final class NearbyMessageTextContent extends NearbyMessageContent {
|
||||
const NearbyMessageTextContent({required this.value});
|
||||
final class NearbyMessageTextRequest extends NearbyMessageContent {
|
||||
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) {
|
||||
return NearbyMessageTextContent(
|
||||
value: json?['value'] ?? '',
|
||||
factory NearbyMessageTextRequest.fromJson(Map<String, dynamic>? json) {
|
||||
return NearbyMessageTextRequest._(
|
||||
id: json?['id'],
|
||||
value: json?['value'],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -29,7 +34,7 @@ final class NearbyMessageTextContent extends NearbyMessageContent {
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is NearbyMessageTextContent &&
|
||||
other is NearbyMessageTextRequest &&
|
||||
runtimeType == other.runtimeType &&
|
||||
value == other.value;
|
||||
|
||||
@@ -38,7 +43,7 @@ final class NearbyMessageTextContent extends NearbyMessageContent {
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'NearbyMessageTextContent{value: $value}';
|
||||
return 'NearbyMessageTextRequest{id: $id, value: $value}';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -50,55 +55,24 @@ final class NearbyMessageTextContent extends NearbyMessageContent {
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Sealed class for files content in NearbyMessage.
|
||||
///
|
||||
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});
|
||||
final class NearbyMessageTextResponse extends NearbyMessageContent {
|
||||
const NearbyMessageTextResponse({required super.id});
|
||||
|
||||
///
|
||||
/// ID of this files pack
|
||||
///
|
||||
final String id;
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'id': id,
|
||||
...super.toJson(),
|
||||
};
|
||||
factory NearbyMessageTextResponse.fromJson(Map<String, dynamic>? json) {
|
||||
return NearbyMessageTextResponse(id: json?['id']);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is NearbyMessageFilesContent &&
|
||||
runtimeType == other.runtimeType &&
|
||||
id == other.id;
|
||||
|
||||
@override
|
||||
int get hashCode => id.hashCode;
|
||||
|
||||
@override
|
||||
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.
|
||||
/// Contains info about the [files].
|
||||
///
|
||||
final class NearbyMessageFilesRequest extends NearbyMessageFilesContent {
|
||||
final class NearbyMessageFilesRequest extends NearbyMessageContent {
|
||||
///
|
||||
/// 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.
|
||||
/// Generates [id] in constructor.
|
||||
///
|
||||
NearbyMessageFilesRequest.create({required this.files})
|
||||
: super(
|
||||
id: RandomUtils.instance.nextInt(1000000, 9999999).toString(),
|
||||
);
|
||||
NearbyMessageFilesRequest.create({required this.files}) : super.create();
|
||||
|
||||
///
|
||||
/// Gets [NearbyMessageFilesRequest] from [json].
|
||||
///
|
||||
factory NearbyMessageFilesRequest.fromJson(Map<String, dynamic>? json) {
|
||||
return NearbyMessageFilesRequest._(
|
||||
id: json?['id'] ?? '',
|
||||
id: json?['id'],
|
||||
files: [
|
||||
...?(json?['files'] as List?)?.map(
|
||||
(e) => NearbyFileInfo.fromJson(e),
|
||||
@@ -171,14 +142,14 @@ final class NearbyMessageFilesRequest extends NearbyMessageFilesContent {
|
||||
///
|
||||
/// 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.
|
||||
/// Provide [id] from [NearbyMessageFilesRequest].
|
||||
///
|
||||
NearbyMessageFilesResponse({
|
||||
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) {
|
||||
return NearbyMessageFilesResponse(
|
||||
id: json?['id'] ?? '',
|
||||
response: json?['response'] ?? false,
|
||||
isAccepted: json?['isAccepted'] ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
/// The main response to the received [NearbyMessageFilesRequest].
|
||||
///
|
||||
final bool response;
|
||||
final bool isAccepted;
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'response': response,
|
||||
'isAccepted': isAccepted,
|
||||
...super.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'NearbyMessageFileResponse{response: $response, id: $id}';
|
||||
return 'NearbyMessageFileResponse{response: $isAccepted, id: $id}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,29 @@
|
||||
///
|
||||
/// 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.
|
||||
/// After accepting the request,
|
||||
/// user can get files bytes stream from the connected device.
|
||||
///
|
||||
enum NearbyMessageContentType {
|
||||
text,
|
||||
textRequest,
|
||||
textResponse,
|
||||
filesRequest,
|
||||
filesResponse;
|
||||
|
||||
///
|
||||
/// Checks if this is [NearbyMessageContentType.text]
|
||||
/// Checks if this is [NearbyMessageContentType.textRequest]
|
||||
///
|
||||
bool get isText {
|
||||
return this == NearbyMessageContentType.text;
|
||||
bool get isTextRequest {
|
||||
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(
|
||||
NearbyMessageFilesContent content, {
|
||||
NearbyMessageContent content, {
|
||||
required NearbyDeviceInfo? sender,
|
||||
required bool isReceived,
|
||||
}) async {
|
||||
assert(
|
||||
content is NearbyMessageFilesResponse ||
|
||||
content is NearbyMessageFilesRequest,
|
||||
"Provide NearbyMessageFilesResponse or NearbyMessageFilesRequest to handleFileMessageContent()",
|
||||
);
|
||||
if (sender != null) {
|
||||
_sender = sender;
|
||||
Logger.debug('Sender was set to $_sender');
|
||||
@@ -47,7 +52,7 @@ class FileSocketsManager {
|
||||
final isRequest = content is NearbyMessageFilesRequest;
|
||||
|
||||
final isPositiveResponse =
|
||||
content is NearbyMessageFilesResponse && content.response;
|
||||
content is NearbyMessageFilesResponse && content.isAccepted;
|
||||
|
||||
if (isRequest) {
|
||||
_cachedFilesRequest = content;
|
||||
|
||||
@@ -98,7 +98,7 @@ class NearbySocketService {
|
||||
},
|
||||
),
|
||||
);
|
||||
_handleMessage(message);
|
||||
_handleFilesMessage(message);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -203,28 +203,27 @@ class NearbySocketService {
|
||||
?.where((e) => e != null)
|
||||
.map(MessagesStreamMapper.toMessage)
|
||||
.cast<ReceivedNearbyMessage>()
|
||||
.map((e) => MessagesStreamMapper.replaceId(e, _connectedDeviceId!))
|
||||
.map((e) => MessagesStreamMapper.replaceId(e, _connectedDeviceId))
|
||||
.listen(
|
||||
(message) async {
|
||||
try {
|
||||
_handleMessage(message);
|
||||
socketListener.onData(message);
|
||||
} catch (e) {
|
||||
Logger.error(e);
|
||||
}
|
||||
// }
|
||||
},
|
||||
onDone: () {
|
||||
state.value = CommunicationChannelState.notConnected;
|
||||
socketListener.onDone?.call();
|
||||
},
|
||||
onError: (e, s) {
|
||||
Logger.error(e);
|
||||
state.value = CommunicationChannelState.notConnected;
|
||||
socketListener.onError?.call(e, s);
|
||||
},
|
||||
cancelOnError: socketListener.cancelOnError,
|
||||
);
|
||||
(message) async {
|
||||
try {
|
||||
_handleFilesMessage(message);
|
||||
socketListener.onData(message);
|
||||
} catch (e) {
|
||||
Logger.error(e);
|
||||
}
|
||||
},
|
||||
onDone: () {
|
||||
state.value = CommunicationChannelState.notConnected;
|
||||
socketListener.onDone?.call();
|
||||
},
|
||||
onError: (e, s) {
|
||||
Logger.error(e);
|
||||
state.value = CommunicationChannelState.notConnected;
|
||||
socketListener.onError?.call(e, s);
|
||||
},
|
||||
cancelOnError: socketListener.cancelOnError,
|
||||
);
|
||||
}
|
||||
if (_messagesSubscription != null) {
|
||||
state.value = CommunicationChannelState.connected;
|
||||
@@ -235,10 +234,11 @@ class NearbySocketService {
|
||||
}
|
||||
}
|
||||
|
||||
void _handleMessage(NearbyMessage message) {
|
||||
if (message.content is NearbyMessageFilesContent) {
|
||||
void _handleFilesMessage(NearbyMessage message) {
|
||||
if (message.content is NearbyMessageFilesRequest ||
|
||||
message.content is NearbyMessageFilesResponse) {
|
||||
_fileSocketsManager.handleFileMessageContent(
|
||||
message.content as NearbyMessageFilesContent,
|
||||
message.content,
|
||||
isReceived: message is ReceivedNearbyMessage,
|
||||
sender: message is ReceivedNearbyMessage ? message.sender : null,
|
||||
);
|
||||
|
||||
@@ -186,19 +186,23 @@ class NearbyIOSService extends NearbyService {
|
||||
cancelOnError: eventListener.cancelOnError,
|
||||
);
|
||||
_resourcesSubscription = NearbyServiceIOSPlatform.instance.resourcesStream
|
||||
.map((event) {
|
||||
print(event);
|
||||
return event;
|
||||
})
|
||||
.map(ResourcesStreamMapper.toFilesPack)
|
||||
.where((event) => event != null)
|
||||
.cast<ReceivedNearbyFilesPack>()
|
||||
.listen(
|
||||
(e) => filesListener?.onData.call(e),
|
||||
onDone: filesListener?.onDone,
|
||||
onError: (e, s) {
|
||||
Logger.error(e);
|
||||
_state.value = CommunicationChannelState.notConnected;
|
||||
filesListener?.onError?.call(e, s);
|
||||
},
|
||||
cancelOnError: filesListener?.cancelOnError,
|
||||
);
|
||||
(e) => filesListener?.onData.call(e),
|
||||
onDone: filesListener?.onDone,
|
||||
onError: (e, s) {
|
||||
Logger.error(e);
|
||||
_state.value = CommunicationChannelState.notConnected;
|
||||
filesListener?.onError?.call(e, s);
|
||||
},
|
||||
cancelOnError: filesListener?.cancelOnError,
|
||||
);
|
||||
if (_messagesSubscription != null) {
|
||||
Logger.info('Messages subscription was created successfully');
|
||||
eventListener.onCreated?.call();
|
||||
|
||||
@@ -4,15 +4,18 @@ import 'package:nearby_service/src/utils/json_decoder.dart';
|
||||
abstract class MessagesStreamMapper {
|
||||
static ReceivedNearbyMessage replaceId(
|
||||
ReceivedNearbyMessage message,
|
||||
String id,
|
||||
String? id,
|
||||
) {
|
||||
return ReceivedNearbyMessage(
|
||||
content: message.content,
|
||||
sender: NearbyDeviceInfo(
|
||||
id: id,
|
||||
displayName: message.sender.displayName,
|
||||
),
|
||||
);
|
||||
if (id != null) {
|
||||
return ReceivedNearbyMessage(
|
||||
content: message.content,
|
||||
sender: NearbyDeviceInfo(
|
||||
id: id,
|
||||
displayName: message.sender.displayName,
|
||||
),
|
||||
);
|
||||
}
|
||||
throw NearbyServiceException('The provided ID does not exist');
|
||||
}
|
||||
|
||||
static ReceivedNearbyMessage? toMessage(dynamic event) {
|
||||
|
||||
Reference in New Issue
Block a user