feat: Implement parsing and sending of retractions
This commit is contained in:
parent
39e9c55fae
commit
edc86a10b3
@ -73,6 +73,7 @@ export 'package:moxxmpp/src/xeps/xep_0384/types.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0384/xep_0384.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0385.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0414.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0424.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0446.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0447.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0448.dart';
|
||||
|
@ -8,6 +8,7 @@ import 'package:moxxmpp/src/xeps/xep_0066.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0085.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0359.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0385.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0424.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0446.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0447.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0461.dart';
|
||||
@ -71,6 +72,7 @@ class MessageEvent extends XmppEvent {
|
||||
this.fun,
|
||||
this.funReplacement,
|
||||
this.funCancellation,
|
||||
this.messageRetraction,
|
||||
});
|
||||
final String body;
|
||||
final JID fromJid;
|
||||
@ -90,6 +92,7 @@ class MessageEvent extends XmppEvent {
|
||||
final String? funReplacement;
|
||||
final String? funCancellation;
|
||||
final bool encrypted;
|
||||
final MessageRetractionData? messageRetraction;
|
||||
final Map<String, dynamic> other;
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ import 'package:moxxmpp/src/xeps/xep_0203.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0359.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0380.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0385.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0424.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0446.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0447.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0461.dart';
|
||||
@ -55,6 +56,9 @@ class StanzaHandlerData with _$StanzaHandlerData {
|
||||
// This is for stanza handlers that are not part of the XMPP library but still need
|
||||
// pass data around.
|
||||
@Default(<String, dynamic>{}) Map<String, dynamic> other,
|
||||
// If non-null, then it indicates the origin Id of the message that should be
|
||||
// retracted
|
||||
MessageRetractionData? messageRetraction,
|
||||
}
|
||||
) = _StanzaHandlerData;
|
||||
}
|
||||
|
@ -54,7 +54,11 @@ mixin _$StanzaHandlerData {
|
||||
DelayedDelivery? get delayedDelivery =>
|
||||
throw _privateConstructorUsedError; // This is for stanza handlers that are not part of the XMPP library but still need
|
||||
// pass data around.
|
||||
Map<String, dynamic> get other => throw _privateConstructorUsedError;
|
||||
Map<String, dynamic> get other =>
|
||||
throw _privateConstructorUsedError; // If non-null, then it indicates the origin Id of the message that should be
|
||||
// retracted
|
||||
MessageRetractionData? get messageRetraction =>
|
||||
throw _privateConstructorUsedError;
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
$StanzaHandlerDataCopyWith<StanzaHandlerData> get copyWith =>
|
||||
@ -87,7 +91,8 @@ abstract class $StanzaHandlerDataCopyWith<$Res> {
|
||||
bool encrypted,
|
||||
ExplicitEncryptionType? encryptionType,
|
||||
DelayedDelivery? delayedDelivery,
|
||||
Map<String, dynamic> other});
|
||||
Map<String, dynamic> other,
|
||||
MessageRetractionData? messageRetraction});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@ -122,6 +127,7 @@ class _$StanzaHandlerDataCopyWithImpl<$Res>
|
||||
Object? encryptionType = freezed,
|
||||
Object? delayedDelivery = freezed,
|
||||
Object? other = freezed,
|
||||
Object? messageRetraction = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
done: done == freezed
|
||||
@ -208,6 +214,10 @@ class _$StanzaHandlerDataCopyWithImpl<$Res>
|
||||
? _value.other
|
||||
: other // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, dynamic>,
|
||||
messageRetraction: messageRetraction == freezed
|
||||
? _value.messageRetraction
|
||||
: messageRetraction // ignore: cast_nullable_to_non_nullable
|
||||
as MessageRetractionData?,
|
||||
));
|
||||
}
|
||||
}
|
||||
@ -240,7 +250,8 @@ abstract class _$$_StanzaHandlerDataCopyWith<$Res>
|
||||
bool encrypted,
|
||||
ExplicitEncryptionType? encryptionType,
|
||||
DelayedDelivery? delayedDelivery,
|
||||
Map<String, dynamic> other});
|
||||
Map<String, dynamic> other,
|
||||
MessageRetractionData? messageRetraction});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@ -277,6 +288,7 @@ class __$$_StanzaHandlerDataCopyWithImpl<$Res>
|
||||
Object? encryptionType = freezed,
|
||||
Object? delayedDelivery = freezed,
|
||||
Object? other = freezed,
|
||||
Object? messageRetraction = freezed,
|
||||
}) {
|
||||
return _then(_$_StanzaHandlerData(
|
||||
done == freezed
|
||||
@ -363,6 +375,10 @@ class __$$_StanzaHandlerDataCopyWithImpl<$Res>
|
||||
? _value._other
|
||||
: other // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, dynamic>,
|
||||
messageRetraction: messageRetraction == freezed
|
||||
? _value.messageRetraction
|
||||
: messageRetraction // ignore: cast_nullable_to_non_nullable
|
||||
as MessageRetractionData?,
|
||||
));
|
||||
}
|
||||
}
|
||||
@ -387,7 +403,8 @@ class _$_StanzaHandlerData implements _StanzaHandlerData {
|
||||
this.encrypted = false,
|
||||
this.encryptionType,
|
||||
this.delayedDelivery,
|
||||
final Map<String, dynamic> other = const <String, dynamic>{}})
|
||||
final Map<String, dynamic> other = const <String, dynamic>{},
|
||||
this.messageRetraction})
|
||||
: _other = other;
|
||||
|
||||
// Indicates to the runner that processing is now done. This means that all
|
||||
@ -463,9 +480,14 @@ class _$_StanzaHandlerData implements _StanzaHandlerData {
|
||||
return EqualUnmodifiableMapView(_other);
|
||||
}
|
||||
|
||||
// If non-null, then it indicates the origin Id of the message that should be
|
||||
// retracted
|
||||
@override
|
||||
final MessageRetractionData? messageRetraction;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'StanzaHandlerData(done: $done, cancel: $cancel, cancelReason: $cancelReason, stanza: $stanza, retransmitted: $retransmitted, sims: $sims, sfs: $sfs, oob: $oob, stableId: $stableId, reply: $reply, chatState: $chatState, isCarbon: $isCarbon, deliveryReceiptRequested: $deliveryReceiptRequested, isMarkable: $isMarkable, fun: $fun, funReplacement: $funReplacement, funCancellation: $funCancellation, encrypted: $encrypted, encryptionType: $encryptionType, delayedDelivery: $delayedDelivery, other: $other)';
|
||||
return 'StanzaHandlerData(done: $done, cancel: $cancel, cancelReason: $cancelReason, stanza: $stanza, retransmitted: $retransmitted, sims: $sims, sfs: $sfs, oob: $oob, stableId: $stableId, reply: $reply, chatState: $chatState, isCarbon: $isCarbon, deliveryReceiptRequested: $deliveryReceiptRequested, isMarkable: $isMarkable, fun: $fun, funReplacement: $funReplacement, funCancellation: $funCancellation, encrypted: $encrypted, encryptionType: $encryptionType, delayedDelivery: $delayedDelivery, other: $other, messageRetraction: $messageRetraction)';
|
||||
}
|
||||
|
||||
@override
|
||||
@ -501,7 +523,9 @@ class _$_StanzaHandlerData implements _StanzaHandlerData {
|
||||
.equals(other.encryptionType, encryptionType) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.delayedDelivery, delayedDelivery) &&
|
||||
const DeepCollectionEquality().equals(other._other, this._other));
|
||||
const DeepCollectionEquality().equals(other._other, this._other) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.messageRetraction, messageRetraction));
|
||||
}
|
||||
|
||||
@override
|
||||
@ -527,7 +551,8 @@ class _$_StanzaHandlerData implements _StanzaHandlerData {
|
||||
const DeepCollectionEquality().hash(encrypted),
|
||||
const DeepCollectionEquality().hash(encryptionType),
|
||||
const DeepCollectionEquality().hash(delayedDelivery),
|
||||
const DeepCollectionEquality().hash(_other)
|
||||
const DeepCollectionEquality().hash(_other),
|
||||
const DeepCollectionEquality().hash(messageRetraction)
|
||||
]);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@ -556,7 +581,8 @@ abstract class _StanzaHandlerData implements StanzaHandlerData {
|
||||
final bool encrypted,
|
||||
final ExplicitEncryptionType? encryptionType,
|
||||
final DelayedDelivery? delayedDelivery,
|
||||
final Map<String, dynamic> other}) = _$_StanzaHandlerData;
|
||||
final Map<String, dynamic> other,
|
||||
final MessageRetractionData? messageRetraction}) = _$_StanzaHandlerData;
|
||||
|
||||
@override // Indicates to the runner that processing is now done. This means that all
|
||||
// pre-processing is done and no other handlers should be consulted.
|
||||
@ -606,6 +632,9 @@ abstract class _StanzaHandlerData implements StanzaHandlerData {
|
||||
@override // This is for stanza handlers that are not part of the XMPP library but still need
|
||||
// pass data around.
|
||||
Map<String, dynamic> get other;
|
||||
@override // If non-null, then it indicates the origin Id of the message that should be
|
||||
// retracted
|
||||
MessageRetractionData? get messageRetraction;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$_StanzaHandlerDataCopyWith<_$_StanzaHandlerData> get copyWith =>
|
||||
|
@ -24,3 +24,4 @@ const omemoManager = 'org.moxxmpp.omemomanager';
|
||||
const emeManager = 'org.moxxmpp.ememanager';
|
||||
const cryptographicHashManager = 'org.moxxmpp.cryptographichashmanager';
|
||||
const delayedDeliveryManager = 'org.moxxmpp.delayeddeliverymanager';
|
||||
const messageRetractionManager = 'org.moxxmpp.messageretractionmanager';
|
||||
|
@ -13,12 +13,12 @@ import 'package:moxxmpp/src/xeps/xep_0085.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0184.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0333.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0359.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0424.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0446.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0447.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0448.dart';
|
||||
|
||||
class MessageDetails {
|
||||
|
||||
const MessageDetails({
|
||||
required this.to,
|
||||
this.body,
|
||||
@ -35,6 +35,7 @@ class MessageDetails {
|
||||
this.funReplacement,
|
||||
this.funCancellation,
|
||||
this.shouldEncrypt = false,
|
||||
this.messageRetraction,
|
||||
});
|
||||
final String to;
|
||||
final String? body;
|
||||
@ -51,6 +52,7 @@ class MessageDetails {
|
||||
final String? funReplacement;
|
||||
final String? funCancellation;
|
||||
final bool shouldEncrypt;
|
||||
final MessageRetractionData? messageRetraction;
|
||||
}
|
||||
|
||||
class MessageManager extends XmppManagerBase {
|
||||
@ -95,6 +97,7 @@ class MessageManager extends XmppManagerBase {
|
||||
funReplacement: state.funReplacement,
|
||||
funCancellation: state.funCancellation,
|
||||
encrypted: state.encrypted,
|
||||
messageRetraction: state.messageRetraction,
|
||||
other: state.other,
|
||||
),);
|
||||
|
||||
@ -159,6 +162,8 @@ class MessageManager extends XmppManagerBase {
|
||||
} else if (firstSource is StatelessFileSharingEncryptedSource) {
|
||||
body = firstSource.source.url;
|
||||
}
|
||||
} else if (details.messageRetraction?.fallback != null) {
|
||||
body = details.messageRetraction!.fallback;
|
||||
}
|
||||
|
||||
stanza.addChild(
|
||||
@ -216,6 +221,33 @@ class MessageManager extends XmppManagerBase {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (details.messageRetraction != null) {
|
||||
stanza.addChild(
|
||||
XMLNode.xmlns(
|
||||
tag: 'apply-to',
|
||||
xmlns: fasteningXmlns,
|
||||
attributes: <String, String>{
|
||||
'id': details.messageRetraction!.id,
|
||||
},
|
||||
children: [
|
||||
XMLNode.xmlns(
|
||||
tag: 'retract',
|
||||
xmlns: messageRetractionXmlns,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
if (details.messageRetraction!.fallback != null) {
|
||||
stanza.addChild(
|
||||
XMLNode.xmlns(
|
||||
tag: 'fallback',
|
||||
xmlns: fallbackIndicationXmlns,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
getAttributes().sendStanza(stanza, awaitable: false);
|
||||
}
|
||||
|
@ -114,6 +114,15 @@ const simsXmlns = 'urn:xmpp:sims:1';
|
||||
// XEP-0420
|
||||
const sceXmlns = 'urn:xmpp:sce:1';
|
||||
|
||||
// XEP-0422
|
||||
const fasteningXmlns = 'urn:xmpp:fasten:0';
|
||||
|
||||
// XEP-0424
|
||||
const messageRetractionXmlns = 'urn:xmpp:message-retract:0';
|
||||
|
||||
// XEp-0428
|
||||
const fallbackIndicationXmlns = 'urn:xmpp:fallback:0';
|
||||
|
||||
// XEP-0446
|
||||
const fileMetadataXmlns = 'urn:xmpp:file:metadata:0';
|
||||
|
||||
|
59
packages/moxxmpp/lib/src/xeps/xep_0424.dart
Normal file
59
packages/moxxmpp/lib/src/xeps/xep_0424.dart
Normal file
@ -0,0 +1,59 @@
|
||||
import 'package:moxxmpp/src/managers/base.dart';
|
||||
import 'package:moxxmpp/src/managers/data.dart';
|
||||
import 'package:moxxmpp/src/managers/handlers.dart';
|
||||
import 'package:moxxmpp/src/managers/namespaces.dart';
|
||||
import 'package:moxxmpp/src/namespaces.dart';
|
||||
import 'package:moxxmpp/src/stanza.dart';
|
||||
|
||||
class MessageRetractionData {
|
||||
MessageRetractionData(this.id, this.fallback);
|
||||
final String? fallback;
|
||||
final String id;
|
||||
}
|
||||
|
||||
class MessageRetractionManager extends XmppManagerBase {
|
||||
@override
|
||||
String getName() => 'MessageRetractionManager';
|
||||
|
||||
@override
|
||||
String getId() => messageRetractionManager;
|
||||
|
||||
@override
|
||||
List<String> getDiscoFeatures() => [ messageRetractionXmlns ];
|
||||
|
||||
@override
|
||||
List<StanzaHandler> getIncomingStanzaHandlers() => [
|
||||
StanzaHandler(
|
||||
stanzaTag: 'message',
|
||||
callback: _onMessage,
|
||||
// Before the MessageManager
|
||||
priority: -99,
|
||||
)
|
||||
];
|
||||
|
||||
@override
|
||||
Future<bool> isSupported() async => true;
|
||||
|
||||
Future<StanzaHandlerData> _onMessage(Stanza message, StanzaHandlerData state) async {
|
||||
final applyTo = message.firstTag('apply-to', xmlns: fasteningXmlns);
|
||||
if (applyTo == null) {
|
||||
return state;
|
||||
}
|
||||
|
||||
final retract = applyTo.firstTag('retract', xmlns: messageRetractionXmlns);
|
||||
if (retract == null) {
|
||||
return state;
|
||||
}
|
||||
|
||||
final isFallbackBody = message.firstTag('fallback', xmlns: fallbackIndicationXmlns) != null;
|
||||
|
||||
return state.copyWith(
|
||||
messageRetraction: MessageRetractionData(
|
||||
applyTo.attributes['id']! as String,
|
||||
isFallbackBody ?
|
||||
message.firstTag('body')?.innerText() :
|
||||
null,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user