import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:material_symbols_icons/symbols.dart'; import 'package:matrix/matrix.dart' as sdk; import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pages/chat_list/chat_list_item.dart'; import 'package:fluffychat/pangea/analytics_summary/learning_progress_indicators.dart'; import 'package:fluffychat/pangea/chat_settings/widgets/chat_context_menu_action.dart'; import 'package:fluffychat/pangea/course_chats/activity_template_chat_list_item.dart'; import 'package:fluffychat/pangea/course_chats/course_chats_page.dart'; import 'package:fluffychat/pangea/course_chats/course_default_chats_enum.dart'; import 'package:fluffychat/pangea/course_chats/unjoined_chat_list_item.dart'; import 'package:fluffychat/pangea/space_analytics/analytics_request_indicator.dart'; import 'package:fluffychat/pangea/spaces/knocking_users_indicator.dart'; import 'package:fluffychat/utils/stream_extension.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; class CourseChatsView extends StatelessWidget { final CourseChatsController controller; const CourseChatsView( this.controller, { super.key, }); @override Widget build(BuildContext context) { final room = controller.room; if (room == null) { return const Center( child: Icon( Icons.search_outlined, size: 80, ), ); } return StreamBuilder( stream: room.client.onSync.stream .where((s) => s.hasRoomUpdate) .rateLimit(const Duration(seconds: 1)), builder: (context, snapshot) { final joinedChats = controller.joinedChats; final joinedSessions = controller.joinedActivities(); final discoveredGroupChats = controller.discoveredGroupChats; final discoveredSessions = controller.discoveredActivities().entries.toList(); final isColumnMode = FluffyThemes.isColumnMode(context); return Padding( padding: isColumnMode ? const EdgeInsets.only( top: 12.0, left: 8.0, right: 8.0, ) : const EdgeInsets.all(0.0), child: ListView.builder( shrinkWrap: true, itemCount: joinedChats.length + joinedSessions.length + discoveredGroupChats.length + discoveredSessions.length + 9, itemBuilder: (context, i) { // courses chats title if (i == 0) { if (isColumnMode) { return const Padding( padding: EdgeInsets.symmetric( vertical: 4.0, horizontal: 8.0, ), child: Column( spacing: 12.0, mainAxisSize: MainAxisSize.min, children: [ LearningProgressIndicators(), Icon( Icons.chat_bubble_outline, size: 30.0, ), SizedBox(height: 12.0), ], ), ); } return const SizedBox(); } i--; if (i == 0) { return KnockingUsersIndicator(room: room); } i--; if (i == 0) { return AnalyticsRequestIndicator(room: room); } i--; if (i == 0) { return _DefaultChatCreationTile( type: CourseDefaultChatsEnum.introductions, controller: controller, ); } i--; if (i == 0) { return _DefaultChatCreationTile( type: CourseDefaultChatsEnum.announcements, controller: controller, ); } i--; // joined group chats if (i < joinedChats.length) { final joinedRoom = joinedChats[i]; return ChatListItem( joinedRoom, onTap: () => controller.onChatTap(joinedRoom), onLongPress: (c) => chatContextMenuAction( joinedRoom, c, context, () => controller.onChatTap(joinedRoom), ), activeChat: controller.widget.activeChat == joinedRoom.id, ); } i -= joinedChats.length; // unjoined group chats if (i < discoveredGroupChats.length) { return UnjoinedChatListItem( chunk: discoveredGroupChats[i], onTap: () => controller.joinChildRoom(discoveredGroupChats[i]), ); } i -= discoveredGroupChats.length; if (i == 0) { return joinedSessions.isEmpty && discoveredSessions.isEmpty ? ListTile( leading: const Icon(Icons.map_outlined), title: Text(L10n.of(context).whatNow), subtitle: Text(L10n.of(context).chooseNextActivity), trailing: const Icon(Icons.arrow_forward), onTap: () => context.pushReplacement( "/rooms/spaces/${room.id}/details?tab=course", ), ) : const SizedBox(); } i--; if (i == 0) { return joinedSessions.isEmpty ? const SizedBox() : Padding( padding: const EdgeInsets.only( top: 20.0, bottom: 4.0, ), child: Text( L10n.of(context).myActivities, style: const TextStyle(fontSize: 12.0), textAlign: TextAlign.center, ), ); } i--; // joined activity sessions if (i < joinedSessions.length) { final joinedRoom = joinedSessions[i]; return ChatListItem( joinedRoom, onTap: () => controller.onChatTap(joinedRoom), onLongPress: (c) => chatContextMenuAction( joinedRoom, c, context, () => controller.onChatTap(joinedRoom), ), activeChat: controller.widget.activeChat == joinedRoom.id, borderRadius: BorderRadius.circular( AppConfig.borderRadius / 2, ), ); } i -= joinedSessions.length; if (i == 0) { return discoveredSessions.isEmpty ? const SizedBox() : Padding( padding: const EdgeInsets.only( top: 20.0, bottom: 4.0, ), child: Text( L10n.of(context).openToJoin, style: const TextStyle(fontSize: 12.0), textAlign: TextAlign.center, ), ); } i--; // unjoined activity sessions if (i < discoveredSessions.length) { final activity = discoveredSessions[i].key; final sessions = discoveredSessions[i].value; return ActivityTemplateChatListItem( space: room, sessions: sessions, joinActivity: (e) => controller.joinActivity(activity, e), ); } i -= discoveredSessions.length; if (controller.noMoreRooms) { return const SizedBox(); } return Padding( padding: const EdgeInsets.symmetric( horizontal: 12.0, vertical: 2.0, ), child: TextButton( onPressed: controller.isLoading ? null : controller.loadHierarchy, child: controller.isLoading ? LinearProgressIndicator( borderRadius: BorderRadius.circular( AppConfig.borderRadius, ), ) : Text(L10n.of(context).loadMore), ), ); }, ), ); }, ); } } class _DefaultChatCreationTile extends StatelessWidget { final CourseDefaultChatsEnum type; final CourseChatsController controller; const _DefaultChatCreationTile({ required this.type, required this.controller, }); @override Widget build(BuildContext context) { if (!controller.showDefaultChatCreation(type)) { return const SizedBox(); } final l10n = L10n.of(context); return ListTile( leading: const Icon(Symbols.chat_add_on), title: Text(type.creationTitle(l10n)), subtitle: Text(type.creationDesc(l10n)), trailing: IconButton( icon: const Icon(Icons.close), onPressed: () => showFutureLoadingDialog( context: context, future: () => controller.dismissDefaultChatCreation(type), ), ), onTap: () => showFutureLoadingDialog( context: context, future: () => controller.createDefaultChat(type), ), ); } }