refactor: null safety for all utils

This commit is contained in:
Lukas Lihotzki 2021-09-23 18:24:43 +02:00
parent 687a6341f1
commit a7818bbd0f
10 changed files with 89 additions and 101 deletions

View File

@ -864,7 +864,8 @@ class KeyManager {
} }
} else if (event.type == EventTypes.ForwardedRoomKey) { } else if (event.type == EventTypes.ForwardedRoomKey) {
// we *received* an incoming key request // we *received* an incoming key request
if (event.encryptedContent == null) { final encryptedContent = event.encryptedContent;
if (encryptedContent == null) {
return; // event wasn't encrypted, this is a security risk return; // event wasn't encrypted, this is a security risk
} }
final request = outgoingShareRequests.values.firstWhereOrNull((r) => final request = outgoingShareRequests.values.firstWhereOrNull((r) =>
@ -876,7 +877,7 @@ class KeyManager {
} }
final device = request.devices.firstWhereOrNull((d) => final device = request.devices.firstWhereOrNull((d) =>
d.userId == event.sender && d.userId == event.sender &&
d.curve25519Key == event.encryptedContent['sender_key']); d.curve25519Key == encryptedContent['sender_key']);
if (device == null) { if (device == null) {
return; // someone we didn't send our request to replied....better ignore this return; // someone we didn't send our request to replied....better ignore this
} }
@ -885,7 +886,7 @@ class KeyManager {
event.content['forwarding_curve25519_key_chain'] = <String>[]; event.content['forwarding_curve25519_key_chain'] = <String>[];
} }
event.content['forwarding_curve25519_key_chain'] event.content['forwarding_curve25519_key_chain']
.add(event.encryptedContent['sender_key']); .add(encryptedContent['sender_key']);
// TODO: verify that the keys work to decrypt a message // TODO: verify that the keys work to decrypt a message
// alright, all checks out, let's go ahead and store this session // alright, all checks out, let's go ahead and store this session
setInboundGroupSession( setInboundGroupSession(
@ -920,7 +921,8 @@ class KeyManager {
} else if (event.type == EventTypes.RoomKey) { } else if (event.type == EventTypes.RoomKey) {
Logs().v( Logs().v(
'[KeyManager] Received room key with session ${event.content['session_id']}'); '[KeyManager] Received room key with session ${event.content['session_id']}');
if (event.encryptedContent == null) { final encryptedContent = event.encryptedContent;
if (encryptedContent == null) {
Logs().v('[KeyManager] not encrypted, ignoring...'); Logs().v('[KeyManager] not encrypted, ignoring...');
return; // the event wasn't encrypted, this is a security risk; return; // the event wasn't encrypted, this is a security risk;
} }
@ -932,8 +934,8 @@ class KeyManager {
event.content['sender_claimed_ed25519_key'] = sender_ed25519; event.content['sender_claimed_ed25519_key'] = sender_ed25519;
} }
Logs().v('[KeyManager] Keeping room key'); Logs().v('[KeyManager] Keeping room key');
setInboundGroupSession(roomId, sessionId, setInboundGroupSession(
event.encryptedContent['sender_key'], event.content, roomId, sessionId, encryptedContent['sender_key'], event.content,
forwarded: false); forwarded: false);
} }
} }
@ -972,11 +974,11 @@ class RoomKeyRequest extends ToDeviceEvent {
KeyManager keyManager; KeyManager keyManager;
KeyManagerKeyShareRequest request; KeyManagerKeyShareRequest request;
RoomKeyRequest.fromToDeviceEvent( RoomKeyRequest.fromToDeviceEvent(
ToDeviceEvent toDeviceEvent, this.keyManager, this.request) { ToDeviceEvent toDeviceEvent, this.keyManager, this.request)
sender = toDeviceEvent.sender; : super(
content = toDeviceEvent.content; sender: toDeviceEvent.sender,
type = toDeviceEvent.type; content: toDeviceEvent.content,
} type: toDeviceEvent.type);
Room get room => request.room; Room get room => request.room;

View File

@ -653,11 +653,12 @@ class OlmManager {
if (event.type == EventTypes.Dummy) { if (event.type == EventTypes.Dummy) {
// We receive dan encrypted m.dummy. This means that the other end was not able to // We receive dan encrypted m.dummy. This means that the other end was not able to
// decrypt our last message. So, we re-send it. // decrypt our last message. So, we re-send it.
if (event.encryptedContent == null || client.database == null) { final encryptedContent = event.encryptedContent;
if (encryptedContent == null || client.database == null) {
return; return;
} }
final device = client.getUserDeviceKeysByCurve25519Key( final device = client.getUserDeviceKeysByCurve25519Key(
event.encryptedContent.tryGet<String>('sender_key') ?? ''); encryptedContent.tryGet<String>('sender_key') ?? '');
if (device == null) { if (device == null) {
return; // device not found return; // device not found
} }

View File

@ -509,9 +509,10 @@ class SSSS {
} else if (event.type == EventTypes.SecretSend) { } else if (event.type == EventTypes.SecretSend) {
// receiving a secret we asked for // receiving a secret we asked for
Logs().i('[SSSS] Received shared secret...'); Logs().i('[SSSS] Received shared secret...');
final encryptedContent = event.encryptedContent;
if (event.sender != client.userID || if (event.sender != client.userID ||
!pendingShareRequests.containsKey(event.content['request_id']) || !pendingShareRequests.containsKey(event.content['request_id']) ||
event.encryptedContent == null) { encryptedContent == null) {
Logs().i('[SSSS] Not by us or unknown request'); Logs().i('[SSSS] Not by us or unknown request');
return; // we have no idea what we just received return; // we have no idea what we just received
} }
@ -519,7 +520,7 @@ class SSSS {
// alright, as we received a known request id, let's check if the sender is valid // alright, as we received a known request id, let's check if the sender is valid
final device = request.devices.firstWhereOrNull((d) => final device = request.devices.firstWhereOrNull((d) =>
d.userId == event.sender && d.userId == event.sender &&
d.curve25519Key == event.encryptedContent['sender_key']); d.curve25519Key == encryptedContent['sender_key']);
if (device == null) { if (device == null) {
Logs().i('[SSSS] Someone else replied?'); Logs().i('[SSSS] Someone else replied?');
return; // someone replied whom we didn't send the share request to return; // someone replied whom we didn't send the share request to

View File

@ -1,4 +1,3 @@
// @dart=2.9
/* /*
* Famedly Matrix SDK * Famedly Matrix SDK
* Copyright (C) 2019, 2020, 2021 Famedly GmbH * Copyright (C) 2019, 2020, 2021 Famedly GmbH

View File

@ -1,4 +1,3 @@
// @dart=2.9
/* /*
* Famedly Matrix SDK * Famedly Matrix SDK
* Copyright (C) 2019, 2020, 2021 Famedly GmbH * Copyright (C) 2019, 2020, 2021 Famedly GmbH
@ -17,6 +16,8 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import 'package:collection/collection.dart';
import '../../encryption.dart'; import '../../encryption.dart';
import '../../matrix.dart'; import '../../matrix.dart';
import '../event.dart'; import '../event.dart';
@ -77,7 +78,7 @@ abstract class EventLocalizations {
// This map holds how to localize event types, and thus which event types exist. // This map holds how to localize event types, and thus which event types exist.
// If an event exists but it does not have a localized body, set its callback to null // If an event exists but it does not have a localized body, set its callback to null
static final Map<String, static final Map<String,
String Function(Event event, MatrixLocalizations i18n, String body)> String Function(Event event, MatrixLocalizations i18n, String body)?>
localizationsMap = { localizationsMap = {
EventTypes.Sticker: (event, i18n, body) => EventTypes.Sticker: (event, i18n, body) =>
i18n.sentASticker(event.sender.calcDisplayname()), i18n.sentASticker(event.sender.calcDisplayname()),
@ -91,11 +92,9 @@ abstract class EventLocalizations {
i18n.createdTheChat(event.sender.calcDisplayname()), i18n.createdTheChat(event.sender.calcDisplayname()),
EventTypes.RoomTombstone: (event, i18n, body) => i18n.roomHasBeenUpgraded, EventTypes.RoomTombstone: (event, i18n, body) => i18n.roomHasBeenUpgraded,
EventTypes.RoomJoinRules: (event, i18n, body) { EventTypes.RoomJoinRules: (event, i18n, body) {
final joinRules = JoinRules.values.firstWhere( final joinRules = JoinRules.values.firstWhereOrNull((r) =>
(r) => r.toString().replaceAll('JoinRules.', '') ==
r.toString().replaceAll('JoinRules.', '') == event.content['join_rule']);
event.content['join_rule'],
orElse: () => null);
if (joinRules == null) { if (joinRules == null) {
return i18n.changedTheJoinRules(event.sender.calcDisplayname()); return i18n.changedTheJoinRules(event.sender.calcDisplayname());
} else { } else {
@ -177,11 +176,9 @@ abstract class EventLocalizations {
EventTypes.RoomAvatar: (event, i18n, body) => EventTypes.RoomAvatar: (event, i18n, body) =>
i18n.changedTheChatAvatar(event.sender.calcDisplayname()), i18n.changedTheChatAvatar(event.sender.calcDisplayname()),
EventTypes.GuestAccess: (event, i18n, body) { EventTypes.GuestAccess: (event, i18n, body) {
final guestAccess = GuestAccess.values.firstWhere( final guestAccess = GuestAccess.values.firstWhereOrNull((r) =>
(r) => r.toString().replaceAll('GuestAccess.', '') ==
r.toString().replaceAll('GuestAccess.', '') == event.content['guest_access']);
event.content['guest_access'],
orElse: () => null);
if (guestAccess == null) { if (guestAccess == null) {
return i18n.changedTheGuestAccessRules(event.sender.calcDisplayname()); return i18n.changedTheGuestAccessRules(event.sender.calcDisplayname());
} else { } else {
@ -190,11 +187,9 @@ abstract class EventLocalizations {
} }
}, },
EventTypes.HistoryVisibility: (event, i18n, body) { EventTypes.HistoryVisibility: (event, i18n, body) {
final historyVisibility = HistoryVisibility.values.firstWhere( final historyVisibility = HistoryVisibility.values.firstWhereOrNull((r) =>
(r) => r.toString().replaceAll('HistoryVisibility.', '') ==
r.toString().replaceAll('HistoryVisibility.', '') == event.content['history_visibility']);
event.content['history_visibility'],
orElse: () => null);
if (historyVisibility == null) { if (historyVisibility == null) {
return i18n.changedTheHistoryVisibility(event.sender.calcDisplayname()); return i18n.changedTheHistoryVisibility(event.sender.calcDisplayname());
} else { } else {

View File

@ -1,4 +1,3 @@
// @dart=2.9
/* /*
* Famedly Matrix SDK * Famedly Matrix SDK
* Copyright (C) 2021 Famedly GmbH * Copyright (C) 2021 Famedly GmbH
@ -17,6 +16,8 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import 'package:collection/collection.dart';
import 'package:html/parser.dart'; import 'package:html/parser.dart';
import 'package:html/dom.dart'; import 'package:html/dom.dart';
import 'package:html_unescape/html_unescape.dart'; import 'package:html_unescape/html_unescape.dart';
@ -71,9 +72,9 @@ class HtmlToText {
} }
final language = final language =
RegExp(r'language-(\w+)', multiLine: false, caseSensitive: false) RegExp(r'language-(\w+)', multiLine: false, caseSensitive: false)
.firstMatch(match.group(1)); .firstMatch(match.group(1)!);
if (language != null) { if (language != null) {
text = language.group(1) + text; text = language.group(1)! + text;
} }
return text; return text;
} }
@ -116,17 +117,16 @@ class HtmlToText {
opts.listDepth++; opts.listDepth++;
final entries = _listChildNodes(opts, node, {'li'}); final entries = _listChildNodes(opts, node, {'li'});
opts.listDepth--; opts.listDepth--;
var entry = 1; final startStr = node.attributes['start'];
if (node.attributes['start'] is String && final start = (startStr is String &&
RegExp(r'^[0-9]+$', multiLine: false) RegExp(r'^[0-9]+$', multiLine: false).hasMatch(startStr))
.hasMatch(node.attributes['start'])) { ? int.parse(startStr)
entry = int.parse(node.attributes['start']); : 1;
}
return entries return entries
.map((s) => .mapIndexed((index, s) =>
(' ' * opts.listDepth) + (' ' * opts.listDepth) +
'${entry++}. ' + '${start + index}. ' +
s.replaceAll('\n', '\n' + (' ' * opts.listDepth) + ' ')) s.replaceAll('\n', '\n' + (' ' * opts.listDepth) + ' '))
.join('\n'); .join('\n');
} }
@ -134,14 +134,14 @@ class HtmlToText {
static const _listBulletPoints = <String>['', '', '', '']; static const _listBulletPoints = <String>['', '', '', ''];
static List<String> _listChildNodes(_ConvertOpts opts, Element node, static List<String> _listChildNodes(_ConvertOpts opts, Element node,
[Iterable<String> types]) { [Iterable<String>? types]) {
final replies = <String>[]; final replies = <String>[];
for (final child in node.nodes) { for (final child in node.nodes) {
if (types != null && if (types != null &&
types.isNotEmpty && types.isNotEmpty &&
((child is Text) || ((child is Text) ||
((child is Element) && ((child is Element) &&
!types.contains(child.localName.toLowerCase())))) { !types.contains(child.localName!.toLowerCase())))) {
continue; continue;
} }
replies.add(_walkNode(opts, child)); replies.add(_walkNode(opts, child));
@ -166,7 +166,7 @@ class HtmlToText {
var reply = ''; var reply = '';
var lastTag = ''; var lastTag = '';
for (final child in node.nodes) { for (final child in node.nodes) {
final thisTag = child is Element ? child.localName.toLowerCase() : ''; final thisTag = child is Element ? child.localName!.toLowerCase() : '';
if (thisTag == 'p' && lastTag == 'p') { if (thisTag == 'p' && lastTag == 'p') {
reply += '\n\n'; reply += '\n\n';
} else if (_blockTags.contains(thisTag) && } else if (_blockTags.contains(thisTag) &&
@ -187,7 +187,7 @@ class HtmlToText {
// ignore \n between single nodes // ignore \n between single nodes
return node.text == '\n' ? '' : node.text; return node.text == '\n' ? '' : node.text;
} else if (node is Element) { } else if (node is Element) {
final tag = node.localName.toLowerCase(); final tag = node.localName!.toLowerCase();
switch (tag) { switch (tag) {
case 'em': case 'em':
case 'i': case 'i':

View File

@ -1,4 +1,3 @@
// @dart=2.9
/* /*
* Famedly Matrix SDK * Famedly Matrix SDK
* Copyright (C) 2020, 2021 Famedly GmbH * Copyright (C) 2020, 2021 Famedly GmbH
@ -24,10 +23,10 @@ import '../room.dart';
extension ImagePackRoomExtension on Room { extension ImagePackRoomExtension on Room {
/// Get all the active image packs for the specified [usage], mapped by their slug /// Get all the active image packs for the specified [usage], mapped by their slug
Map<String, ImagePackContent> getImagePacks([ImagePackUsage usage]) { Map<String, ImagePackContent> getImagePacks([ImagePackUsage? usage]) {
final allMxcs = <Uri>{}; // used for easy deduplication final allMxcs = <Uri>{}; // used for easy deduplication
final packs = <String, ImagePackContent>{}; final packs = <String, ImagePackContent>{};
final addImagePack = (BasicEvent event, {Room room, String slug}) { final addImagePack = (BasicEvent? event, {Room? room, String? slug}) {
if (event == null) return; if (event == null) return;
final imagePack = event.parsedImagePackContent; final imagePack = event.parsedImagePackContent;
final finalSlug = slugify(slug ?? 'pack'); final finalSlug = slugify(slug ?? 'pack');
@ -42,17 +41,16 @@ extension ImagePackRoomExtension on Room {
!imageUsage.contains(usage)) { !imageUsage.contains(usage)) {
continue; continue;
} }
if (!packs.containsKey(finalSlug)) { packs
packs[finalSlug] = ImagePackContent.fromJson(<String, dynamic>{}); .putIfAbsent(
packs[finalSlug].pack.displayName = imagePack.pack.displayName ?? finalSlug,
room?.displayname ?? () => ImagePackContent.fromJson({})
finalSlug ?? ..pack.displayName = imagePack.pack.displayName ??
''; room?.displayname ??
packs[finalSlug].pack.avatarUrl = finalSlug
imagePack.pack.avatarUrl ?? room?.avatar; ..pack.avatarUrl = imagePack.pack.avatarUrl ?? room?.avatar
packs[finalSlug].pack.attribution = imagePack.pack.attribution; ..pack.attribution = imagePack.pack.attribution)
} .images[entry.key] = image;
packs[finalSlug].images[entry.key] = image;
allMxcs.add(image.url); allMxcs.add(image.url);
} }
}; };
@ -89,7 +87,7 @@ extension ImagePackRoomExtension on Room {
/// Get a flat view of all the image packs of a specified [usage], that is a map of all /// Get a flat view of all the image packs of a specified [usage], that is a map of all
/// slugs to a map of the image code to their mxc url /// slugs to a map of the image code to their mxc url
Map<String, Map<String, String>> getImagePacksFlat([ImagePackUsage usage]) => Map<String, Map<String, String>> getImagePacksFlat([ImagePackUsage? usage]) =>
getImagePacks(usage).map((k, v) => getImagePacks(usage).map((k, v) =>
MapEntry(k, v.images.map((k, v) => MapEntry(k, v.url.toString())))); MapEntry(k, v.images.map((k, v) => MapEntry(k, v.url.toString()))));
} }

View File

@ -1,4 +1,3 @@
// @dart=2.9
/* /*
* Famedly Matrix SDK * Famedly Matrix SDK
* Copyright (C) 2020, 2021 Famedly GmbH * Copyright (C) 2020, 2021 Famedly GmbH

View File

@ -1,4 +1,3 @@
// @dart=2.9
/* /*
* Famedly Matrix SDK * Famedly Matrix SDK
* Copyright (C) 2020, 2021 Famedly GmbH * Copyright (C) 2020, 2021 Famedly GmbH
@ -37,7 +36,7 @@ class SpoilerSyntax extends TagSyntax {
@override @override
Node close(InlineParser parser, Delimiter opener, Delimiter closer, Node close(InlineParser parser, Delimiter opener, Delimiter closer,
{List<Node> Function() getChildren}) { {required List<Node> Function() getChildren}) {
final children = getChildren(); final children = getChildren();
final newChildren = <Node>[]; final newChildren = <Node>[];
var searchingForReason = true; var searchingForReason = true;
@ -72,16 +71,16 @@ class SpoilerSyntax extends TagSyntax {
} }
class EmoteSyntax extends InlineSyntax { class EmoteSyntax extends InlineSyntax {
final Map<String, Map<String, String>> Function() getEmotePacks; final Map<String, Map<String, String>> Function()? getEmotePacks;
Map<String, Map<String, String>> emotePacks; Map<String, Map<String, String>>? emotePacks;
EmoteSyntax(this.getEmotePacks) : super(r':(?:([-\w]+)~)?([-\w]+):'); EmoteSyntax(this.getEmotePacks) : super(r':(?:([-\w]+)~)?([-\w]+):');
@override @override
bool onMatch(InlineParser parser, Match match) { bool onMatch(InlineParser parser, Match match) {
emotePacks ??= getEmotePacks?.call() ?? <String, Map<String, String>>{}; final emotePacks = this.emotePacks ??= getEmotePacks?.call() ?? {};
final pack = match[1] ?? ''; final pack = match[1] ?? '';
final emote = match[2]; final emote = match[2];
String mxc; String? mxc;
if (pack.isEmpty) { if (pack.isEmpty) {
// search all packs // search all packs
for (final emotePack in emotePacks.values) { for (final emotePack in emotePacks.values) {
@ -91,11 +90,11 @@ class EmoteSyntax extends InlineSyntax {
} }
} }
} else { } else {
mxc = emotePacks[pack] != null ? emotePacks[pack][emote] : null; mxc = emotePacks[pack]?[emote];
} }
if (mxc == null) { if (mxc == null) {
// emote not found. Insert the whole thing as plain text // emote not found. Insert the whole thing as plain text
parser.addNode(Text(match[0])); parser.addNode(Text(match[0]!));
return true; return true;
} }
final element = Element.empty('img'); final element = Element.empty('img');
@ -116,8 +115,8 @@ class InlineLatexSyntax extends TagSyntax {
@override @override
bool onMatch(InlineParser parser, Match match) { bool onMatch(InlineParser parser, Match match) {
final element = final element =
Element('span', [Element.text('code', htmlEscape.convert(match[1]))]); Element('span', [Element.text('code', htmlEscape.convert(match[1]!))]);
element.attributes['data-mx-maths'] = htmlAttrEscape.convert(match[1]); element.attributes['data-mx-maths'] = htmlAttrEscape.convert(match[1]!);
parser.addNode(element); parser.addNode(element);
return true; return true;
} }
@ -136,11 +135,11 @@ class BlockLatexSyntax extends BlockSyntax {
var first = true; var first = true;
while (!parser.isDone) { while (!parser.isDone) {
final match = endPattern.firstMatch(parser.current); final match = endPattern.firstMatch(parser.current);
if (match == null || (first && match.group(1).trim().isEmpty)) { if (match == null || (first && match[1]!.trim().isEmpty)) {
childLines.add(parser.current); childLines.add(parser.current);
parser.advance(); parser.advance();
} else { } else {
childLines.add(match.group(1)); childLines.add(match[1]!);
parser.advance(); parser.advance();
break; break;
} }
@ -171,10 +170,10 @@ class PillSyntax extends InlineSyntax {
bool onMatch(InlineParser parser, Match match) { bool onMatch(InlineParser parser, Match match) {
if (match.start > 0 && if (match.start > 0 &&
!RegExp(r'[\s.!?:;\(]').hasMatch(match.input[match.start - 1])) { !RegExp(r'[\s.!?:;\(]').hasMatch(match.input[match.start - 1])) {
parser.addNode(Text(match[0])); parser.addNode(Text(match[0]!));
return true; return true;
} }
final identifier = match[1]; final identifier = match[1]!;
final element = Element.text('a', htmlEscape.convert(identifier)); final element = Element.text('a', htmlEscape.convert(identifier));
element.attributes['href'] = element.attributes['href'] =
htmlAttrEscape.convert('https://matrix.to/#/$identifier'); htmlAttrEscape.convert('https://matrix.to/#/$identifier');
@ -184,19 +183,19 @@ class PillSyntax extends InlineSyntax {
} }
class MentionSyntax extends InlineSyntax { class MentionSyntax extends InlineSyntax {
final String Function(String) getMention; final String Function(String)? getMention;
MentionSyntax(this.getMention) : super(r'(@(?:\[[^\]:]+\]|\w+)(?:#\w+)?)'); MentionSyntax(this.getMention) : super(r'(@(?:\[[^\]:]+\]|\w+)(?:#\w+)?)');
@override @override
bool onMatch(InlineParser parser, Match match) { bool onMatch(InlineParser parser, Match match) {
final mention = getMention?.call(match[1]); final mention = getMention?.call(match[1]!);
if ((match.start > 0 && if ((match.start > 0 &&
!RegExp(r'[\s.!?:;\(]').hasMatch(match.input[match.start - 1])) || !RegExp(r'[\s.!?:;\(]').hasMatch(match.input[match.start - 1])) ||
mention == null) { mention == null) {
parser.addNode(Text(match[0])); parser.addNode(Text(match[0]!));
return true; return true;
} }
final element = Element.text('a', htmlEscape.convert(match[1])); final element = Element.text('a', htmlEscape.convert(match[1]!));
element.attributes['href'] = element.attributes['href'] =
htmlAttrEscape.convert('https://matrix.to/#/$mention'); htmlAttrEscape.convert('https://matrix.to/#/$mention');
parser.addNode(element); parser.addNode(element);
@ -206,8 +205,8 @@ class MentionSyntax extends InlineSyntax {
String markdown( String markdown(
String text, { String text, {
Map<String, Map<String, String>> Function() getEmotePacks, Map<String, Map<String, String>> Function()? getEmotePacks,
String Function(String) getMention, String Function(String)? getMention,
}) { }) {
var ret = markdownToHtml( var ret = markdownToHtml(
text, text,

View File

@ -1,4 +1,3 @@
// @dart=2.9
/* /*
* Famedly Matrix SDK * Famedly Matrix SDK
* Copyright (C) 2020, 2021 Famedly GmbH * Copyright (C) 2020, 2021 Famedly GmbH
@ -20,36 +19,31 @@
import '../../matrix.dart'; import '../../matrix.dart';
class ToDeviceEvent extends BasicEventWithSender { class ToDeviceEvent extends BasicEventWithSender {
Map<String, dynamic> encryptedContent; Map<String, dynamic>? encryptedContent;
String get sender => senderId; String get sender => senderId;
set sender(String sender) => senderId = sender; set sender(String sender) => senderId = sender;
ToDeviceEvent({ ToDeviceEvent({
String sender, required String sender,
String type, required String type,
Map<String, dynamic> content, required Map<String, dynamic> content,
this.encryptedContent, this.encryptedContent,
}) { }) : super(senderId: sender, type: type, content: content);
senderId = sender;
this.type = type;
this.content = content;
}
ToDeviceEvent.fromJson(Map<String, dynamic> json) { factory ToDeviceEvent.fromJson(Map<String, dynamic> json) {
final event = BasicEventWithSender.fromJson(json); final event = BasicEventWithSender.fromJson(json);
senderId = event.senderId; return ToDeviceEvent(
type = event.type; sender: event.senderId, type: event.type, content: event.content);
content = event.content;
} }
} }
class ToDeviceEventDecryptionError extends ToDeviceEvent { class ToDeviceEventDecryptionError extends ToDeviceEvent {
Exception exception; Exception exception;
StackTrace stackTrace; StackTrace? stackTrace;
ToDeviceEventDecryptionError({ ToDeviceEventDecryptionError({
ToDeviceEvent toDeviceEvent, required ToDeviceEvent toDeviceEvent,
this.exception, required this.exception,
this.stackTrace, this.stackTrace,
}) : super( }) : super(
sender: toDeviceEvent.senderId, sender: toDeviceEvent.senderId,