Skip to content

Commit e3ffefe

Browse files
authored
feat: missed calls (#694)
* [PBE-1710] make example app Firebase friendly * [PBE-1710] support reject.reason & show notification * fix ios, improve setup * support pronto-staging * write openapi script + generate new API
1 parent 45736c3 commit e3ffefe

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1728
-89
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/bin/bash
2+
set -euo pipefail
3+
4+
OPENAPI_SPEC_PATH="https://raw.githubusercontent.com/GetStream/protocol/main/openapi/video-openapi-clientside.yaml"
5+
6+
OUTPUT_DIR="./lib/open_api/video/coordinator"
7+
TEMP_OUTPUT_DIR="./lib/open_api/video/coordinator_temp"
8+
9+
# Clean previous output
10+
rm -rf $TEMP_OUTPUT_DIR
11+
rm -rf $OUTPUT_DIR
12+
13+
# NOTE: https://openapi-generator.tech/docs/generators/dart/
14+
# Generate the Coordinator API models
15+
docker pull openapitools/openapi-generator-cli
16+
docker run --rm \
17+
-v "${TEMP_OUTPUT_DIR}:/local" openapitools/openapi-generator-cli generate \
18+
-i "$OPENAPI_SPEC_PATH" \
19+
-g dart \
20+
-o /local
21+
22+
23+
# Copy the generated code to the correct location
24+
cp -r $TEMP_OUTPUT_DIR/lib $OUTPUT_DIR
25+
26+
# Write the API version to a file
27+
README_FILE="$TEMP_OUTPUT_DIR/README.md"
28+
API_VERSION=$(grep -i "API version" "$README_FILE" | awk -F ': ' '{print $2}')
29+
OUTPUT_FILE="$OUTPUT_DIR/.api_version"
30+
echo "$API_VERSION" > "$OUTPUT_FILE"
31+
32+
# Remove the generated API client, just keep the models
33+
rm -rf $TEMP_OUTPUT_DIR
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/bin/bash
2+
set -euo pipefail
3+
4+
OPENAPI_SPEC_PATH="https://raw.githubusercontent.com/GetStream/protocol/main/openapi/video-openapi-clientside.yaml"
5+
6+
OUTPUT_DIR="./lib/open_api/video/coordinator"
7+
TEMP_OUTPUT_DIR="./lib/open_api/video/coordinator_temp"
8+
9+
# Clean previous output
10+
rm -rf $TEMP_OUTPUT_DIR
11+
rm -rf $OUTPUT_DIR
12+
13+
# NOTE: https://openapi-generator.tech/docs/generators/dart/
14+
# Generate the Coordinator API models
15+
docker pull ghcr.io/getstream/openapi-generator:master
16+
docker run --rm \
17+
-v "${TEMP_OUTPUT_DIR}:/local" ghcr.io/getstream/openapi-generator:master generate \
18+
-i "$OPENAPI_SPEC_PATH" \
19+
-g dart \
20+
-o /local
21+
22+
23+
# Copy the generated code to the correct location
24+
cp -r $TEMP_OUTPUT_DIR/lib $OUTPUT_DIR
25+
26+
# Write the API version to a file
27+
README_FILE="$TEMP_OUTPUT_DIR/README.md"
28+
API_VERSION=$(grep -i "API version" "$README_FILE" | awk -F ': ' '{print $2}')
29+
OUTPUT_FILE="$OUTPUT_DIR/.api_version"
30+
echo "$API_VERSION" > "$OUTPUT_FILE"
31+
32+
# Remove the generated API client, just keep the models
33+
rm -rf $TEMP_OUTPUT_DIR
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
v116.0.2

packages/stream_video/lib/open_api/video/coordinator/api.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ part 'model/call_member_added_event.dart';
5757
part 'model/call_member_removed_event.dart';
5858
part 'model/call_member_updated_event.dart';
5959
part 'model/call_member_updated_permission_event.dart';
60+
part 'model/call_missed_event.dart';
6061
part 'model/call_notification_event.dart';
6162
part 'model/call_participant_response.dart';
6263
part 'model/call_reaction_event.dart';
@@ -128,6 +129,8 @@ part 'model/ice_server.dart';
128129
part 'model/join_call_request.dart';
129130
part 'model/join_call_response.dart';
130131
part 'model/label_thresholds.dart';
132+
part 'model/limits_settings_request.dart';
133+
part 'model/limits_settings_response.dart';
131134
part 'model/list_devices_response.dart';
132135
part 'model/list_recordings_response.dart';
133136
part 'model/list_transcriptions_response.dart';
@@ -162,6 +165,7 @@ part 'model/reaction_response.dart';
162165
part 'model/read_receipts.dart';
163166
part 'model/record_settings_request.dart';
164167
part 'model/record_settings_response.dart';
168+
part 'model/reject_call_request.dart';
165169
part 'model/reject_call_response.dart';
166170
part 'model/request_permission_request.dart';
167171
part 'model/request_permission_response.dart';

packages/stream_video/lib/open_api/video/coordinator/api/productvideo_api.dart

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1455,20 +1455,22 @@ class ProductvideoApi {
14551455
/// * [String] type (required):
14561456
///
14571457
/// * [String] id (required):
1458-
Future<Response> rejectCallWithHttpInfo(String type, String id,) async {
1458+
///
1459+
/// * [RejectCallRequest] rejectCallRequest (required):
1460+
Future<Response> rejectCallWithHttpInfo(String type, String id, RejectCallRequest rejectCallRequest,) async {
14591461
// ignore: prefer_const_declarations
14601462
final path = r'/video/call/{type}/{id}/reject'
14611463
.replaceAll('{type}', type)
14621464
.replaceAll('{id}', id);
14631465

14641466
// ignore: prefer_final_locals
1465-
Object? postBody;
1467+
Object? postBody = rejectCallRequest;
14661468

14671469
final queryParams = <QueryParam>[];
14681470
final headerParams = <String, String>{};
14691471
final formParams = <String, String>{};
14701472

1471-
const contentTypes = <String>[];
1473+
const contentTypes = <String>['application/json'];
14721474

14731475

14741476
return apiClient.invokeAPI(
@@ -1491,8 +1493,10 @@ class ProductvideoApi {
14911493
/// * [String] type (required):
14921494
///
14931495
/// * [String] id (required):
1494-
Future<RejectCallResponse?> rejectCall(String type, String id,) async {
1495-
final response = await rejectCallWithHttpInfo(type, id,);
1496+
///
1497+
/// * [RejectCallRequest] rejectCallRequest (required):
1498+
Future<RejectCallResponse?> rejectCall(String type, String id, RejectCallRequest rejectCallRequest,) async {
1499+
final response = await rejectCallWithHttpInfo(type, id, rejectCallRequest,);
14961500
if (response.statusCode >= HttpStatus.badRequest) {
14971501
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
14981502
}

packages/stream_video/lib/open_api/video/coordinator/api_client.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,8 @@ class ApiClient {
236236
return CallMemberUpdatedEvent.fromJson(value);
237237
case 'CallMemberUpdatedPermissionEvent':
238238
return CallMemberUpdatedPermissionEvent.fromJson(value);
239+
case 'CallMissedEvent':
240+
return CallMissedEvent.fromJson(value);
239241
case 'CallNotificationEvent':
240242
return CallNotificationEvent.fromJson(value);
241243
case 'CallParticipantResponse':
@@ -378,6 +380,10 @@ class ApiClient {
378380
return JoinCallResponse.fromJson(value);
379381
case 'LabelThresholds':
380382
return LabelThresholds.fromJson(value);
383+
case 'LimitsSettingsRequest':
384+
return LimitsSettingsRequest.fromJson(value);
385+
case 'LimitsSettingsResponse':
386+
return LimitsSettingsResponse.fromJson(value);
381387
case 'ListDevicesResponse':
382388
return ListDevicesResponse.fromJson(value);
383389
case 'ListRecordingsResponse':
@@ -446,6 +452,8 @@ class ApiClient {
446452
return RecordSettingsRequest.fromJson(value);
447453
case 'RecordSettingsResponse':
448454
return RecordSettingsResponse.fromJson(value);
455+
case 'RejectCallRequest':
456+
return RejectCallRequest.fromJson(value);
449457
case 'RejectCallResponse':
450458
return RejectCallResponse.fromJson(value);
451459
case 'RequestPermissionRequest':
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
//
2+
// AUTO-GENERATED FILE, DO NOT MODIFY!
3+
//
4+
// @dart=2.18
5+
6+
// ignore_for_file: unused_element, unused_import
7+
// ignore_for_file: always_put_required_named_parameters_first
8+
// ignore_for_file: constant_identifier_names
9+
// ignore_for_file: lines_longer_than_80_chars
10+
11+
part of openapi.api;
12+
13+
class CallMissedEvent {
14+
/// Returns a new [CallMissedEvent] instance.
15+
CallMissedEvent({
16+
required this.call,
17+
required this.callCid,
18+
required this.createdAt,
19+
this.members = const [],
20+
required this.sessionId,
21+
this.type = 'call.missed',
22+
required this.user,
23+
});
24+
25+
CallResponse call;
26+
27+
String callCid;
28+
29+
DateTime createdAt;
30+
31+
/// List of members who missed the call
32+
List<MemberResponse> members;
33+
34+
/// Call session ID
35+
String sessionId;
36+
37+
/// The type of event: \"call.notification\" in this case
38+
String type;
39+
40+
UserResponse user;
41+
42+
@override
43+
bool operator ==(Object other) => identical(this, other) || other is CallMissedEvent &&
44+
other.call == call &&
45+
other.callCid == callCid &&
46+
other.createdAt == createdAt &&
47+
_deepEquality.equals(other.members, members) &&
48+
other.sessionId == sessionId &&
49+
other.type == type &&
50+
other.user == user;
51+
52+
@override
53+
int get hashCode =>
54+
// ignore: unnecessary_parenthesis
55+
(call.hashCode) +
56+
(callCid.hashCode) +
57+
(createdAt.hashCode) +
58+
(members.hashCode) +
59+
(sessionId.hashCode) +
60+
(type.hashCode) +
61+
(user.hashCode);
62+
63+
@override
64+
String toString() => 'CallMissedEvent[call=$call, callCid=$callCid, createdAt=$createdAt, members=$members, sessionId=$sessionId, type=$type, user=$user]';
65+
66+
Map<String, dynamic> toJson() {
67+
final json = <String, dynamic>{};
68+
json[r'call'] = this.call;
69+
json[r'call_cid'] = this.callCid;
70+
json[r'created_at'] = this.createdAt.toUtc().toIso8601String();
71+
json[r'members'] = this.members;
72+
json[r'session_id'] = this.sessionId;
73+
json[r'type'] = this.type;
74+
json[r'user'] = this.user;
75+
return json;
76+
}
77+
78+
/// Returns a new [CallMissedEvent] instance and imports its values from
79+
/// [value] if it's a [Map], null otherwise.
80+
// ignore: prefer_constructors_over_static_methods
81+
static CallMissedEvent? fromJson(dynamic value) {
82+
if (value is Map) {
83+
final json = value.cast<String, dynamic>();
84+
85+
// Ensure that the map contains the required keys.
86+
// Note 1: the values aren't checked for validity beyond being non-null.
87+
// Note 2: this code is stripped in release mode!
88+
assert(() {
89+
requiredKeys.forEach((key) {
90+
assert(json.containsKey(key), 'Required key "CallMissedEvent[$key]" is missing from JSON.');
91+
assert(json[key] != null, 'Required key "CallMissedEvent[$key]" has a null value in JSON.');
92+
});
93+
return true;
94+
}());
95+
96+
return CallMissedEvent(
97+
call: CallResponse.fromJson(json[r'call'])!,
98+
callCid: mapValueOfType<String>(json, r'call_cid')!,
99+
createdAt: mapDateTime(json, r'created_at', r'')!,
100+
members: MemberResponse.listFromJson(json[r'members']),
101+
sessionId: mapValueOfType<String>(json, r'session_id')!,
102+
type: mapValueOfType<String>(json, r'type')!,
103+
user: UserResponse.fromJson(json[r'user'])!,
104+
);
105+
}
106+
return null;
107+
}
108+
109+
static List<CallMissedEvent> listFromJson(dynamic json, {bool growable = false,}) {
110+
final result = <CallMissedEvent>[];
111+
if (json is List && json.isNotEmpty) {
112+
for (final row in json) {
113+
final value = CallMissedEvent.fromJson(row);
114+
if (value != null) {
115+
result.add(value);
116+
}
117+
}
118+
}
119+
return result.toList(growable: growable);
120+
}
121+
122+
static Map<String, CallMissedEvent> mapFromJson(dynamic json) {
123+
final map = <String, CallMissedEvent>{};
124+
if (json is Map && json.isNotEmpty) {
125+
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
126+
for (final entry in json.entries) {
127+
final value = CallMissedEvent.fromJson(entry.value);
128+
if (value != null) {
129+
map[entry.key] = value;
130+
}
131+
}
132+
}
133+
return map;
134+
}
135+
136+
// maps a json object with a list of CallMissedEvent-objects as value to a dart map
137+
static Map<String, List<CallMissedEvent>> mapListFromJson(dynamic json, {bool growable = false,}) {
138+
final map = <String, List<CallMissedEvent>>{};
139+
if (json is Map && json.isNotEmpty) {
140+
// ignore: parameter_assignments
141+
json = json.cast<String, dynamic>();
142+
for (final entry in json.entries) {
143+
map[entry.key] = CallMissedEvent.listFromJson(entry.value, growable: growable,);
144+
}
145+
}
146+
return map;
147+
}
148+
149+
/// The list of required keys that must be present in a JSON.
150+
static const requiredKeys = <String>{
151+
'call',
152+
'call_cid',
153+
'created_at',
154+
'members',
155+
'session_id',
156+
'type',
157+
'user',
158+
};
159+
}
160+

packages/stream_video/lib/open_api/video/coordinator/model/call_rejected_event.dart

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ class CallRejectedEvent {
1616
required this.call,
1717
required this.callCid,
1818
required this.createdAt,
19+
this.reason,
1920
this.type = 'call.rejected',
2021
required this.user,
2122
});
@@ -26,6 +27,14 @@ class CallRejectedEvent {
2627

2728
DateTime createdAt;
2829

30+
///
31+
/// Please note: This property should have been non-nullable! Since the specification file
32+
/// does not include a default value (using the "default:" property), however, the generated
33+
/// source code must fall back to having a nullable type.
34+
/// Consider adding a "default:" property in the specification file to hide this note.
35+
///
36+
String? reason;
37+
2938
/// The type of event: \"call.rejected\" in this case
3039
String type;
3140

@@ -36,6 +45,7 @@ class CallRejectedEvent {
3645
other.call == call &&
3746
other.callCid == callCid &&
3847
other.createdAt == createdAt &&
48+
other.reason == reason &&
3949
other.type == type &&
4050
other.user == user;
4151

@@ -45,17 +55,23 @@ class CallRejectedEvent {
4555
(call.hashCode) +
4656
(callCid.hashCode) +
4757
(createdAt.hashCode) +
58+
(reason == null ? 0 : reason!.hashCode) +
4859
(type.hashCode) +
4960
(user.hashCode);
5061

5162
@override
52-
String toString() => 'CallRejectedEvent[call=$call, callCid=$callCid, createdAt=$createdAt, type=$type, user=$user]';
63+
String toString() => 'CallRejectedEvent[call=$call, callCid=$callCid, createdAt=$createdAt, reason=$reason, type=$type, user=$user]';
5364

5465
Map<String, dynamic> toJson() {
5566
final json = <String, dynamic>{};
5667
json[r'call'] = this.call;
5768
json[r'call_cid'] = this.callCid;
5869
json[r'created_at'] = this.createdAt.toUtc().toIso8601String();
70+
if (this.reason != null) {
71+
json[r'reason'] = this.reason;
72+
} else {
73+
json[r'reason'] = null;
74+
}
5975
json[r'type'] = this.type;
6076
json[r'user'] = this.user;
6177
return json;
@@ -83,6 +99,7 @@ class CallRejectedEvent {
8399
call: CallResponse.fromJson(json[r'call'])!,
84100
callCid: mapValueOfType<String>(json, r'call_cid')!,
85101
createdAt: mapDateTime(json, r'created_at', r'')!,
102+
reason: mapValueOfType<String>(json, r'reason'),
86103
type: mapValueOfType<String>(json, r'type')!,
87104
user: UserResponse.fromJson(json[r'user'])!,
88105
);

0 commit comments

Comments
 (0)