Line data Source code
1 : import 'package:matrix/matrix.dart';
2 :
3 : class FamedlyCallMemberEvent {
4 : final List<CallMembership> memberships;
5 :
6 6 : FamedlyCallMemberEvent({required this.memberships});
7 :
8 0 : Map<String, dynamic> toJson() {
9 0 : return {'memberships': memberships.map((e) => e.toJson()).toList()};
10 : }
11 :
12 6 : factory FamedlyCallMemberEvent.fromJson(Event event, VoIP voip) {
13 6 : final List<CallMembership> callMemberships = [];
14 12 : final memberships = event.content.tryGetList('memberships');
15 6 : if (memberships != null && memberships.isNotEmpty) {
16 12 : for (final mem in memberships) {
17 6 : if (isValidMemEvent(mem)) {
18 6 : final callMem = CallMembership.fromJson(
19 : mem,
20 6 : event.senderId,
21 12 : event.room.id,
22 6 : event.eventId,
23 : voip,
24 : );
25 6 : if (callMem != null) callMemberships.add(callMem);
26 : }
27 : }
28 : }
29 6 : return FamedlyCallMemberEvent(memberships: callMemberships);
30 : }
31 : }
32 :
33 : /// userId - The userId of the member
34 : ///
35 : /// callId - The callId the member is a part of, usually an empty string
36 : ///
37 : /// application, scope - something you can narrow down the calls with, might be
38 : /// removed soon
39 : ///
40 : /// backend - The CallBackend, either mesh or livekit
41 : ///
42 : /// deviceId - The deviceId of the member
43 : ///
44 : /// eventId - The eventId in matrix for this call membership
45 : /// not always present because we do not have it when we are just sending
46 : /// the event
47 : ///
48 : /// expiresTs - Timestamp at which this membership event will be considered expired
49 : ///
50 : /// membershipId - A cachebuster for state events, usually is reset every time a client
51 : /// loads
52 : ///
53 : /// feeds - Feeds from mesh calls, is not used for livekit calls
54 : ///
55 : /// voip - The voip parent class for using timeouts probably
56 : ///
57 : /// roomId - The roomId for the call
58 : class CallMembership {
59 : final String userId;
60 : final String callId;
61 : final String? application;
62 : final String? scope;
63 : final CallBackend backend;
64 : final String deviceId;
65 : final String? eventId;
66 : final int expiresTs;
67 : final String membershipId;
68 : final List? feeds;
69 : final VoIP voip;
70 : final String roomId;
71 :
72 6 : CallMembership({
73 : required this.userId,
74 : required this.callId,
75 : required this.backend,
76 : required this.deviceId,
77 : this.eventId,
78 : required this.expiresTs,
79 : required this.roomId,
80 : required this.membershipId,
81 : required this.voip,
82 : this.application = 'm.call',
83 : this.scope = 'm.room',
84 : this.feeds,
85 : });
86 :
87 6 : Map<String, dynamic> toJson() {
88 6 : return {
89 12 : 'call_id': callId,
90 12 : 'application': application,
91 12 : 'scope': scope,
92 24 : 'foci_active': [backend.toJson()],
93 12 : 'device_id': deviceId,
94 12 : 'event_id': eventId,
95 12 : 'expires_ts': expiresTs,
96 6 : 'expires': 7200000, // element compatibiltiy remove asap
97 12 : 'membershipID': membershipId, // sessionId
98 14 : if (feeds != null) 'feeds': feeds,
99 : };
100 : }
101 :
102 6 : static CallMembership? fromJson(
103 : Map json,
104 : String userId,
105 : String roomId,
106 : String? eventId,
107 : VoIP voip,
108 : ) {
109 : try {
110 6 : return CallMembership(
111 : userId: userId,
112 : roomId: roomId,
113 6 : callId: json['call_id'],
114 6 : application: json['application'],
115 6 : scope: json['scope'],
116 6 : backend: (json['foci_active'] as List)
117 18 : .map((e) => CallBackend.fromJson(e))
118 6 : .first,
119 6 : deviceId: json['device_id'],
120 : eventId: eventId,
121 6 : expiresTs: json['expires_ts'],
122 : membershipId:
123 6 : json['membershipID'] ?? 'someone_forgot_to_set_the_membershipID',
124 6 : feeds: json['feeds'],
125 : voip: voip,
126 : );
127 : } catch (e, s) {
128 0 : Logs().e('[VOIP] call membership parsing failed. $json', e, s);
129 : return null;
130 : }
131 : }
132 :
133 0 : @override
134 : bool operator ==(other) =>
135 : identical(this, other) ||
136 0 : other is CallMembership &&
137 0 : runtimeType == other.runtimeType &&
138 0 : userId == other.userId &&
139 0 : roomId == other.roomId &&
140 0 : callId == other.callId &&
141 0 : application == other.application &&
142 0 : scope == other.scope &&
143 0 : backend.type == other.backend.type &&
144 0 : deviceId == other.deviceId &&
145 0 : eventId == other.eventId &&
146 0 : membershipId == other.membershipId;
147 :
148 0 : @override
149 0 : int get hashCode => Object.hash(
150 0 : userId.hashCode,
151 0 : roomId.hashCode,
152 0 : callId.hashCode,
153 0 : application.hashCode,
154 0 : scope.hashCode,
155 0 : backend.type.hashCode,
156 0 : deviceId.hashCode,
157 0 : eventId.hashCode,
158 0 : membershipId.hashCode,
159 : );
160 :
161 : // with a buffer of 1 minute just incase we were slow to process a
162 : // call event, if the device is actually dead it should
163 : // get removed pretty soon
164 6 : bool get isExpired =>
165 12 : expiresTs <
166 6 : DateTime.now()
167 24 : .subtract(voip.timeouts!.expireTsBumpDuration)
168 6 : .millisecondsSinceEpoch;
169 :
170 0 : @override
171 : String toString() {
172 0 : return 'CallMembership(userId: $userId, callId: $callId, application: $application, scope: $scope, backend: $backend, deviceId: $deviceId, eventId: $eventId, expiresTs: $expiresTs, membershipId: $membershipId, feeds: $feeds, voip: $voip, roomId: $roomId)';
173 : }
174 : }
|