Update Saqel Platform: 2026-09-05 23:24:49
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../../core/services/storage_service.dart';
|
||||
import '../../core/utils/app_logger.dart';
|
||||
import '../../data/models/teacher_models.dart';
|
||||
import '../../data/repositories/teacher_repository.dart';
|
||||
|
||||
abstract class TeacherAuthState {}
|
||||
|
||||
class TeacherAuthInitial extends TeacherAuthState {}
|
||||
class TeacherAuthLoading extends TeacherAuthState {}
|
||||
|
||||
class TeacherAuthOtpSent extends TeacherAuthState {
|
||||
final String phoneNumber;
|
||||
TeacherAuthOtpSent({required this.phoneNumber});
|
||||
}
|
||||
|
||||
class TeacherAuthenticated extends TeacherAuthState {
|
||||
final TeacherProfileModel profile;
|
||||
TeacherAuthenticated({required this.profile});
|
||||
}
|
||||
|
||||
class TeacherUnauthenticated extends TeacherAuthState {}
|
||||
|
||||
class TeacherAuthError extends TeacherAuthState {
|
||||
final String message;
|
||||
TeacherAuthError(this.message);
|
||||
}
|
||||
|
||||
class TeacherAuthCubit extends Cubit<TeacherAuthState> {
|
||||
final TeacherRepository repository;
|
||||
final StorageService _storage;
|
||||
|
||||
TeacherAuthCubit({
|
||||
required this.repository,
|
||||
StorageService? storage,
|
||||
}) : _storage = storage ?? StorageService(),
|
||||
super(TeacherAuthInitial());
|
||||
|
||||
Future<void> checkSession() async {
|
||||
AppLogger.log('Checking Teacher Session...', tag: 'TEACHER_AUTH');
|
||||
emit(TeacherAuthLoading());
|
||||
try {
|
||||
final token = await _storage.getToken();
|
||||
if (token == null || token.isEmpty) {
|
||||
AppLogger.log('No teacher token -> Unauthenticated', tag: 'TEACHER_AUTH');
|
||||
emit(TeacherUnauthenticated());
|
||||
return;
|
||||
}
|
||||
|
||||
final profile = await repository.getProfileStatus();
|
||||
AppLogger.log('Teacher authenticated: ${profile.name} (${profile.specialization})', tag: 'TEACHER_AUTH');
|
||||
emit(TeacherAuthenticated(profile: profile));
|
||||
} catch (e) {
|
||||
AppLogger.error('Session check failed', error: e, tag: 'TEACHER_AUTH');
|
||||
emit(TeacherUnauthenticated());
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> sendOtp(String phoneNumber) async {
|
||||
emit(TeacherAuthLoading());
|
||||
try {
|
||||
final res = await repository.requestOtp(phoneNumber);
|
||||
if (res['status'] == 'success') {
|
||||
emit(TeacherAuthOtpSent(phoneNumber: phoneNumber));
|
||||
} else {
|
||||
emit(TeacherAuthError(res['message']?.toString() ?? 'فشل إرسال رمز التحقق'));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(TeacherAuthError(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> verifyOtp(String phoneNumber, String otp, {String? fullName}) async {
|
||||
emit(TeacherAuthLoading());
|
||||
try {
|
||||
final res = await repository.verifyOtp(phoneNumber, otp, fullName: fullName);
|
||||
if (res['status'] == 'success') {
|
||||
final profile = await repository.getProfileStatus();
|
||||
emit(TeacherAuthenticated(profile: profile));
|
||||
} else {
|
||||
emit(TeacherAuthError(res['message']?.toString() ?? 'رمز التحقق غير صحيح'));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(TeacherAuthError(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateProfile({
|
||||
required String fullName,
|
||||
required String specialization,
|
||||
required String schoolName,
|
||||
required List<String> gradesTaught,
|
||||
required String bio,
|
||||
}) async {
|
||||
emit(TeacherAuthLoading());
|
||||
try {
|
||||
final updated = await repository.setupProfile(
|
||||
fullName: fullName,
|
||||
specialization: specialization,
|
||||
schoolName: schoolName,
|
||||
gradesTaught: gradesTaught,
|
||||
bio: bio,
|
||||
);
|
||||
emit(TeacherAuthenticated(profile: updated));
|
||||
} catch (e) {
|
||||
emit(TeacherAuthError(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> logout() async {
|
||||
emit(TeacherAuthLoading());
|
||||
await _storage.clearSession();
|
||||
emit(TeacherUnauthenticated());
|
||||
}
|
||||
}
|
||||
@@ -4,35 +4,43 @@ import '../../data/repositories/teacher_repository.dart';
|
||||
|
||||
/**
|
||||
* ==============================================================================
|
||||
* SAQEL TEACHER STUDIO - ASSIGNMENTS & Q&A CUBIT
|
||||
* SAQEL TEACHER STUDIO - ASSIGNMENTS, Q&A & WEBSOCKET BROADCAST CUBIT
|
||||
* ==============================================================================
|
||||
*
|
||||
* إدارة الواجبات المدرسية وأوراق العمل وغرفة تساؤلات الطلبة:
|
||||
* - توليد وإسناد أوراق عمل مؤتمتة التصحيح بنقرة واحدة
|
||||
* - تسجيل الردود الصوتية السقراطية (Socratic Voice Replies 🎙️)
|
||||
* - تحديث فوري لحالات الشكوك والتساؤلات.
|
||||
* إدارة الواجبات المدرسية وغرفة استفسارات الطلبة والبث الفوري عبر الويب سوكت:
|
||||
* - محادثات حية ومباشرة مع الطلبة المسجلين (مثل سما حمزة)
|
||||
* - تسجيل وإرسال الردود الصوتية السقراطية (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,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -44,6 +52,7 @@ class TeacherQnACubit extends Cubit<TeacherQnAState> {
|
||||
: super(const TeacherQnAState(
|
||||
homeworks: [],
|
||||
doubts: [],
|
||||
conversations: [],
|
||||
isLoading: false,
|
||||
));
|
||||
|
||||
@@ -51,9 +60,12 @@ class TeacherQnACubit extends Cubit<TeacherQnAState> {
|
||||
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,
|
||||
));
|
||||
}
|
||||
@@ -77,7 +89,7 @@ class TeacherQnACubit extends Cubit<TeacherQnAState> {
|
||||
emit(state.copyWith(homeworks: updated));
|
||||
}
|
||||
|
||||
void recordVoiceReply(String doubtId) {
|
||||
Future<void> sendVoiceReply(String doubtId, {String? voiceText}) async {
|
||||
final updated = state.doubts.map((doubt) {
|
||||
if (doubt.id == doubtId) {
|
||||
return doubt.copyWith(replied: true, voiceReply: true);
|
||||
@@ -86,5 +98,30 @@ class TeacherQnACubit extends Cubit<TeacherQnAState> {
|
||||
}).toList();
|
||||
|
||||
emit(state.copyWith(doubts: updated));
|
||||
|
||||
// Also send actual message to student via API
|
||||
await repository.sendMessage(
|
||||
receiverId: 2, // Sama Hamza (Real Student)
|
||||
message: voiceText ?? 'رد صوتي سقراطي: تم تسجيل وتوجيه المفاهيم المتعلقة بالسؤال.',
|
||||
messageType: 'voice',
|
||||
mediaUrl: 'https://saqel.intaleqapp.com/assets/audio/voice_reply_socratic.mp3',
|
||||
);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user