refactor: Use strippedstatevent as base for room state and user class
Before we have used the Event class for all state events while for invite rooms those actually were StrippedStateEvent objects. This created some problems like we needed to set a fake originServerTs (usually to DateTime.now()). Actually we don't need the additional keys for state events most of the time so just using StrippedStateEvent for all states and typecasting them to event where needed is not much of a hassle while we benefit from a more clear structure. This also now uses StrippedStateEvent as a base class for the User class which makes the User class more minimal as keys like event_id and origin_server_ts are no longer necessary. As we create a lot of fake User objects where we had to put fake values in it, it brings more benefits than problems to just get rid of those fields.
This commit is contained in:
parent
ffe496a234
commit
5b46ae6e31
|
|
@ -1312,7 +1312,10 @@ class Client extends MatrixApi {
|
|||
|
||||
final CachedStreamController<Event> onGroupMember = CachedStreamController();
|
||||
|
||||
final CachedStreamController<Event> onRoomState = CachedStreamController();
|
||||
/// When a state in a room has been updated this will return the room ID
|
||||
/// and the state event.
|
||||
final CachedStreamController<({String roomId, StrippedStateEvent state})>
|
||||
onRoomState = CachedStreamController();
|
||||
|
||||
/// How long should the app wait until it retrys the synchronisation after
|
||||
/// an error?
|
||||
|
|
@ -2385,7 +2388,7 @@ class Client extends MatrixApi {
|
|||
|
||||
switch (eventUpdate.type) {
|
||||
case EventUpdateType.inviteState:
|
||||
room.setState(Event.fromJson(eventUpdate.content, room));
|
||||
room.setState(StrippedStateEvent.fromJson(eventUpdate.content));
|
||||
break;
|
||||
case EventUpdateType.state:
|
||||
case EventUpdateType.timeline:
|
||||
|
|
|
|||
|
|
@ -247,10 +247,7 @@ class Event extends MatrixEvent {
|
|||
prevContent: prevContent,
|
||||
content: content,
|
||||
typeKey: type,
|
||||
eventId: eventId,
|
||||
senderId: senderId,
|
||||
originServerTs: originServerTs,
|
||||
unsigned: unsigned,
|
||||
room: room,
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ class Room {
|
|||
/// The room states are a key value store of the key (`type`,`state_key`) => State(event).
|
||||
/// In a lot of cases the `state_key` might be an empty string. You **should** use the
|
||||
/// methods `getState()` and `setState()` to interact with the room states.
|
||||
Map<String, Map<String, Event>> states = {};
|
||||
Map<String, Map<String, StrippedStateEvent>> states = {};
|
||||
|
||||
/// Key-Value store for ephemerals.
|
||||
Map<String, BasicRoomEvent> ephemerals = {};
|
||||
|
|
@ -157,19 +157,31 @@ class Room {
|
|||
|
||||
/// Returns the [Event] for the given [typeKey] and optional [stateKey].
|
||||
/// If no [stateKey] is provided, it defaults to an empty string.
|
||||
Event? getState(String typeKey, [String stateKey = '']) =>
|
||||
/// This returns either a `StrippedStateEvent` for rooms with membership
|
||||
/// "invite" or a `User`/`Event`. If you need additional information like
|
||||
/// the Event ID or originServerTs you need to do a type check like:
|
||||
/// ```dart
|
||||
/// if (state is Event) { /*...*/ }
|
||||
/// ```
|
||||
StrippedStateEvent? getState(String typeKey, [String stateKey = '']) =>
|
||||
states[typeKey]?[stateKey];
|
||||
|
||||
/// Adds the [state] to this room and overwrites a state with the same
|
||||
/// typeKey/stateKey key pair if there is one.
|
||||
void setState(Event state) {
|
||||
void setState(StrippedStateEvent state) {
|
||||
// Ignore other non-state events
|
||||
final stateKey = state.stateKey;
|
||||
|
||||
// For non invite rooms this is usually an Event and we should validate
|
||||
// the room ID:
|
||||
if (state is Event) {
|
||||
final roomId = state.roomId;
|
||||
if (roomId == null || roomId != id) {
|
||||
Logs().w('Tried to set state event for wrong room!');
|
||||
Logs().wtf('Tried to set state event for wrong room!');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (stateKey == null) {
|
||||
Logs().w(
|
||||
'Tried to set a non state event with type "${state.type}" as state event for a room',
|
||||
|
|
@ -179,7 +191,7 @@ class Room {
|
|||
|
||||
(states[state.type] ??= {})[stateKey] = state;
|
||||
|
||||
client.onRoomState.add(state);
|
||||
client.onRoomState.add((roomId: id, state: state));
|
||||
}
|
||||
|
||||
/// ID of the fully read marker event.
|
||||
|
|
@ -276,9 +288,11 @@ class Room {
|
|||
if (membership == Membership.invite) {
|
||||
final ownMember = unsafeGetUserFromMemoryOrFallback(client.userID!);
|
||||
|
||||
ownMember.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n);
|
||||
unsafeGetUserFromMemoryOrFallback(ownMember.senderId)
|
||||
.calcDisplayname(i18n: i18n);
|
||||
if (ownMember.senderId != ownMember.stateKey) {
|
||||
return ownMember.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n);
|
||||
return unsafeGetUserFromMemoryOrFallback(ownMember.senderId)
|
||||
.calcDisplayname(i18n: i18n);
|
||||
}
|
||||
}
|
||||
if (membership == Membership.leave) {
|
||||
|
|
@ -312,7 +326,7 @@ class Room {
|
|||
if (heroes != null && heroes.length == 1) {
|
||||
final hero = getState(EventTypes.RoomMember, heroes.first);
|
||||
if (hero != null) {
|
||||
return hero.asUser.avatarUrl;
|
||||
return hero.asUser(this).avatarUrl;
|
||||
}
|
||||
}
|
||||
if (isDirectChat) {
|
||||
|
|
@ -383,6 +397,7 @@ class Room {
|
|||
states.forEach((final String key, final entry) {
|
||||
final state = entry[''];
|
||||
if (state == null) return;
|
||||
if (state is! Event) return;
|
||||
if (state.originServerTs.millisecondsSinceEpoch >
|
||||
lastTime.millisecondsSinceEpoch) {
|
||||
lastTime = state.originServerTs;
|
||||
|
|
@ -1554,7 +1569,7 @@ class Room {
|
|||
if (members != null) {
|
||||
return members.entries
|
||||
.where((entry) => entry.value.type == EventTypes.RoomMember)
|
||||
.map((entry) => entry.value.asUser)
|
||||
.map((entry) => entry.value.asUser(this))
|
||||
.where((user) => membershipFilter.contains(user.membership))
|
||||
.toList();
|
||||
}
|
||||
|
|
@ -1643,7 +1658,7 @@ class Room {
|
|||
User unsafeGetUserFromMemoryOrFallback(String mxID) {
|
||||
final user = getState(EventTypes.RoomMember, mxID);
|
||||
if (user != null) {
|
||||
return user.asUser;
|
||||
return user.asUser(this);
|
||||
} else {
|
||||
if (mxID.isValidMatrixId) {
|
||||
// ignore: discarded_futures
|
||||
|
|
@ -1672,7 +1687,7 @@ class Room {
|
|||
// Checks if the user is really missing
|
||||
final stateUser = getState(EventTypes.RoomMember, mxID);
|
||||
if (stateUser != null) {
|
||||
return stateUser.asUser;
|
||||
return stateUser.asUser(this);
|
||||
}
|
||||
|
||||
// it may be in the database
|
||||
|
|
|
|||
|
|
@ -19,7 +19,10 @@
|
|||
import 'package:matrix/matrix.dart';
|
||||
|
||||
/// Represents a Matrix User which may be a participant in a Matrix Room.
|
||||
class User extends Event {
|
||||
class User extends StrippedStateEvent {
|
||||
final Room room;
|
||||
final Map<String, Object?>? prevContent;
|
||||
|
||||
factory User(
|
||||
String id, {
|
||||
String? membership,
|
||||
|
|
@ -30,7 +33,6 @@ class User extends Event {
|
|||
return User.fromState(
|
||||
stateKey: id,
|
||||
senderId: id,
|
||||
eventId: 'fake_event',
|
||||
content: {
|
||||
if (membership != null) 'membership': membership,
|
||||
if (displayName != null) 'displayname': displayName,
|
||||
|
|
@ -38,20 +40,16 @@ class User extends Event {
|
|||
},
|
||||
typeKey: EventTypes.RoomMember,
|
||||
room: room,
|
||||
originServerTs: DateTime.now(),
|
||||
);
|
||||
}
|
||||
|
||||
User.fromState({
|
||||
super.prevContent,
|
||||
required String super.stateKey,
|
||||
super.content = const {},
|
||||
required String typeKey,
|
||||
required super.eventId,
|
||||
required super.senderId,
|
||||
required super.originServerTs,
|
||||
super.unsigned,
|
||||
required super.room,
|
||||
required this.room,
|
||||
this.prevContent,
|
||||
}) : super(
|
||||
type: typeKey,
|
||||
);
|
||||
|
|
@ -241,3 +239,14 @@ class User extends Event {
|
|||
const _maximumHashLength = 10000;
|
||||
String _hash(String s) =>
|
||||
(s.codeUnits.fold<int>(0, (a, b) => a + b) % _maximumHashLength).toString();
|
||||
|
||||
extension FromStrippedStateEventExtension on StrippedStateEvent {
|
||||
User asUser(Room room) => User.fromState(
|
||||
// state key should always be set for member events
|
||||
stateKey: stateKey!,
|
||||
content: content,
|
||||
typeKey: type,
|
||||
senderId: senderId,
|
||||
room: room,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
*/
|
||||
|
||||
import 'package:matrix/matrix_api_lite.dart';
|
||||
import 'package:matrix/src/event.dart';
|
||||
|
||||
class SpaceChild {
|
||||
final String? roomId;
|
||||
|
|
@ -25,7 +24,7 @@ class SpaceChild {
|
|||
final String order;
|
||||
final bool? suggested;
|
||||
|
||||
SpaceChild.fromState(Event state)
|
||||
SpaceChild.fromState(StrippedStateEvent state)
|
||||
: assert(state.type == EventTypes.SpaceChild),
|
||||
roomId = state.stateKey,
|
||||
via = state.content.tryGetList<String>('via') ?? [],
|
||||
|
|
@ -38,7 +37,7 @@ class SpaceParent {
|
|||
final List<String> via;
|
||||
final bool? canonical;
|
||||
|
||||
SpaceParent.fromState(Event state)
|
||||
SpaceParent.fromState(StrippedStateEvent state)
|
||||
: assert(state.type == EventTypes.SpaceParent),
|
||||
roomId = state.stateKey,
|
||||
via = state.content.tryGetList<String>('via') ?? [],
|
||||
|
|
|
|||
|
|
@ -89,8 +89,12 @@ class VoIP {
|
|||
|
||||
// handles the com.famedly.call events.
|
||||
client.onRoomState.stream.listen(
|
||||
(event) async {
|
||||
if (event.type == EventTypes.GroupCallMember) {
|
||||
(update) async {
|
||||
final event = update.state;
|
||||
if (event is! Event) return;
|
||||
if (event.room.membership != Membership.join) return;
|
||||
if (event.type != EventTypes.GroupCallMember) return;
|
||||
|
||||
Logs().v('[VOIP] onRoomState: type ${event.toJson()}');
|
||||
final mems = event.room.getCallMembershipsFromEvent(event);
|
||||
for (final mem in mems) {
|
||||
|
|
@ -103,7 +107,6 @@ class VoIP {
|
|||
await map.value.onMemberStateChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue