import 'package:flutter_bloc/flutter_bloc.dart'; import '../../data/models/teacher_models.dart'; import '../../data/repositories/teacher_repository.dart'; /** * ============================================================================== * SAQEL TEACHER STUDIO - ASSIGNMENTS, Q&A & WEBSOCKET BROADCAST CUBIT * ============================================================================== * * إدارة الواجبات المدرسية وغرفة استفسارات الطلبة والبث الفوري عبر الويب سوكت: * - محادثات حية ومباشرة مع الطلبة المسجلين * - تسجيل وإرسال الردود الصوتية السقراطية (Socratic Voice Replies 🎙️) * - بث مقاطع صوتية توجيهية لكافة طلبة الشعبة عبر الويب سوكت (0ms Broadcast). */ class TeacherQnAState { final List homeworks; final List doubts; final List conversations; final bool isLoading; final bool isBroadcasting; const TeacherQnAState({ required this.homeworks, required this.doubts, required this.conversations, required this.isLoading, this.isBroadcasting = false, }); TeacherQnAState copyWith({ List? homeworks, List? doubts, List? conversations, bool? isLoading, bool? isBroadcasting, }) { return TeacherQnAState( homeworks: homeworks ?? this.homeworks, doubts: doubts ?? this.doubts, conversations: conversations ?? this.conversations, isLoading: isLoading ?? this.isLoading, isBroadcasting: isBroadcasting ?? this.isBroadcasting, ); } } class TeacherQnACubit extends Cubit { final TeacherRepository repository; TeacherQnACubit({required this.repository}) : super(const TeacherQnAState( homeworks: [], doubts: [], conversations: [], isLoading: false, )); Future loadQnA() async { emit(state.copyWith(isLoading: true)); final hwList = await repository.getHomeworks(); final doubtsList = await repository.getStudentDoubts(); final conversationsList = await repository.getConversations(); emit(state.copyWith( homeworks: hwList, doubts: doubtsList, conversations: conversationsList, isLoading: false, )); } void dispatchNewHomework({ required String title, required String className, required String dueDate, }) { throw StateError('إنشاء الواجبات ينتظر واجهة حفظ حقيقية على الخادم.'); } Future sendVoiceReply(String doubtId, {String? voiceText}) async { final targetIndex = state.doubts.indexWhere((doubt) => doubt.id == doubtId); if (targetIndex < 0 || state.doubts[targetIndex].studentId <= 0) { throw StateError('لا يوجد طالب حقيقي مرتبط بهذا الاستفسار.'); } final target = state.doubts[targetIndex]; final replyText = voiceText?.trim() ?? ''; if (replyText.isEmpty) { throw StateError('لا يمكن تسجيل رد صوتي دون ملف صوت حقيقي؛ أرسل رداً نصياً حالياً.'); } await repository.sendMessage( receiverId: target.studentId, message: replyText, messageType: 'text', ); final updated = state.doubts.map((doubt) => doubt.id == doubtId ? doubt.copyWith(replied: true, voiceReply: false) : doubt).toList(); emit(state.copyWith(doubts: updated)); } Future broadcastAudioAnnouncement({ required String title, required String explanationText, required String gradeLevel, }) async { emit(state.copyWith(isBroadcasting: true)); final success = await repository.broadcastAnnouncement( title: title, message: explanationText, messageType: 'voice', mediaUrl: 'https://saqel.intaleqapp.com/assets/audio/teacher_broadcast.mp3', gradeLevel: gradeLevel, ); emit(state.copyWith(isBroadcasting: false)); return success; } }