diff --git a/lib/view_models/user_view_model.dart b/lib/view_models/user_view_model.dart index 7be382a..dac2bb4 100644 --- a/lib/view_models/user_view_model.dart +++ b/lib/view_models/user_view_model.dart @@ -85,7 +85,6 @@ class UserViewModel extends StateNotifier { state = state.copyWith(isLoading: true); try { var semesters = await CourseHandler(_grpcHandler).fetchSemesters(); - semesters.item1.add(semesters.item2); state = state.copyWith(current: semesters.item2, isLoading: false); state = state.copyWith(semesters: semesters.item1, isLoading: false); diff --git a/lib/views/components/filter_popup_menu_button.dart b/lib/views/components/filter_popup_menu_button.dart index 7e22ce1..8931099 100644 --- a/lib/views/components/filter_popup_menu_button.dart +++ b/lib/views/components/filter_popup_menu_button.dart @@ -21,9 +21,6 @@ class FilterPopupMenuButton extends ConsumerWidget { return PopupMenuButton( onSelected: (choice) { - // When an item is selected, update both providers to ensure they stay in sync - ref.read(userViewModelProvider.notifier).setSelectedSemester(choice); - ref.read(pinnedCourseViewModelProvider.notifier).setSelectedSemester(choice); onClick(choice); }, itemBuilder: (BuildContext context) { diff --git a/lib/views/course_view/components/live_stream_section.dart b/lib/views/course_view/components/live_stream_section.dart index 7195934..92ec595 100644 --- a/lib/views/course_view/components/live_stream_section.dart +++ b/lib/views/course_view/components/live_stream_section.dart @@ -89,6 +89,7 @@ class LiveStreamSection extends StatelessWidget { roomName: stream.item1.roomName, roomNumber: stream.item1.roomCode, path: imagePath, + isDownloaded: false, courseId: course.id, onTap: () { Navigator.push( diff --git a/lib/views/course_view/components/small_stream_card.dart b/lib/views/course_view/components/small_stream_card.dart index 70c20b0..4109b67 100644 --- a/lib/views/course_view/components/small_stream_card.dart +++ b/lib/views/course_view/components/small_stream_card.dart @@ -4,6 +4,7 @@ import 'package:gocast_mobile/base/networking/api/gocast/api_v2.pb.dart'; import 'package:gocast_mobile/utils/constants.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +//import 'package:http/http.dart' as http; class SmallStreamCard extends StatelessWidget { final String title; @@ -131,7 +132,7 @@ class SmallStreamCard extends StatelessWidget { _buildCourseTitle(themeData.textTheme), _buildCourseSubtitle(themeData.textTheme), const SizedBox(height: 15), - _buildLocation(themeData), + if (!isDownloaded!) _buildLocation(themeData), ], ), ), @@ -183,14 +184,25 @@ class SmallStreamCard extends StatelessWidget { ], ); } - Widget _buildLocation(ThemeData themeData) { - if (roomNumber == null) return const SizedBox(); + final isLocationEmpty = + (roomName?.isEmpty ?? true) && (roomNumber?.isEmpty ?? true); + + return isLocationEmpty + ? _buildInactiveLocation(themeData) + : _buildActiveLocation(themeData); + } - final Uri url = Uri.parse('https://nav.tum.de/room/$roomNumber'); + Widget _buildActiveLocation(ThemeData themeData) { + // Determine the URL based on the availability of roomNumber + final Uri url = roomNumber?.isNotEmpty ?? false + ? Uri.parse( + 'https://nav.tum.de/room/$roomNumber') // Use roomNumber in URL if available + : Uri.parse( + 'https://nav.tum.de/search?q=${Uri.encodeComponent(roomName ?? '')}'); // Fall back to search URL using roomName return Align( - alignment: Alignment.centerRight, // Align to the right + alignment: Alignment.centerRight, child: Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( @@ -204,18 +216,19 @@ class SmallStreamCard extends StatelessWidget { ), child: InkWell( onTap: () async { - if (await canLaunchUrl(url)) { - await launchUrl(url); - } else { - throw 'Could not launch $url'; + try { + if (await canLaunchUrl(url)) { + await launchUrl(url); + } + } catch (e) { + // Handle exceptions or errors here } }, child: const Row( mainAxisSize: MainAxisSize.min, - // Constrain row size to its children children: [ Icon(Icons.room, size: 24), - SizedBox(width: 8), // Spacing before the arrow icon + SizedBox(width: 8), Icon(Icons.arrow_forward_ios, size: 16), ], ), @@ -224,6 +237,27 @@ class SmallStreamCard extends StatelessWidget { ); } + Widget _buildInactiveLocation(ThemeData themeData) { + return Align( + alignment: Alignment.centerRight, + child: Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + border: Border.all(color: Colors.grey.withOpacity(0.4)), + borderRadius: BorderRadius.circular(5), + ), + child: const Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(Icons.room, size: 24, color: Colors.grey), + SizedBox(width: 8), + Icon(Icons.arrow_forward_ios, size: 16, color: Colors.grey), + ], + ), + ), + ); + } + Widget _buildCourseTumID() { return Text( tumID, diff --git a/lib/views/course_view/components/stream_card.dart b/lib/views/course_view/components/stream_card.dart index ad50ffb..293b2c4 100644 --- a/lib/views/course_view/components/stream_card.dart +++ b/lib/views/course_view/components/stream_card.dart @@ -23,9 +23,6 @@ class StreamCard extends ConsumerStatefulWidget { } class StreamCardState extends ConsumerState { - String imageName = - 'https://live.rbg.tum.de/thumb-fallback.png'; // Replace with your image URL - @override void initState() { super.initState(); @@ -103,7 +100,7 @@ class StreamCardState extends ConsumerState { AspectRatio( aspectRatio: 16 / 9, child: Image.network( - imageName, + widget.imageName, fit: BoxFit.cover, loadingBuilder: (context, child, loadingProgress) { if (loadingProgress == null) { diff --git a/lib/views/course_view/course_detail_view/course_detail_view.dart b/lib/views/course_view/course_detail_view/course_detail_view.dart index 076fbf1..28b948c 100644 --- a/lib/views/course_view/course_detail_view/course_detail_view.dart +++ b/lib/views/course_view/course_detail_view/course_detail_view.dart @@ -71,7 +71,7 @@ class CourseDetailState extends ConsumerState { videoViewModelNotifier.updatedDisplayedStreams(temp); isSearchInitialized = false; } else { - displayedStreams = displayedStreams.where((stream) { + displayedStreams = temp.where((stream) { return stream.item1.name.toLowerCase().contains(searchInput) || stream.item1.description.toLowerCase().contains(searchInput); }).toList(); @@ -88,7 +88,7 @@ class CourseDetailState extends ConsumerState { appBar: CustomSearchTopNavBarWithBackButton( searchController: searchController, onClick: _handleSortOptionSelected, - filterOptions: [AppLocalizations.of(context)!.newest_first, AppLocalizations.of(context)!.oldest_first], + filterOptions: const ['Newest First', 'Oldest First'], ), body: RefreshIndicator( onRefresh: () => _refreshStreams(widget.courseId), diff --git a/lib/views/course_view/courses_overview.dart b/lib/views/course_view/courses_overview.dart index d95aade..39a9ae2 100644 --- a/lib/views/course_view/courses_overview.dart +++ b/lib/views/course_view/courses_overview.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gocast_mobile/providers.dart'; import 'package:gocast_mobile/utils/section_kind.dart'; +import 'package:gocast_mobile/utils/sort_utils.dart'; import 'package:gocast_mobile/views/components/base_view.dart'; import 'package:gocast_mobile/views/course_view/components/course_section.dart'; import 'package:gocast_mobile/views/course_view/components/live_stream_section.dart'; @@ -49,6 +50,12 @@ class CourseOverviewState extends ConsumerState { } final userCourses = ref.watch(userViewModelProvider).userCourses ?? []; final publicCourses = ref.watch(userViewModelProvider).publicCourses ?? []; + final currentSemester = + ref.watch(userViewModelProvider).currentAsString ?? "All"; + final userCoursesCurrent = + CourseUtils.filterCoursesBySemester(userCourses, currentSemester); + final publicCoursesCurrent = + CourseUtils.filterCoursesBySemester(publicCourses, currentSemester); final liveStreams = ref.watch(videoViewModelProvider).liveStreams; final liveStreamWithThumb = ref.watch(videoViewModelProvider).liveStreamsWithThumb ?? []; @@ -76,7 +83,7 @@ class CourseOverviewState extends ConsumerState { child: LiveStreamSection( ref: ref, sectionTitle: AppLocalizations.of(context)!.live_now, - courses: (userCourses) + (publicCourses), + courses: (userCoursesCurrent) + (publicCoursesCurrent), streams: liveStreamWithThumb, ), ), @@ -88,7 +95,7 @@ class CourseOverviewState extends ConsumerState { child: _buildSection( AppLocalizations.of(context)!.my_courses, SectionKind.myCourses, - userCourses, + userCoursesCurrent, liveStreams, ), ), @@ -96,7 +103,7 @@ class CourseOverviewState extends ConsumerState { child: _buildSection( AppLocalizations.of(context)!.public_courses, SectionKind.publicCourses, - publicCourses, + publicCoursesCurrent, liveStreams, ), ), @@ -106,13 +113,13 @@ class CourseOverviewState extends ConsumerState { _buildSection( AppLocalizations.of(context)!.my_courses, SectionKind.myCourses, - userCourses, + userCoursesCurrent, liveStreams, ), _buildSection( AppLocalizations.of(context)!.public_courses, SectionKind.publicCourses, - publicCourses, + publicCoursesCurrent, liveStreams, ), ], diff --git a/lib/views/course_view/list_courses_view/my_courses_view.dart b/lib/views/course_view/list_courses_view/my_courses_view.dart index bab0da0..01b77c1 100644 --- a/lib/views/course_view/list_courses_view/my_courses_view.dart +++ b/lib/views/course_view/list_courses_view/my_courses_view.dart @@ -40,6 +40,9 @@ class MyCoursesState extends ConsumerState { void filterCoursesBySemester(String selectedSemester) { var allUserCourses = ref.watch(userViewModelProvider).userCourses ?? []; + ref + .read(pinnedCourseViewModelProvider.notifier) + .setSelectedSemester(selectedSemester); ref .read(userViewModelProvider.notifier) .updateSelectedSemester(selectedSemester, allUserCourses); @@ -59,7 +62,7 @@ class MyCoursesState extends ConsumerState { userViewModelNotifier.updatedDisplayedCourses(temp); isSearchInitialized = false; } else { - displayedCourses = displayedCourses.where((course) { + displayedCourses = temp.where((course) { return course.name.toLowerCase().contains(searchInput) || course.slug.toLowerCase().contains(searchInput); }).toList(); diff --git a/lib/views/course_view/list_courses_view/public_courses_view.dart b/lib/views/course_view/list_courses_view/public_courses_view.dart index 31c913d..a636cb5 100644 --- a/lib/views/course_view/list_courses_view/public_courses_view.dart +++ b/lib/views/course_view/list_courses_view/public_courses_view.dart @@ -30,8 +30,8 @@ class PublicCoursesState extends ConsumerState { void _initializeCourses() { final userViewModelNotifier = ref.read(userViewModelProvider.notifier); Future.microtask(() async { - await userViewModelNotifier.fetchPublicCourses(); await userViewModelNotifier.fetchSemesters(); + await userViewModelNotifier.fetchPublicCourses(); }); } @@ -41,6 +41,9 @@ class PublicCoursesState extends ConsumerState { void filterCoursesBySemester(String selectedSemester) { var allUserCourses = ref.watch(userViewModelProvider).publicCourses ?? []; + ref + .read(pinnedCourseViewModelProvider.notifier) + .setSelectedSemester(selectedSemester); ref .read(userViewModelProvider.notifier) .updateSelectedSemester(selectedSemester, allUserCourses); @@ -60,7 +63,7 @@ class PublicCoursesState extends ConsumerState { userViewModelNotifier.updatedDisplayedCourses(temp); isSearchInitialized = false; } else { - displayedCourses = displayedCourses.where((course) { + displayedCourses = temp.where((course) { return course.name.toLowerCase().contains(searchInput) || course.slug.toLowerCase().contains(searchInput); }).toList(); diff --git a/lib/views/course_view/pinned_courses_view/pinned_courses_view.dart b/lib/views/course_view/pinned_courses_view/pinned_courses_view.dart index c3567fd..fa13518 100644 --- a/lib/views/course_view/pinned_courses_view/pinned_courses_view.dart +++ b/lib/views/course_view/pinned_courses_view/pinned_courses_view.dart @@ -42,6 +42,9 @@ class PinnedCoursesState extends ConsumerState { } void filterCoursesBySemester(String selectedSemester) { + ref + .read(userViewModelProvider.notifier) + .setSelectedSemester(selectedSemester); var userPinned = ref.watch(pinnedCourseViewModelProvider).userPinned ?? []; ref .read(pinnedCourseViewModelProvider.notifier) @@ -62,7 +65,7 @@ class PinnedCoursesState extends ConsumerState { pinnedViewModelNotifier.updatedDisplayedPinnedCourses(temp); isSearchInitialized = false; } else { - displayedCourses = displayedCourses.where((course) { + displayedCourses = temp.where((course) { return course.name.toLowerCase().contains(searchInput) || course.slug.toLowerCase().contains(searchInput); }).toList(); diff --git a/lib/views/video_view/video_player.dart b/lib/views/video_view/video_player.dart index a79dd55..7efea07 100644 --- a/lib/views/video_view/video_player.dart +++ b/lib/views/video_view/video_player.dart @@ -81,7 +81,8 @@ class VideoPlayerPageState extends ConsumerState { .read(courseViewModelProvider) .course; if (course != null) { - if (course.chatEnabled && course.vodChatEnabled && widget.stream.chatEnabled) { + if ((course.chatEnabled && widget.stream.chatEnabled) || + (course.vodChatEnabled && widget.stream.chatEnabled)) { setState(() { _isChatActive = true; _isPollActive = true;