Add live room preview thumbnails

This commit is contained in:
2026-04-22 11:01:32 +08:00
parent 425ea363f8
commit b07f243c88
5 changed files with 208 additions and 18 deletions

View File

@@ -10,8 +10,10 @@ import 'player_page.dart';
import 'my_stream_page.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
_HomePageState createState() => _HomePageState();
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@@ -21,7 +23,7 @@ class _HomePageState extends State<HomePage> {
Widget build(BuildContext context) {
bool isWide = MediaQuery.of(context).size.width > 600;
final List<Widget> _pages = [
final List<Widget> pages = [
_ExploreView(onGoLive: () => setState(() => _selectedIndex = 1)),
MyStreamPage(),
SettingsPage(),
@@ -51,7 +53,7 @@ class _HomePageState extends State<HomePage> {
),
],
),
Expanded(child: _pages[_selectedIndex]),
Expanded(child: pages[_selectedIndex]),
],
),
bottomNavigationBar: !isWide
@@ -91,6 +93,8 @@ class _ExploreViewState extends State<_ExploreView> {
List<dynamic> _activeRooms = [];
bool _isLoading = false;
Timer? _refreshTimer;
String _thumbnailCacheBuster = DateTime.now().millisecondsSinceEpoch
.toString();
@override
void initState() {
@@ -117,13 +121,20 @@ class _ExploreViewState extends State<_ExploreView> {
final response = await api.getActiveRooms();
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
if (mounted) setState(() => _activeRooms = data['active_rooms'] ?? []);
if (mounted) {
setState(() {
_activeRooms = data['active_rooms'] ?? [];
_thumbnailCacheBuster = DateTime.now().millisecondsSinceEpoch
.toString();
});
}
}
} catch (e) {
if (!isAuto && mounted)
if (!isAuto && mounted) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text("Failed to load rooms")));
}
} finally {
if (!isAuto && mounted) setState(() => _isLoading = false);
}
@@ -198,20 +209,21 @@ class _ExploreViewState extends State<_ExploreView> {
}
Widget _buildRoomCard(dynamic room, SettingsProvider settings) {
final roomId = room['room_id'].toString();
return Card(
elevation: 4,
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: InkWell(
onTap: () {
final playbackUrl = settings.playbackUrl(room['room_id'].toString());
final playbackUrl = settings.playbackUrl(roomId);
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => PlayerPage(
title: room['title'],
playbackUrl: playbackUrl,
roomId: room['room_id'].toString(),
roomId: roomId,
),
),
);
@@ -224,16 +236,37 @@ class _ExploreViewState extends State<_ExploreView> {
child: Stack(
fit: StackFit.expand,
children: [
Container(
color: Theme.of(context).colorScheme.primaryContainer,
child: Center(
child: Icon(
Icons.live_tv,
size: 50,
color: Theme.of(context).colorScheme.primary,
),
),
),
settings.livePreviewThumbnailsEnabled
? Image.network(
settings.thumbnailUrl(
roomId,
cacheBuster: _thumbnailCacheBuster,
),
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) =>
_buildRoomPreviewFallback(),
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) {
return child;
}
return Stack(
fit: StackFit.expand,
children: [
_buildRoomPreviewFallback(),
const Center(
child: SizedBox(
width: 22,
height: 22,
child: CircularProgressIndicator(
strokeWidth: 2,
),
),
),
],
);
},
)
: _buildRoomPreviewFallback(),
Positioned(
top: 8,
left: 8,
@@ -306,4 +339,17 @@ class _ExploreViewState extends State<_ExploreView> {
),
);
}
Widget _buildRoomPreviewFallback() {
return Container(
color: Theme.of(context).colorScheme.primaryContainer,
child: Center(
child: Icon(
Icons.live_tv,
size: 50,
color: Theme.of(context).colorScheme.primary,
),
),
);
}
}

View File

@@ -11,7 +11,7 @@ class SettingsPage extends StatefulWidget {
const SettingsPage({super.key});
@override
_SettingsPageState createState() => _SettingsPageState();
State<SettingsPage> createState() => _SettingsPageState();
}
class _SettingsPageState extends State<SettingsPage> {
@@ -221,6 +221,18 @@ class _SettingsPageState extends State<SettingsPage> {
);
}).toList(),
),
const SizedBox(height: 32),
_buildSectionTitle("Explore"),
const SizedBox(height: 8),
SwitchListTile.adaptive(
contentPadding: EdgeInsets.zero,
title: const Text("Live Preview Thumbnails"),
subtitle: const Text(
"Show cached snapshot covers for live rooms when available.",
),
value: settings.livePreviewThumbnailsEnabled,
onChanged: settings.setLivePreviewThumbnailsEnabled,
),
if (isAuthenticated) ...[
const SizedBox(height: 32),
_buildSectionTitle("Security"),