117 lines
4.1 KiB
Dart
117 lines
4.1 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));
|
|
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<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: 'voice',
|
|
mediaUrl: 'https://saqel.intaleqapp.com/assets/audio/teacher_broadcast.mp3',
|
|
gradeLevel: gradeLevel,
|
|
);
|
|
emit(state.copyWith(isBroadcasting: false));
|
|
return success;
|
|
}
|
|
}
|