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)); try { final results = await Future.wait([ repository.getHomeworks(), repository.getStudentDoubts(), repository.getConversations(), ]); emit(state.copyWith( homeworks: results[0] as List, doubts: results[1] as List, conversations: results[2] as List, isLoading: false, )); } catch (_) { emit(state.copyWith(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: 'text', mediaUrl: null, gradeLevel: gradeLevel, ); emit(state.copyWith(isBroadcasting: false)); return success; } }