Files
saqel/apps/teacher_app/lib/logic/cubits/teacher_qna_cubit.dart
T

127 lines
4.2 KiB
Dart

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<TeacherHomeworkModel> homeworks;
final List<StudentDoubtModel> doubts;
final List<ChatConversationModel> 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<TeacherHomeworkModel>? homeworks,
List<StudentDoubtModel>? doubts,
List<ChatConversationModel>? 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<TeacherQnAState> {
final TeacherRepository repository;
TeacherQnACubit({required this.repository})
: super(const TeacherQnAState(
homeworks: [],
doubts: [],
conversations: [],
isLoading: false,
));
Future<void> 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<TeacherHomeworkModel>,
doubts: results[1] as List<StudentDoubtModel>,
conversations: results[2] as List<ChatConversationModel>,
isLoading: false,
));
} catch (_) {
emit(state.copyWith(isLoading: false));
}
}
void dispatchNewHomework({
required String title,
required String className,
required String dueDate,
}) {
throw StateError('إنشاء الواجبات ينتظر واجهة حفظ حقيقية على الخادم.');
}
Future<void> 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<bool> 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;
}
}