You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

258 lines
8.3 KiB

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_audios.dart';
import 'package:hadi_hoda_flutter/core/constants/my_constants.dart';
import 'package:hadi_hoda_flutter/core/params/question_params.dart';
import 'package:hadi_hoda_flutter/core/routers/hero_dialog_route.dart';
import 'package:hadi_hoda_flutter/core/routers/my_routes.dart';
import 'package:hadi_hoda_flutter/core/services/audio_service.dart';
import 'package:hadi_hoda_flutter/core/status/base_status.dart';
import 'package:hadi_hoda_flutter/core/utils/context_provider.dart';
import 'package:hadi_hoda_flutter/core/utils/local_storage.dart';
import 'package:hadi_hoda_flutter/core/widgets/dialog/hadith_dialog.dart';
import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart';
import 'package:hadi_hoda_flutter/features/question/domain/entity/answer_entity.dart';
import 'package:hadi_hoda_flutter/features/question/domain/entity/question_entity.dart';
import 'package:hadi_hoda_flutter/features/question/domain/usecases/get_level_usecase.dart';
import 'package:hadi_hoda_flutter/features/question/domain/usecases/get_next_level_usecase.dart';
import 'package:hadi_hoda_flutter/features/question/presentation/bloc/question_event.dart';
import 'package:hadi_hoda_flutter/features/question/presentation/bloc/question_state.dart';
import 'package:hadi_hoda_flutter/features/question/presentation/ui/screens/answer_screen.dart';
import 'package:showcaseview/showcaseview.dart';
class QuestionBloc extends Bloc<QuestionEvent, QuestionState> {
/// ------------constructor------------
QuestionBloc(
this._getLevelUseCase,
this._getNextLevelUseCase,
this._mainAudioService,
this._effectAudioService,
) : super(QuestionState()) {
volumeStream = _mainAudioService.volumeStream();
stopMusic();
registerShowCase();
on<GetLevelEvent>(_getLevelEvent);
on<ChooseAnswerEvent>(_chooseAnswerEvent);
}
@override
Future<void> close() {
ShowcaseView.get().unregister();
return super.close();
}
/// ------------UseCases------------
final GetLevelUseCase _getLevelUseCase;
final GetNextLevelUseCase _getNextLevelUseCase;
/// ------------Variables------------
final Map<String, GlobalKey> showCaseKey = {
'answer_key_0': GlobalKey(),
'answer_key_1': GlobalKey(),
'answer_key_2': GlobalKey(),
'answer_key_3': GlobalKey(),
'notif_key_0': GlobalKey(),
'notif_key_1': GlobalKey(),
'notif_key_2': GlobalKey(),
'notif_key_3': GlobalKey(),
'stepper_key': GlobalKey(),
'hadith_key': GlobalKey(),
'guide_key': GlobalKey(),
};
late final Stream<double> volumeStream;
bool isPlaying = false;
/// ------------Controllers------------
final AudioService _mainAudioService;
final AudioService _effectAudioService;
/// ------------Functions------------
void registerShowCase() {
ShowcaseView.register(
onStart: (showcaseIndex, key) {
LocalStorage.saveData(key: MyConstants.firstShowcase, value: 'true');
},
);
}
void startShowcase() {
if (LocalStorage.readData(key: MyConstants.firstShowcase) != 'true') {
ShowcaseView.get().startShowCase([
showCaseKey['answer_key_1']!,
showCaseKey['notif_key_0']!,
showCaseKey['stepper_key']!,
showCaseKey['hadith_key']!,
showCaseKey['guide_key']!,
]);
}
}
void showHadith({required BuildContext context}) {
if(isPlaying) return;
showHadithDialog(
context: context,
hadith: state.levelEntity?.hadith ?? [],
);
}
void goToHomePage({required BuildContext context}) {
context.goNamed(Routes.homePage);
}
void goToLevelPage({required BuildContext context}) {
context.goNamed(Routes.levelPage);
}
Future<void> playDiamondAudio() async {
await _effectAudioService.setAudio(assetPath: MyAudios.diamondEnd);
await _effectAudioService.play();
}
Future<void> stopMusic() async {
await _mainAudioService.stop();
await _mainAudioService.setLoopMode(isLoop: false);
}
Future<void> playCorrectAudio() async {
await _effectAudioService.setAudio(assetPath: MyAudios.rightAnswer);
await _effectAudioService.play();
}
Future<void> playWrongAudio() async {
await _effectAudioService.setAudio(assetPath: MyAudios.incorrectAnswer);
await _effectAudioService.play();
}
Future<void> playAnswerAudio({String? audio}) async {
await _mainAudioService.setAudio(filePath: audio);
await _mainAudioService.play();
}
Future<void> playQuestionAudio() async {
await _mainAudioService.setAudio(filePath: state.currentQuestion?.audio);
await _mainAudioService.play();
}
Future<void> changeMute() async {
await Future.wait([
_mainAudioService.changeMute(),
_effectAudioService.changeMute(),
]);
}
Future<void> showAnswerDialog({
required BuildContext context,
required AnswerEntity answerEntity,
}) async {
Navigator.of(context).push(
HeroDialogRoute(
builder: (dialogContext) {
return AnswerScreen(
answerEntity: answerEntity,
onNotifTap: (answer) => playAnswerAudio(audio: answer.audio),
);
},
),
);
playAnswerAudio(audio: answerEntity.audio);
}
Future<void> getNextLevelEvent() async {
await _getNextLevelUseCase(QuestionParams()).then((value) =>
value.fold(
(data) {
ContextProvider.context.pushReplacementNamed(
Routes.questionPage,
pathParameters: {
'id': '${data.id}'
},
);
},
(error) {
goToLevelPage(context: ContextProvider.context);
},
),
);
}
/// ------------Event Calls------------
FutureOr<void> _getLevelEvent(GetLevelEvent event, Emitter<QuestionState> emit) async {
await _getLevelUseCase(QuestionParams(id: int.parse(event.id ?? '0'))).then(
(value) {
value.fold(
(data) async {
final LevelEntity level = LevelEntity(
id: data.id,
order: data.order,
title: data.title,
hadith: data.hadith,
questions: [
...?data.questions,
QuestionEntity(order: (data.questions?.length ?? 0) + 1)
],
);
emit(state.copyWith(
getQuestionStatus: BaseComplete(''),
levelEntity: level,
currentQuestion: data.questions?.first,
));
await playQuestionAudio();
startShowcase();
},
(error) {
emit(state.copyWith(getQuestionStatus: BaseError(error.errorMessage)));
},
);
},
);
}
FutureOr<void> _chooseAnswerEvent(ChooseAnswerEvent event,
Emitter<QuestionState> emit,) async {
emit(state.copyWith(correctAnswer: event.chooseCorrectAnswer));
if (event.chooseCorrectAnswer) {
playCorrectAudio();
await Navigator.of(ContextProvider.context).push(
HeroDialogRoute(
builder: (dialogContext) {
return AnswerScreen(
answerEntity: state.currentQuestion?.answers?.singleWhere((e) =>
e.order == event.correctAnswer) ?? AnswerEntity(),
showConfetti: true,
);
},
),
);
await Future.delayed(Duration(seconds: 1), () async {
final QuestionEntity? findPreQuestion = state.currentQuestion;
final int findIndex = (findPreQuestion?.order ?? 1);
emit(
state.copyWith(
currentQuestion: state.levelEntity?.questions?[findIndex],
),
);
if (state.currentQuestion?.order ==
state.levelEntity?.questions?.length) {
playDiamondAudio();
int currentLevel = int.parse(
LocalStorage.readData(key: MyConstants.currentLevel) ?? '1');
if (state.levelEntity?.order == currentLevel) {
++currentLevel;
await LocalStorage.saveData(
key: MyConstants.currentLevel,
value: '$currentLevel',
);
}
} else {
playQuestionAudio();
}
});
} else {
playWrongAudio();
}
}
}