import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:hadi_hoda_flutter/core/params/no_params.dart'; import 'package:hadi_hoda_flutter/core/status/base_status.dart'; import 'package:hadi_hoda_flutter/core/utils/pre_cache_image.dart'; import 'package:hadi_hoda_flutter/features/download/domain/entities/download_entity.dart'; import 'package:hadi_hoda_flutter/features/download/domain/usecases/batch_download_usecase.dart'; import 'package:hadi_hoda_flutter/features/download/domain/usecases/cancel_download_usecase.dart'; import 'package:hadi_hoda_flutter/features/download/domain/usecases/get_last_downloaded_level.dart'; import 'package:hadi_hoda_flutter/features/download/domain/usecases/loading_stream_usecase.dart'; import 'package:hadi_hoda_flutter/features/download/domain/usecases/save_levels_usecase.dart'; import 'package:hadi_hoda_flutter/features/download/presentation/bloc/download_event.dart'; import 'package:hadi_hoda_flutter/features/download/presentation/bloc/download_state.dart'; class DownloadBloc extends Bloc { /// ------------constructor------------ DownloadBloc( this._loadingStreamUseCase, this._saveLevelsUseCase, this._cancelDownloadUseCase, this._getLastDownloadedLevel, this._batchDownloadUseCase, ) : super(const DownloadState()) { preCacheImages(); on(_startDownloadEvent); on(_saveLevelsEvent); on(_cancelDownloadEvent); loadingStream = _loadingStreamUseCase(); } /// ------------UseCases------------ final SaveLevelsUseCase _saveLevelsUseCase; final BatchDownloadUseCase _batchDownloadUseCase; final LoadingStreamUseCase _loadingStreamUseCase; final CancelDownloadUseCase _cancelDownloadUseCase; final GetLastDownloadedLevel _getLastDownloadedLevel; Stream loadingStream = const Stream.empty(); bool _isDownloading = false; bool _isCancelled = false; bool get isDownloading => _isDownloading; FutureOr _startDownloadEvent( StartDownloadEvent event, Emitter emit, ) async { _isCancelled = false; _isDownloading = true; emit(state.copyWith(status: const BaseLoading())); final levelResult = await _saveLevelsUseCase(NoParams()); if (levelResult.isError) { _isDownloading = false; emit(state.copyWith(status: BaseError(levelResult.error!.errorMessage))); return; } final downloadResult = await _batchDownloadUseCase(event.toLevel); downloadResult.fold( (_) { _isDownloading = false; emit(state.copyWith(status: const BaseComplete(''))); }, (e) { _isDownloading = false; emit(state.copyWith(status: BaseError(e.errorMessage))); }, ); // if (event.types.contains(DownloadType.levels)) { // await LocalStorage.saveData( // key: MyConstants.firstDownload, // value: 'true', // ); // } // if (MyContext.get.mounted && event.destinationRoute != null) { // MyContext.get.goNamed(event.destinationRoute!); // } } Future lastDownloadedLevel() async => (await _getLastDownloadedLevel(NoParams())).data ?? 0; /// Orchestrates batch download (level-by-level). /// If a download is already in progress, just updates the destination /// so the running download navigates there when it finishes. // FutureOr _startBatchDownloadEvent( // StartBatchDownloadEvent event, // Emitter emit, // ) async { // _isCancelled = false; // final String lang = event.batchParams.lang; // final int alreadyDownloaded = int.tryParse( // LocalStorage.readData(key: '${MyConstants.downloadedLevelCount}_$lang') ?? '0', // ) ?? 0; // final int neededUpTo = // event.batchParams.batchStart + event.batchParams.batchSize - 1; // // // Already downloaded — skip straight to destination. // if (alreadyDownloaded >= neededUpTo) { // emit(state.copyWith(getFilesStatus: const BaseComplete(''))); // if (MyContext.get.mounted && event.destinationRoute != null) { // MyContext.get.goNamed( // event.destinationRoute!, // pathParameters: event.destinationPathParameters ?? {}, // ); // } // return; // } // // // A download is already running — just update the destination so the // // ongoing download navigates there when it finishes. // if (_isBatchDownloading) { // emit(state.copyWith( // destinationRoute: event.destinationRoute, // destinationPathParameters: event.destinationPathParameters, // )); // return; // } // // _isBatchDownloading = true; // // emit(state.copyWith( // getFilesStatus: const BaseLoading(), // batchParams: event.batchParams, // destinationRoute: event.destinationRoute, // destinationPathParameters: event.destinationPathParameters, // )); // // final levelsResult = await _saveLevelsUseCase(NoParams()); // if (levelsResult.isError) { // if (_isCancelled) return; // _isBatchDownloading = false; // emit(state.copyWith( // getFilesStatus: BaseError(levelsResult.error!.errorMessage), // )); // return; // } // // if (_isCancelled) return; // final result = await _batchDownloadUseCase(event.batchParams); // if (result.isError) { // if (_isCancelled) return; // _isBatchDownloading = false; // emit(state.copyWith( // getFilesStatus: BaseError(result.error!.errorMessage), // )); // return; // } // // if (_isCancelled) return; // _isBatchDownloading = false; // // emit(state.copyWith(getFilesStatus: const BaseComplete(''))); // // await LocalStorage.saveData( // key: '${MyConstants.downloadedLevelCount}_$lang', // value: '$neededUpTo', // ); // // await LocalStorage.saveData( // key: MyConstants.firstDownload, // value: 'true', // ); // // // Read destination from state (may have been updated by a later event). // if (MyContext.get.mounted && state.destinationRoute != null) { // MyContext.get.goNamed( // state.destinationRoute!, // pathParameters: state.destinationPathParameters ?? {}, // ); // } // } /// Downloads images only (standalone, no chaining). // FutureOr _getImagesEvent( // GetImagesEvent event, // Emitter emit, // ) async { // _isCancelled = false; // emit(state.copyWith(getFilesStatus: const BaseLoading())); // final result = await _getImagesUseCase(NoParams()); // if (result.isError) { // if (_isCancelled) return; // emit(state.copyWith(getFilesStatus: BaseError(result.error!.errorMessage))); // } else { // if (_isCancelled) return; // emit(state.copyWith(getFilesStatus: const BaseComplete(''))); // } // } /// Downloads audios only (standalone, no chaining). // FutureOr _getAudiosEvent( // GetAudiosEvent event, // Emitter emit, // ) async { // _isCancelled = false; // emit(state.copyWith(getFilesStatus: const BaseLoading())); // final result = await _getAudiosUseCase(NoParams()); // if (result.isError) { // if (_isCancelled) return; // emit(state.copyWith(getFilesStatus: BaseError(result.error!.errorMessage))); // } else { // if (_isCancelled) return; // emit(state.copyWith(getFilesStatus: const BaseComplete(''))); // } // } /// Saves levels only (standalone, no chaining). FutureOr _saveLevelsEvent( SaveLevelsEvent event, Emitter emit, ) async { _isCancelled = false; emit(state.copyWith(status: const BaseLoading())); final result = await _saveLevelsUseCase(NoParams()); if (result.isError) { if (_isCancelled) return; emit(state.copyWith(status: BaseError(result.error!.errorMessage))); } else { if (_isCancelled) return; emit(state.copyWith(status: const BaseComplete(''))); } } FutureOr _cancelDownloadEvent( CancelDownloadEvent event, Emitter emit, ) { _isCancelled = true; _isDownloading = false; _cancelDownloadUseCase(NoParams()); emit(state.copyWith(status: const BaseInit())); } }