...
 
Commits (3)
......@@ -22,6 +22,9 @@ class AliasesChangeEvent extends StateEvent implements HasDiff {
@override
final AliasesChangeDiff diff;
List<Change<RoomAlias>> get changes =>
diff.aliases.where((c) => c is! Unchanged);
AliasesChangeEvent(
RoomEventArgs args, {
@required this.content,
......@@ -88,6 +91,9 @@ class AliasesChangeDiff extends Diff {
AliasesChangeDiff(this._previous, this._current);
@override
List get props => [aliases];
List<Change<RoomAlias>> get aliases =>
Change.createList(_previous?.aliases, _current.aliases);
Change.list(_previous?.aliases, _current.aliases);
}
......@@ -20,12 +20,75 @@ class PowerLevelsChangeEvent extends StateEvent implements HasDiff {
@override
final PowerLevelsChangeDiff diff;
PowerLevelsChangeEvent(
RoomEventArgs args, {
@required this.content,
PowerLevelsChangeEvent._(
RoomEventArgs args,
this.content,
this.previousContent,
}) : diff = PowerLevelsChangeDiff(previousContent, content),
super(args, stateKey: '');
this.diff,
) : super(args, stateKey: '');
factory PowerLevelsChangeEvent(
RoomEventArgs args, {
@required PowerLevelsChange content,
PowerLevelsChange previousContent,
}) {
final diff = PowerLevelsChangeDiff(previousContent, content);
final changes = diff.changes;
// If there's only 1 change, it's a specific change event
if (changes.length == 1) {
final change = changes.first;
if (change == diff.banLevel) {
return BanLevelChangeEvent._(args, content, previousContent, diff);
} else if (change == diff.inviteLevel) {
return InviteLevelChangeEvent._(args, content, previousContent, diff);
} else if (change == diff.kickLevel) {
return KickLevelChangeEvent._(args, content, previousContent, diff);
} else if (change == diff.redactLevel) {
return RedactLevelChangeEvent._(args, content, previousContent, diff);
} else if (change == diff.stateEventsDefaultLevel) {
return StateEventsDefaultLevelChangeEvent._(
args,
content,
previousContent,
diff,
);
} else if (change == diff.eventsDefaultLevel) {
return EventsDefaultLevelChangeEvent._(
args,
content,
previousContent,
diff,
);
} else if (change.runtimeType == diff.eventLevels.runtimeType &&
diff.eventLevels.values.whereType<Edited>().length == 1) {
return EventLevelChangeEvent._(args, content, previousContent, diff);
} else if (change == diff.userDefaultLevel) {
return UserDefaultLevelChangeEvent._(
args,
content,
previousContent,
diff,
);
// Just check the type for maps
} else if (change.runtimeType == diff.userLevels.runtimeType &&
diff.userLevels.values.whereType<Edited>().length == 1) {
return UserLevelChangeEvent._(args, content, previousContent, diff);
} else if (change == diff.roomNotificationLevel) {
return RoomNotificationLevelChangeEvent._(
args,
content,
previousContent,
diff,
);
} else {
return PowerLevelsChangeEvent._(args, content, previousContent, diff);
}
} else {
return PowerLevelsChangeEvent._(args, content, previousContent, diff);
}
}
factory PowerLevelsChangeEvent.fromJson(RoomEventArgs args, dynamic json) {
if (!hasStateKey(json)) {
......@@ -112,9 +175,9 @@ class PowerLevelsChange extends EventContent {
redactLevel: redact,
stateEventsDefaultLevel: stateDefault,
eventsDefaultLevel: eventsDefault,
eventLevels: events,
eventLevels: events ?? {},
userDefaultLevel: userDefault,
userLevels: users,
userLevels: users ?? [],
roomNotificationLevel: roomNotifications,
);
}
......@@ -171,6 +234,20 @@ class PowerLevelsChangeDiff extends Diff {
final PowerLevelsChange _previous;
final PowerLevelsChange _current;
@override
List get props => [
banLevel,
inviteLevel,
kickLevel,
redactLevel,
stateEventsDefaultLevel,
eventsDefaultLevel,
eventLevels,
userDefaultLevel,
userLevels,
roomNotificationLevel,
];
PowerLevelsChangeDiff(this._previous, this._current);
Change<int> get banLevel => Change(_previous?.banLevel, _current.banLevel);
......@@ -192,14 +269,14 @@ class PowerLevelsChangeDiff extends Diff {
Change(_previous?.eventsDefaultLevel, _current.eventsDefaultLevel);
Map<Type, Change<int>> get eventLevels =>
Change.createMap(_previous?.eventLevels, _current.eventLevels)
Change.map(_previous?.eventLevels, _current.eventLevels)
.toEdited(defaultValue: _current.eventsDefaultLevel);
Change<int> get userDefaultLevel =>
Change(_previous?.userDefaultLevel, _current.userDefaultLevel);
Map<UserId, Change<int>> get userLevels =>
Change.createMap(_previous?.userLevels, _current.userLevels)
Change.map(_previous?.userLevels, _current.userLevels)
.toEdited(defaultValue: _current.userDefaultLevel);
Change<int> get roomNotificationLevel =>
......@@ -216,3 +293,133 @@ extension _ChangeMapExtension<K, V> on Map<K, Change<V>> {
: MapEntry(key, change));
}
}
class BanLevelChangeEvent extends PowerLevelsChangeEvent {
Edited<int> get change => diff.banLevel;
BanLevelChangeEvent._(
RoomEventArgs args,
PowerLevelsChange content,
PowerLevelsChange previousContent,
PowerLevelsChangeDiff diff,
) : super._(args, content, previousContent, diff);
}
class InviteLevelChangeEvent extends PowerLevelsChangeEvent {
Edited<int> get change => diff.inviteLevel;
InviteLevelChangeEvent._(
RoomEventArgs args,
PowerLevelsChange content,
PowerLevelsChange previousContent,
PowerLevelsChangeDiff diff,
) : super._(args, content, previousContent, diff);
}
class KickLevelChangeEvent extends PowerLevelsChangeEvent {
Edited<int> get change => diff.kickLevel;
KickLevelChangeEvent._(
RoomEventArgs args,
PowerLevelsChange content,
PowerLevelsChange previousContent,
PowerLevelsChangeDiff diff,
) : super._(args, content, previousContent, diff);
}
class RedactLevelChangeEvent extends PowerLevelsChangeEvent {
Edited<int> get change => diff.redactLevel;
RedactLevelChangeEvent._(
RoomEventArgs args,
PowerLevelsChange content,
PowerLevelsChange previousContent,
PowerLevelsChangeDiff diff,
) : super._(args, content, previousContent, diff);
}
class StateEventsDefaultLevelChangeEvent extends PowerLevelsChangeEvent {
Edited<int> get change => diff.stateEventsDefaultLevel;
StateEventsDefaultLevelChangeEvent._(
RoomEventArgs args,
PowerLevelsChange content,
PowerLevelsChange previousContent,
PowerLevelsChangeDiff diff,
) : super._(args, content, previousContent, diff);
}
class EventsDefaultLevelChangeEvent extends PowerLevelsChangeEvent {
Edited<int> get change => diff.eventsDefaultLevel;
EventsDefaultLevelChangeEvent._(
RoomEventArgs args,
PowerLevelsChange content,
PowerLevelsChange previousContent,
PowerLevelsChangeDiff diff,
) : super._(args, content, previousContent, diff);
}
class EventLevelChangeEvent extends PowerLevelsChangeEvent {
EventLevelChange get change => diff.eventLevels.entries
.where((e) => e.value is Edited<int>)
.map((e) => EventLevelChange._(e.key, e.value as Edited<int>))
.first;
EventLevelChangeEvent._(
RoomEventArgs args,
PowerLevelsChange content,
PowerLevelsChange previousContent,
PowerLevelsChangeDiff diff,
) : super._(args, content, previousContent, diff);
}
class EventLevelChange extends Edited<int> {
final Type type;
EventLevelChange._(this.type, Edited<int> original)
: super(original.previousValue, original.value);
}
class UserDefaultLevelChangeEvent extends PowerLevelsChangeEvent {
Edited<int> get change => diff.userDefaultLevel;
UserDefaultLevelChangeEvent._(
RoomEventArgs args,
PowerLevelsChange content,
PowerLevelsChange previousContent,
PowerLevelsChangeDiff diff,
) : super._(args, content, previousContent, diff);
}
class UserLevelChangeEvent extends PowerLevelsChangeEvent {
UserLevelChange get change => diff.userLevels.entries
.where((e) => e.value is Edited<int>)
.map((e) => UserLevelChange._(e.key, e.value as Edited<int>))
.first;
UserLevelChangeEvent._(
RoomEventArgs args,
PowerLevelsChange content,
PowerLevelsChange previousContent,
PowerLevelsChangeDiff diff,
) : super._(args, content, previousContent, diff);
}
class UserLevelChange extends Edited<int> {
final UserId id;
UserLevelChange._(this.id, Edited<int> original)
: super(original.previousValue, original.value);
}
class RoomNotificationLevelChangeEvent extends PowerLevelsChangeEvent {
Edited<int> get change => diff.roomNotificationLevel;
RoomNotificationLevelChangeEvent._(
RoomEventArgs args,
PowerLevelsChange content,
PowerLevelsChange previousContent,
PowerLevelsChangeDiff diff,
) : super._(args, content, previousContent, diff);
}
......@@ -5,6 +5,7 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
import 'package:matrix_sdk/src/event/room/room_event.dart';
import 'package:meta/meta.dart';
import '../../event.dart';
......@@ -33,7 +34,24 @@ abstract class StateEvent extends RoomEvent {
/// If a property is a [Map], it will contain the keys of the previous and
/// current content combined, with values which are null if they're unchanged,
/// and the current values if they've changed.
abstract class Diff {}
abstract class Diff {
@protected
List get props;
/// Returns all properties of this diff that are not [Unchanged]
/// or contain [Unchanged].
///
/// Items can be a [Change], `Map<dynamic, Change>` or `List<Change>`.
List get changes {
return props
.where((p) =>
p is! Unchanged &&
(p is Map<dynamic, Change>
? p.hasChanges
: p is List<Change> ? p.hasChanges : true))
.toList(growable: false);
}
}
abstract class HasDiff {
Diff get diff;
......@@ -44,41 +62,36 @@ abstract class Change<T> {
Change._();
factory Change(T previous, T current, {bool inIterable = false}) {
if (!inIterable) {
return previous != current
? Edited(previous, current)
: Unchanged(current);
factory Change._inIterable(T previous, T current) {
if (previous == current) {
return Unchanged(current);
} else if (previous == null && current != null) {
return Added(current);
} else if (previous != null && current == null) {
return Removed(previous);
} else {
if (previous == current) {
return Unchanged(current);
} else if (previous == null && current != null) {
return Added(current);
} else if (previous != null && current == null) {
return Removed(previous);
} else {
return Edited(previous, current);
}
return Edited(previous, current);
}
}
static Map<K, Change<V>> createMap<K, V>(
Map<K, V> previous,
Map<K, V> current,
) {
factory Change(T previous, T current) {
return previous != current ? Edited(previous, current) : Unchanged(current);
}
static Map<K, Change<V>> map<K, V>(Map<K, V> previous, Map<K, V> current) {
return previous == null || previous.isEmpty
? current.map((key, value) => MapEntry(key, Added(value)))
: Map.fromEntries(
previous.keys.followedBy(current.keys).toSet().map(
(key) => MapEntry(
key,
Change(previous[key], current[key], inIterable: true),
Change._inIterable(previous[key], current[key]),
),
),
);
}
static List<Change<T>> createList<T>(List<T> previous, List<T> current) {
static List<Change<T>> list<T>(List<T> previous, List<T> current) {
if (previous == null || previous.isEmpty) {
return current.map((e) => Added(e)).toList(growable: false);
}
......@@ -97,6 +110,18 @@ abstract class Change<T> {
@override
String toString() => '$runtimeType($value)';
@override
bool operator ==(other) {
if (other is Change) {
return this.runtimeType == other.runtimeType && this.value == other.value;
} else {
return false;
}
}
@override
int get hashCode => runtimeType.hashCode + value.hashCode;
}
class Unchanged<T> extends Change<T> {
......@@ -130,6 +155,19 @@ class Edited<T> extends Change<T> {
@override
String toString() => '$runtimeType($previousValue -> $value)';
@override
bool operator ==(other) {
if (other is Edited) {
return this.value == other.value &&
this.previousValue == other.previousValue;
} else {
return false;
}
}
@override
int get hashCode => runtimeType.hashCode + value.hashCode;
}
extension ChangeMapExtension<K, V> on Map<K, Change<V>> {
......