Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 22 additions & 3 deletions dogfooding/lib/app/app_content.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import '../di/injector.dart';
import '../router/router.dart';
import '../router/routes.dart';
import '../theme/app_palette.dart';
import '../utils/call_lookup.dart';
import '../utils/consts.dart';
import 'custom_video_localizations.dart';
import 'firebase_messaging_handler.dart';
Expand Down Expand Up @@ -114,6 +115,7 @@ class _StreamDogFoodingAppContentState
call: call,
connectOptions: null,
effectsManager: null,
encryptionKey: null,
);

_router.push(CallRoute($extra: extra).location, extra: extra);
Expand All @@ -140,6 +142,7 @@ class _StreamDogFoodingAppContentState
call: callToJoin,
connectOptions: null,
effectsManager: null,
encryptionKey: null,
);

_router.push(CallRoute($extra: extra).location, extra: extra);
Expand All @@ -156,6 +159,7 @@ class _StreamDogFoodingAppContentState
call: call,
connectOptions: null,
effectsManager: null,
encryptionKey: null,
);

_router.push(CallRoute($extra: extra).location, extra: extra);
Expand Down Expand Up @@ -226,10 +230,25 @@ class _StreamDogFoodingAppContentState
try {
final streamVideo = locator.get<StreamVideo>();
final call = streamVideo.makeCall(callType: kCallType, id: callId);
final encryptionKey = uri.queryParameters['encryption_key'];

// A link can point at a call that was never created — the lobby creates
// it, with the encryption mode chosen there.
final lookup = await lookupCallExists(call);
if (lookup is Failure) {
debugPrint('Error looking up call $callId: ${lookup.error}');
return;
}

await call.getOrCreate();

await _router.push<void>(LobbyRoute($extra: call).location, extra: call);
final extra = (
call: call,
callExists: (lookup as Success<bool>).data,
encryptionKey: encryptionKey,
);
await _router.push<void>(
LobbyRoute($extra: extra).location,
extra: extra,
);
} catch (e, stk) {
debugPrint('Error joining or creating call: $e');
debugPrint(stk.toString());
Expand Down
35 changes: 23 additions & 12 deletions dogfooding/lib/core/model/environment.dart
Original file line number Diff line number Diff line change
Expand Up @@ -73,18 +73,29 @@ enum Environment {
/// Whether this is a Pronto environment.
bool get isPronto => envName == 'pronto';

String? getJoinUrl({required String callId, String? callType}) {
switch (this) {
case Environment.pronto:
case Environment.prontoStaging:
case Environment.staging:
return '${baseUrls.first}/join/$callId?type=${callType ?? 'default'}';
case Environment.demo:
return '${baseUrls.first}/video/demos/join/$callId?type=${callType ?? 'default'}';
case Environment.livestream:
return '${baseUrls.first}/?id=$callId&type=${callType ?? 'livestream'}';
case Environment.custom:
return null;
/// The URL that joins [callId] on this environment, or null when it has no
/// public join page.
///
/// [encryptionKey] is the shared passphrase, appended as `encryption_key`.
String? getJoinUrl({
required String callId,
String? callType,
String? encryptionKey,
}) {
final url = switch (this) {
Environment.pronto || Environment.prontoStaging || Environment.staging =>
'${baseUrls.first}/join/$callId?type=${callType ?? 'default'}',
Environment.demo =>
'${baseUrls.first}/video/demos/join/$callId?type=${callType ?? 'default'}',
Environment.livestream =>
'${baseUrls.first}/?id=$callId&type=${callType ?? 'livestream'}',
Environment.custom => null,
};

if (url == null || encryptionKey == null || encryptionKey.isEmpty) {
return url;
}

return '$url&encryption_key=${Uri.encodeQueryComponent(encryptionKey)}';
}
}
4 changes: 4 additions & 0 deletions dogfooding/lib/di/injector.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import '../core/repos/token_service.dart';
import '../core/repos/user_auth_repository.dart';
import '../core/repos/user_chat_repository.dart';
import '../log_config.dart';
import '../utils/ringing_encryption.dart';

GetIt locator = GetIt.instance;

Expand Down Expand Up @@ -164,6 +165,9 @@ StreamVideo _initStreamVideo(
logPriority: Priority.debug,
keepConnectionsAliveWhenInBackground: true,
audioProcessor: NoiseCancellationAudioProcessor(),
defaultCallPreferences: DefaultCallPreferences(
encryptionKeyResolver: resolveRingingEncryptionKey,
),
),
pushNotificationManagerProvider: StreamVideoPushNotificationManager.create(
iosPushProvider: const StreamVideoPushProvider.apn(name: 'flutter-apn'),
Expand Down
37 changes: 25 additions & 12 deletions dogfooding/lib/router/routes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,33 @@ class LoginRoute extends GoRouteData with $LoginRoute {
class LobbyRoute extends GoRouteData with $LobbyRoute {
const LobbyRoute({required this.$extra});

final Call $extra;
final ({Call call, bool callExists, String? encryptionKey}) $extra;

@override
Widget build(BuildContext context, GoRouterState state) {
return LobbyScreen(
call: $extra,
onJoinCallPressed: (connectOptions, effectsManager) {
// Navigate to the call screen.
CallRoute(
$extra: (
call: $extra,
connectOptions: connectOptions,
effectsManager: effectsManager,
),
).replace(context);
},
call: $extra.call,
callExists: $extra.callExists,
initialEncryptionKey: $extra.encryptionKey,
onJoinCallPressed:
({
required call,
required connectOptions,
required effectsManager,
encryptionKey,
}) {
// Navigate to the call screen.
CallRoute(
$extra: (
call: call,
connectOptions: connectOptions,
effectsManager: effectsManager,
// The passphrase the lobby settled on, so the call screen can
// put it in the invite it offers.
encryptionKey: encryptionKey,
),
).replace(context);
},
);
}
}
Expand All @@ -78,6 +89,7 @@ class CallRoute extends GoRouteData with $CallRoute {
Call call,
CallConnectOptions? connectOptions,
StreamVideoEffectsManager? effectsManager,
String? encryptionKey,
})
$extra;

Expand All @@ -87,6 +99,7 @@ class CallRoute extends GoRouteData with $CallRoute {
call: $extra.call,
connectOptions: $extra.connectOptions,
videoEffectsManager: $extra.effectsManager,
encryptionKey: $extra.encryptionKey,
);
}
}
Expand Down
7 changes: 5 additions & 2 deletions dogfooding/lib/router/routes.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 19 additions & 1 deletion dogfooding/lib/screens/call_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import '../utils/feedback_dialog.dart';
import '../widgets/badged_call_option.dart';
import '../widgets/call_duration_title.dart';
import '../widgets/closed_captions_widget.dart';
import '../widgets/e2ee_key_notification.dart';
import '../widgets/settings_menu/settings_menu.dart';
import '../widgets/share_call_card.dart';

Expand All @@ -32,18 +33,24 @@ class CallScreen extends StatefulWidget {
required this.call,
this.connectOptions,
this.videoEffectsManager,
this.encryptionKey,
});

final Call call;
final CallConnectOptions? connectOptions;
final StreamVideoEffectsManager? videoEffectsManager;

/// The passphrase [call]'s shared key was derived from.
final String? encryptionKey;

@override
State<CallScreen> createState() => _CallScreenState();
}

class _CallScreenState extends State<CallScreen> {
late final _userChatRepo = locator.get<UserChatRepository>();

late String? _encryptionKey = widget.encryptionKey;
late final _videoEffectsManager =
widget.videoEffectsManager ?? StreamVideoEffectsManager(widget.call);

Expand Down Expand Up @@ -201,6 +208,14 @@ class _CallScreenState extends State<CallScreen> {
ClosedCaptionsWidget(call: call),
],
),
Align(
alignment: Alignment.bottomCenter,
child: E2eeKeyNotification(
call: call,
onKeyApplied: (key) =>
setState(() => _encryptionKey = key),
),
),
if (_moreMenuVisible) ...[
GestureDetector(
onTap: () => setState(() => _moreMenuVisible = false),
Expand Down Expand Up @@ -244,7 +259,10 @@ class _CallScreenState extends State<CallScreen> {
call: call,
selector: (state) => state.otherParticipants.isEmpty,
builder: (context, isEmpty) => isEmpty
? ShareCallWelcomeCard(callId: call.id)
? ShareCallWelcomeCard(
call: call,
encryptionKey: _encryptionKey,
)
: const SizedBox.shrink(),
),
),
Expand Down
Loading
Loading