Merge pull request #1786 from famedly/use-stripped-state-for-room-state
refactor: Use strippedstatevent as base for room state and user class
This commit is contained in:
commit
587ce378c7
|
|
@ -1312,7 +1312,10 @@ class Client extends MatrixApi {
|
||||||
|
|
||||||
final CachedStreamController<Event> onGroupMember = CachedStreamController();
|
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
|
/// How long should the app wait until it retrys the synchronisation after
|
||||||
/// an error?
|
/// an error?
|
||||||
|
|
@ -2385,7 +2388,7 @@ class Client extends MatrixApi {
|
||||||
|
|
||||||
switch (eventUpdate.type) {
|
switch (eventUpdate.type) {
|
||||||
case EventUpdateType.inviteState:
|
case EventUpdateType.inviteState:
|
||||||
room.setState(Event.fromJson(eventUpdate.content, room));
|
room.setState(StrippedStateEvent.fromJson(eventUpdate.content));
|
||||||
break;
|
break;
|
||||||
case EventUpdateType.state:
|
case EventUpdateType.state:
|
||||||
case EventUpdateType.timeline:
|
case EventUpdateType.timeline:
|
||||||
|
|
|
||||||
|
|
@ -247,10 +247,7 @@ class Event extends MatrixEvent {
|
||||||
prevContent: prevContent,
|
prevContent: prevContent,
|
||||||
content: content,
|
content: content,
|
||||||
typeKey: type,
|
typeKey: type,
|
||||||
eventId: eventId,
|
|
||||||
senderId: senderId,
|
senderId: senderId,
|
||||||
originServerTs: originServerTs,
|
|
||||||
unsigned: unsigned,
|
|
||||||
room: room,
|
room: room,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ class Room {
|
||||||
/// The room states are a key value store of the key (`type`,`state_key`) => State(event).
|
/// 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
|
/// 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.
|
/// 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.
|
/// Key-Value store for ephemerals.
|
||||||
Map<String, BasicRoomEvent> ephemerals = {};
|
Map<String, BasicRoomEvent> ephemerals = {};
|
||||||
|
|
@ -157,19 +157,31 @@ class Room {
|
||||||
|
|
||||||
/// Returns the [Event] for the given [typeKey] and optional [stateKey].
|
/// Returns the [Event] for the given [typeKey] and optional [stateKey].
|
||||||
/// If no [stateKey] is provided, it defaults to an empty string.
|
/// 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];
|
states[typeKey]?[stateKey];
|
||||||
|
|
||||||
/// Adds the [state] to this room and overwrites a state with the same
|
/// Adds the [state] to this room and overwrites a state with the same
|
||||||
/// typeKey/stateKey key pair if there is one.
|
/// typeKey/stateKey key pair if there is one.
|
||||||
void setState(Event state) {
|
void setState(StrippedStateEvent state) {
|
||||||
// Ignore other non-state events
|
// Ignore other non-state events
|
||||||
final stateKey = state.stateKey;
|
final stateKey = state.stateKey;
|
||||||
final roomId = state.roomId;
|
|
||||||
if (roomId == null || roomId != id) {
|
// For non invite rooms this is usually an Event and we should validate
|
||||||
Logs().w('Tried to set state event for wrong room!');
|
// the room ID:
|
||||||
return;
|
if (state is Event) {
|
||||||
|
final roomId = state.roomId;
|
||||||
|
if (roomId == null || roomId != id) {
|
||||||
|
Logs().wtf('Tried to set state event for wrong room!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stateKey == null) {
|
if (stateKey == null) {
|
||||||
Logs().w(
|
Logs().w(
|
||||||
'Tried to set a non state event with type "${state.type}" as state event for a room',
|
'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;
|
(states[state.type] ??= {})[stateKey] = state;
|
||||||
|
|
||||||
client.onRoomState.add(state);
|
client.onRoomState.add((roomId: id, state: state));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ID of the fully read marker event.
|
/// ID of the fully read marker event.
|
||||||
|
|
@ -276,9 +288,11 @@ class Room {
|
||||||
if (membership == Membership.invite) {
|
if (membership == Membership.invite) {
|
||||||
final ownMember = unsafeGetUserFromMemoryOrFallback(client.userID!);
|
final ownMember = unsafeGetUserFromMemoryOrFallback(client.userID!);
|
||||||
|
|
||||||
ownMember.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n);
|
unsafeGetUserFromMemoryOrFallback(ownMember.senderId)
|
||||||
|
.calcDisplayname(i18n: i18n);
|
||||||
if (ownMember.senderId != ownMember.stateKey) {
|
if (ownMember.senderId != ownMember.stateKey) {
|
||||||
return ownMember.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n);
|
return unsafeGetUserFromMemoryOrFallback(ownMember.senderId)
|
||||||
|
.calcDisplayname(i18n: i18n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (membership == Membership.leave) {
|
if (membership == Membership.leave) {
|
||||||
|
|
@ -312,7 +326,7 @@ class Room {
|
||||||
if (heroes != null && heroes.length == 1) {
|
if (heroes != null && heroes.length == 1) {
|
||||||
final hero = getState(EventTypes.RoomMember, heroes.first);
|
final hero = getState(EventTypes.RoomMember, heroes.first);
|
||||||
if (hero != null) {
|
if (hero != null) {
|
||||||
return hero.asUser.avatarUrl;
|
return hero.asUser(this).avatarUrl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isDirectChat) {
|
if (isDirectChat) {
|
||||||
|
|
@ -383,6 +397,7 @@ class Room {
|
||||||
states.forEach((final String key, final entry) {
|
states.forEach((final String key, final entry) {
|
||||||
final state = entry[''];
|
final state = entry[''];
|
||||||
if (state == null) return;
|
if (state == null) return;
|
||||||
|
if (state is! Event) return;
|
||||||
if (state.originServerTs.millisecondsSinceEpoch >
|
if (state.originServerTs.millisecondsSinceEpoch >
|
||||||
lastTime.millisecondsSinceEpoch) {
|
lastTime.millisecondsSinceEpoch) {
|
||||||
lastTime = state.originServerTs;
|
lastTime = state.originServerTs;
|
||||||
|
|
@ -1554,7 +1569,7 @@ class Room {
|
||||||
if (members != null) {
|
if (members != null) {
|
||||||
return members.entries
|
return members.entries
|
||||||
.where((entry) => entry.value.type == EventTypes.RoomMember)
|
.where((entry) => entry.value.type == EventTypes.RoomMember)
|
||||||
.map((entry) => entry.value.asUser)
|
.map((entry) => entry.value.asUser(this))
|
||||||
.where((user) => membershipFilter.contains(user.membership))
|
.where((user) => membershipFilter.contains(user.membership))
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
@ -1643,7 +1658,7 @@ class Room {
|
||||||
User unsafeGetUserFromMemoryOrFallback(String mxID) {
|
User unsafeGetUserFromMemoryOrFallback(String mxID) {
|
||||||
final user = getState(EventTypes.RoomMember, mxID);
|
final user = getState(EventTypes.RoomMember, mxID);
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
return user.asUser;
|
return user.asUser(this);
|
||||||
} else {
|
} else {
|
||||||
if (mxID.isValidMatrixId) {
|
if (mxID.isValidMatrixId) {
|
||||||
// ignore: discarded_futures
|
// ignore: discarded_futures
|
||||||
|
|
@ -1672,7 +1687,7 @@ class Room {
|
||||||
// Checks if the user is really missing
|
// Checks if the user is really missing
|
||||||
final stateUser = getState(EventTypes.RoomMember, mxID);
|
final stateUser = getState(EventTypes.RoomMember, mxID);
|
||||||
if (stateUser != null) {
|
if (stateUser != null) {
|
||||||
return stateUser.asUser;
|
return stateUser.asUser(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// it may be in the database
|
// it may be in the database
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,10 @@
|
||||||
import 'package:matrix/matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
|
|
||||||
/// Represents a Matrix User which may be a participant in a Matrix Room.
|
/// 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(
|
factory User(
|
||||||
String id, {
|
String id, {
|
||||||
String? membership,
|
String? membership,
|
||||||
|
|
@ -30,7 +33,6 @@ class User extends Event {
|
||||||
return User.fromState(
|
return User.fromState(
|
||||||
stateKey: id,
|
stateKey: id,
|
||||||
senderId: id,
|
senderId: id,
|
||||||
eventId: 'fake_event',
|
|
||||||
content: {
|
content: {
|
||||||
if (membership != null) 'membership': membership,
|
if (membership != null) 'membership': membership,
|
||||||
if (displayName != null) 'displayname': displayName,
|
if (displayName != null) 'displayname': displayName,
|
||||||
|
|
@ -38,20 +40,16 @@ class User extends Event {
|
||||||
},
|
},
|
||||||
typeKey: EventTypes.RoomMember,
|
typeKey: EventTypes.RoomMember,
|
||||||
room: room,
|
room: room,
|
||||||
originServerTs: DateTime.now(),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
User.fromState({
|
User.fromState({
|
||||||
super.prevContent,
|
|
||||||
required String super.stateKey,
|
required String super.stateKey,
|
||||||
super.content = const {},
|
super.content = const {},
|
||||||
required String typeKey,
|
required String typeKey,
|
||||||
required super.eventId,
|
|
||||||
required super.senderId,
|
required super.senderId,
|
||||||
required super.originServerTs,
|
required this.room,
|
||||||
super.unsigned,
|
this.prevContent,
|
||||||
required super.room,
|
|
||||||
}) : super(
|
}) : super(
|
||||||
type: typeKey,
|
type: typeKey,
|
||||||
);
|
);
|
||||||
|
|
@ -241,3 +239,14 @@ class User extends Event {
|
||||||
const _maximumHashLength = 10000;
|
const _maximumHashLength = 10000;
|
||||||
String _hash(String s) =>
|
String _hash(String s) =>
|
||||||
(s.codeUnits.fold<int>(0, (a, b) => a + b) % _maximumHashLength).toString();
|
(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/matrix_api_lite.dart';
|
||||||
import 'package:matrix/src/event.dart';
|
|
||||||
|
|
||||||
class SpaceChild {
|
class SpaceChild {
|
||||||
final String? roomId;
|
final String? roomId;
|
||||||
|
|
@ -25,7 +24,7 @@ class SpaceChild {
|
||||||
final String order;
|
final String order;
|
||||||
final bool? suggested;
|
final bool? suggested;
|
||||||
|
|
||||||
SpaceChild.fromState(Event state)
|
SpaceChild.fromState(StrippedStateEvent state)
|
||||||
: assert(state.type == EventTypes.SpaceChild),
|
: assert(state.type == EventTypes.SpaceChild),
|
||||||
roomId = state.stateKey,
|
roomId = state.stateKey,
|
||||||
via = state.content.tryGetList<String>('via') ?? [],
|
via = state.content.tryGetList<String>('via') ?? [],
|
||||||
|
|
@ -38,7 +37,7 @@ class SpaceParent {
|
||||||
final List<String> via;
|
final List<String> via;
|
||||||
final bool? canonical;
|
final bool? canonical;
|
||||||
|
|
||||||
SpaceParent.fromState(Event state)
|
SpaceParent.fromState(StrippedStateEvent state)
|
||||||
: assert(state.type == EventTypes.SpaceParent),
|
: assert(state.type == EventTypes.SpaceParent),
|
||||||
roomId = state.stateKey,
|
roomId = state.stateKey,
|
||||||
via = state.content.tryGetList<String>('via') ?? [],
|
via = state.content.tryGetList<String>('via') ?? [],
|
||||||
|
|
|
||||||
|
|
@ -89,19 +89,22 @@ class VoIP {
|
||||||
|
|
||||||
// handles the com.famedly.call events.
|
// handles the com.famedly.call events.
|
||||||
client.onRoomState.stream.listen(
|
client.onRoomState.stream.listen(
|
||||||
(event) async {
|
(update) async {
|
||||||
if (event.type == EventTypes.GroupCallMember) {
|
final event = update.state;
|
||||||
Logs().v('[VOIP] onRoomState: type ${event.toJson()}');
|
if (event is! Event) return;
|
||||||
final mems = event.room.getCallMembershipsFromEvent(event);
|
if (event.room.membership != Membership.join) return;
|
||||||
for (final mem in mems) {
|
if (event.type != EventTypes.GroupCallMember) return;
|
||||||
unawaited(createGroupCallFromRoomStateEvent(mem));
|
|
||||||
}
|
Logs().v('[VOIP] onRoomState: type ${event.toJson()}');
|
||||||
for (final map in groupCalls.entries) {
|
final mems = event.room.getCallMembershipsFromEvent(event);
|
||||||
if (map.key.roomId == event.room.id) {
|
for (final mem in mems) {
|
||||||
// because we don't know which call got updated, just update all
|
unawaited(createGroupCallFromRoomStateEvent(mem));
|
||||||
// group calls we have entered for that room
|
}
|
||||||
await map.value.onMemberStateChanged();
|
for (final map in groupCalls.entries) {
|
||||||
}
|
if (map.key.roomId == event.room.id) {
|
||||||
|
// because we don't know which call got updated, just update all
|
||||||
|
// group calls we have entered for that room
|
||||||
|
await map.value.onMemberStateChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue