Line data Source code
1 : import 'package:matrix/matrix.dart';
2 :
3 : class FamedlyCallMemberEvent {
4 : final List<CallMembership> memberships;
5 :
6 4 : FamedlyCallMemberEvent({required this.memberships});
7 :
8 0 : Map<String, dynamic> toJson() {
9 0 : return {'memberships': memberships.map((e) => e.toJson()).toList()};
10 : }
11 :
12 4 : factory FamedlyCallMemberEvent.fromJson(Event event, VoIP voip) {
13 4 : final List<CallMembership> callMemberships = [];
14 8 : final memberships = event.content.tryGetList('memberships');
15 4 : if (memberships != null && memberships.isNotEmpty) {
16 8 : for (final mem in memberships) {
17 4 : if (isValidMemEvent(mem)) {
18 4 : final callMem = CallMembership.fromJson(
19 : mem,
20 4 : event.senderId,
21 8 : event.room.id,
22 4 : event.eventId,
23 : voip,
24 : );
25 4 : if (callMem != null) callMemberships.add(callMem);
26 : }
27 : }
28 : }
29 4 : 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 4 : 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 4 : Map<String, dynamic> toJson() {
88 4 : return {
89 8 : 'call_id': callId,
90 8 : 'application': application,
91 8 : 'scope': scope,
92 16 : 'foci_active': [backend.toJson()],
93 8 : 'device_id': deviceId,
94 8 : 'expires_ts': expiresTs,
95 8 : 'membershipID': membershipId, // sessionId
96 8 : if (feeds != null) 'feeds': feeds,
97 : };
98 : }
99 :
100 4 : static CallMembership? fromJson(
101 : Map json,
102 : String userId,
103 : String roomId,
104 : String? eventId,
105 : VoIP voip,
106 : ) {
107 : try {
108 4 : return CallMembership(
109 : userId: userId,
110 : roomId: roomId,
111 4 : callId: json['call_id'],
112 4 : application: json['application'],
113 4 : scope: json['scope'],
114 4 : backend: (json['foci_active'] as List)
115 12 : .map((e) => CallBackend.fromJson(e))
116 4 : .first,
117 4 : deviceId: json['device_id'],
118 : eventId: eventId,
119 4 : expiresTs: json['expires_ts'],
120 : membershipId:
121 4 : json['membershipID'] ?? 'someone_forgot_to_set_the_membershipID',
122 4 : feeds: json['feeds'],
123 : voip: voip,
124 : );
125 : } catch (e, s) {
126 0 : Logs().e('[VOIP] call membership parsing failed. $json', e, s);
127 : return null;
128 : }
129 : }
130 :
131 0 : @override
132 : bool operator ==(other) =>
133 : identical(this, other) ||
134 0 : other is CallMembership &&
135 0 : runtimeType == other.runtimeType &&
136 0 : userId == other.userId &&
137 0 : roomId == other.roomId &&
138 0 : callId == other.callId &&
139 0 : application == other.application &&
140 0 : scope == other.scope &&
141 0 : backend.type == other.backend.type &&
142 0 : deviceId == other.deviceId &&
143 0 : eventId == other.eventId &&
144 0 : membershipId == other.membershipId;
145 :
146 0 : @override
147 0 : int get hashCode => Object.hash(
148 0 : userId.hashCode,
149 0 : roomId.hashCode,
150 0 : callId.hashCode,
151 0 : application.hashCode,
152 0 : scope.hashCode,
153 0 : backend.type.hashCode,
154 0 : deviceId.hashCode,
155 0 : eventId.hashCode,
156 0 : membershipId.hashCode,
157 : );
158 :
159 : // with a buffer of 1 minute just incase we were slow to process a
160 : // call event, if the device is actually dead it should
161 : // get removed pretty soon
162 4 : bool get isExpired =>
163 8 : expiresTs <
164 4 : DateTime.now()
165 16 : .subtract(voip.timeouts!.expireTsBumpDuration)
166 4 : .millisecondsSinceEpoch;
167 :
168 0 : @override
169 : String toString() {
170 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)';
171 : }
172 : }
|