Skip to content

Commit c6343fa

Browse files
authored
feat: add option to scan livestream qr code (#980)
* feat: add option to scan livestream qr code * formatting
1 parent f82e7e3 commit c6343fa

File tree

7 files changed

+183
-3
lines changed

7 files changed

+183
-3
lines changed

dogfooding/lib/core/model/environment.dart

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ enum Environment {
2828
'pronto.getstream.io',
2929
baseUrls: ['https://staging.getstream.io'],
3030
),
31+
livestream(
32+
'Livestream',
33+
'demo',
34+
'pronto.getstream.io',
35+
baseUrls: ['https://livestream-react-demo.vercel.app'],
36+
),
3137
custom(
3238
'Custom',
3339
'custom',
@@ -68,14 +74,16 @@ enum Environment {
6874
);
6975
}
7076

71-
String? getJoinUrl({required String callId, String callType = 'default'}) {
77+
String? getJoinUrl({required String callId, String? callType}) {
7278
switch (this) {
7379
case Environment.pronto:
7480
case Environment.prontoStaging:
7581
case Environment.staging:
76-
return '${baseUrls.first}/join/$callId?type=$callType';
82+
return '${baseUrls.first}/join/$callId?type=${callType ?? 'default'}';
7783
case Environment.demo:
78-
return '${baseUrls.first}/video/demos/join/$callId?type=$callType';
84+
return '${baseUrls.first}/video/demos/join/$callId?type=${callType ?? 'default'}';
85+
case Environment.livestream:
86+
return '${baseUrls.first}/?id=$callId&type=${callType ?? 'livestream'}';
7987
case Environment.custom:
8088
return null;
8189
}

dogfooding/lib/router/router.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ GoRouter initRouter(UserAuthController authNotifier) {
1717
$callRoute,
1818
$callParticipantsRoute,
1919
$callStatsRoute,
20+
$livestreamRoute,
2021
],
2122
builder: (context, state, child) {
2223
return StreamChat(

dogfooding/lib/router/routes.dart

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import 'package:flutter/material.dart';
33
import 'package:flutter_dogfooding/screens/call_participants_list.dart';
44
import 'package:flutter_dogfooding/screens/call_stats_screen.dart';
5+
import 'package:flutter_dogfooding/screens/livestream_demo_screen.dart';
56

67
// 📦 Package imports:
78
import 'package:go_router/go_router.dart';
@@ -57,6 +58,19 @@ class LobbyRoute extends GoRouteData {
5758
}
5859
}
5960

61+
@immutable
62+
@TypedGoRoute<LivestreamRoute>(path: '/livestream', name: 'livestream')
63+
class LivestreamRoute extends GoRouteData {
64+
const LivestreamRoute({required this.$extra});
65+
66+
final String $extra;
67+
68+
@override
69+
Widget build(BuildContext context, GoRouterState state) {
70+
return LivestreamDemoScreen(callId: $extra);
71+
}
72+
}
73+
6074
@immutable
6175
@TypedGoRoute<CallRoute>(path: '/call', name: 'call')
6276
class CallRoute extends GoRouteData {

dogfooding/lib/router/routes.g.dart

Lines changed: 28 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dogfooding/lib/screens/home_screen.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// 🎯 Dart imports:
22
import 'dart:async';
3+
import 'dart:io';
34
import 'dart:math' as math;
45

56
// 🐦 Flutter imports:
@@ -439,6 +440,16 @@ class _JoinForm extends StatelessWidget {
439440
return;
440441
}
441442

443+
if (environment == Environment.livestream) {
444+
// TODO: handle livestream join
445+
// Example: https://livestream-react-demo.vercel.app/?id=6G9bxsMaFbMiGvLWWP85d&type=livestream
446+
final callId = uri.queryParameters['id'];
447+
if (callId != null) {
448+
LivestreamRoute($extra: callId).push(context);
449+
}
450+
return;
451+
}
452+
442453
if (environment != currentEnvironment) {
443454
if (!kIsProd) {
444455
showDialog(
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter_dogfooding/core/model/environment.dart';
3+
import 'package:flutter_dogfooding/core/repos/token_service.dart';
4+
import 'package:flutter_dogfooding/screens/login_screen.dart';
5+
import 'package:stream_video_flutter/stream_video_flutter.dart';
6+
7+
class LivestreamDemoScreen extends StatefulWidget {
8+
const LivestreamDemoScreen({
9+
super.key,
10+
required this.callId,
11+
});
12+
13+
final String callId;
14+
15+
@override
16+
State<LivestreamDemoScreen> createState() => _LivestreamDemoScreenState();
17+
}
18+
19+
class _LivestreamDemoScreenState extends State<LivestreamDemoScreen> {
20+
static const _tokenService = TokenService();
21+
StreamVideo? _streamVideo;
22+
Call? _call;
23+
24+
@override
25+
void initState() {
26+
super.initState();
27+
_joinCall();
28+
}
29+
30+
Future<void> _joinCall() async {
31+
final userId = randomId(size: 6);
32+
33+
final tokenResponse = await _tokenService.loadToken(
34+
userId: userId,
35+
environment: Environment.livestream,
36+
);
37+
38+
var streamVideo = StreamVideo.create(
39+
tokenResponse.apiKey,
40+
user: User.regular(
41+
userId: userId,
42+
),
43+
tokenLoader: (userId) async {
44+
final token = await _tokenService.loadToken(
45+
userId: userId,
46+
environment: Environment.livestream,
47+
);
48+
return token.token;
49+
},
50+
);
51+
52+
final call = streamVideo.makeCall(
53+
callType: StreamCallType.liveStream(),
54+
id: widget.callId,
55+
);
56+
final result = await call.getOrCreate();
57+
58+
if (!mounted) {
59+
return;
60+
}
61+
62+
_streamVideo = streamVideo;
63+
64+
result.fold(success: (data) {
65+
_call = call;
66+
if (mounted) {
67+
setState(() {});
68+
}
69+
}, failure: (error) {
70+
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
71+
content: Text(error.toString()),
72+
));
73+
});
74+
}
75+
76+
@override
77+
void dispose() {
78+
_call?.leave().then((value) {
79+
_streamVideo?.dispose();
80+
});
81+
super.dispose();
82+
}
83+
84+
@override
85+
Widget build(BuildContext context) {
86+
final streamVideoTheme = StreamVideoTheme.of(context);
87+
final textTheme = streamVideoTheme.textTheme;
88+
89+
return Scaffold(
90+
appBar: AppBar(
91+
elevation: 0,
92+
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
93+
titleSpacing: 4,
94+
centerTitle: false,
95+
title: Text(
96+
'Livestream Demo',
97+
style: textTheme.body,
98+
),
99+
actions: [
100+
IconButton(
101+
icon: const Icon(
102+
Icons.close,
103+
color: Colors.white,
104+
),
105+
onPressed: () {
106+
Navigator.maybePop(context);
107+
},
108+
),
109+
],
110+
),
111+
body: switch (_call) {
112+
null => const Center(child: CircularProgressIndicator()),
113+
Call call => LivestreamPlayer(call: call),
114+
},
115+
);
116+
}
117+
}

dogfooding/lib/widgets/environment_switcher.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ class _EnvironmentSwitcherState extends State<EnvironmentSwitcher> {
8585
},
8686
menuChildren: [
8787
...Environment.values
88+
.where((e) => e != Environment.livestream)
8889
.map(
8990
(env) => MenuItemButton(
9091
onPressed: () async {

0 commit comments

Comments
 (0)