From 6c91b55c5707aa1af61e9dd40192488e5fdf9116 Mon Sep 17 00:00:00 2001 From: AmirrezaChegini Date: Tue, 7 Oct 2025 10:25:30 +0330 Subject: [PATCH] fix: download logic --- lib/core/auth_storage/auth_storage.dart | 2 +- lib/core/constants/my_api.dart | 3 +- lib/core/constants/my_constants.dart | 6 +- lib/core/routers/my_routes.dart | 13 +- lib/core/utils/local_storage.dart | 4 +- .../data/datasource/download_datasource.dart | 116 +++++++++++++++ .../download_repository_impl.dart | 50 +++++++ .../domain/entities/download_entity.dart | 0 .../repository/download_repository.dart | 10 ++ .../domain/usecases/get_audios_usecase.dart | 16 +++ .../loading_stream_audio_usecase.dart | 12 ++ .../domain/usecases/save_levels_usecase.dart | 4 +- .../presentation/bloc/download_bloc.dart | 74 ++++++++++ .../presentation/bloc/download_event.dart | 5 + .../presentation/bloc/download_state.dart | 15 ++ .../presentation/ui/download_page.dart | 130 +++++++++++++++++ .../ui/widgets/download_loading_widget.dart | 136 ++++++++++++++++++ .../data/datasource/intro_datasource.dart | 60 ++------ .../intro_repository_impl.dart | 22 +-- .../domain/repository/intro_repository.dart | 5 +- ...s_usecase.dart => get_images_usecase.dart} | 6 +- .../usecases/loading_stream_usecase.dart | 2 +- .../intro/presentation/bloc/intro_bloc.dart | 52 +++---- .../intro/presentation/bloc/intro_event.dart | 3 +- .../intro/presentation/ui/intro_page.dart | 6 +- .../ui/widgets/intro_loading_widget.dart | 2 +- .../presentation/bloc/language_bloc.dart | 27 ++-- .../data/datasource/level_datasource.dart | 2 +- .../level/presentation/bloc/level_bloc.dart | 11 +- .../data/datasource/question_datasource.dart | 2 +- lib/init_bindings.dart | 21 ++- lib/main.dart | 12 +- 32 files changed, 672 insertions(+), 157 deletions(-) create mode 100644 lib/features/download/data/datasource/download_datasource.dart create mode 100644 lib/features/download/data/repository_impl/download_repository_impl.dart rename lib/features/{intro => download}/domain/entities/download_entity.dart (100%) create mode 100644 lib/features/download/domain/repository/download_repository.dart create mode 100644 lib/features/download/domain/usecases/get_audios_usecase.dart create mode 100644 lib/features/download/domain/usecases/loading_stream_audio_usecase.dart rename lib/features/{intro => download}/domain/usecases/save_levels_usecase.dart (78%) create mode 100644 lib/features/download/presentation/bloc/download_bloc.dart create mode 100644 lib/features/download/presentation/bloc/download_event.dart create mode 100644 lib/features/download/presentation/bloc/download_state.dart create mode 100644 lib/features/download/presentation/ui/download_page.dart create mode 100644 lib/features/download/presentation/ui/widgets/download_loading_widget.dart rename lib/features/intro/domain/usecases/{get_files_usecase.dart => get_images_usecase.dart} (77%) diff --git a/lib/core/auth_storage/auth_storage.dart b/lib/core/auth_storage/auth_storage.dart index 525eb2e..42b3ff3 100644 --- a/lib/core/auth_storage/auth_storage.dart +++ b/lib/core/auth_storage/auth_storage.dart @@ -19,7 +19,7 @@ class AuthStorage { } static void loadData() { - token = LocalStorage.readData(key: MyConstants.token); + token = LocalStorage.readData(key: MyConstants.token) ?? ''; } static bool isLogin() { diff --git a/lib/core/constants/my_api.dart b/lib/core/constants/my_api.dart index dd59e1f..77e1bfe 100644 --- a/lib/core/constants/my_api.dart +++ b/lib/core/constants/my_api.dart @@ -11,5 +11,6 @@ class MyApi { static const String baseUrl = 'https://hadihoda.newhorizonco.uk/api'; static const String levels = '/quiz/optimized/levels/'; - static const String files = '/quiz/optimized/download-all-files/'; + static const String images = '/quiz/optimized/download-all-files/images/'; + static const String audios = '/quiz/optimized/download-all-files/audio/'; } diff --git a/lib/core/constants/my_constants.dart b/lib/core/constants/my_constants.dart index 09e197e..15c0889 100644 --- a/lib/core/constants/my_constants.dart +++ b/lib/core/constants/my_constants.dart @@ -6,8 +6,10 @@ class MyConstants { static const String token = 'TOKEN'; static const String theme = 'THEME'; static const String levelBox = 'LEVEL_BOX'; - static const String downloadCompleted = 'DOWNLOAD_COMPLETED'; - static const String extractCompleted = 'EXTRACT_COMPLETED'; + static const String downloadedImage = 'DOWNLOADED_IMAGE'; + static const String extractedImage = 'EXTRACTED_IMAGE'; + static const String downloadedAudio = 'DOWNLOADED_AUDIO'; + static const String extractedAudio = 'EXTRACTED_AUDIO'; static const String selectLanguage = 'SELECT_LANGUAGE'; static const String firstLanguagePage = 'FIRST_LANGUAGE_PAGE'; static const String currentLevel = 'CURRENT_LEVEL'; diff --git a/lib/core/routers/my_routes.dart b/lib/core/routers/my_routes.dart index 1c696a2..1260c79 100644 --- a/lib/core/routers/my_routes.dart +++ b/lib/core/routers/my_routes.dart @@ -1,6 +1,8 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import 'package:hadi_hoda_flutter/core/utils/context_provider.dart'; +import 'package:hadi_hoda_flutter/features/download/presentation/bloc/download_bloc.dart'; +import 'package:hadi_hoda_flutter/features/download/presentation/ui/download_page.dart'; import 'package:hadi_hoda_flutter/features/home/presentation/bloc/home_bloc.dart'; import 'package:hadi_hoda_flutter/features/home/presentation/ui/home_page.dart'; import 'package:hadi_hoda_flutter/features/intro/presentation/bloc/intro_bloc.dart'; @@ -22,6 +24,7 @@ class Routes { factory Routes() => _i; static const String introPage = '/intro_page'; + static const String downloadPage = '/download_page'; static const String languagePage = '/language_page'; static const String homePage = '/home_page'; static const String questionPage = '/question_page'; @@ -36,10 +39,18 @@ GoRouter get appPages => GoRouter( name: Routes.introPage, path: Routes.introPage, builder: (context, state) => BlocProvider( - create: (context) => IntroBloc(locator(), locator(), locator()), + create: (context) => IntroBloc(locator(), locator()), child: const IntroPage(), ), ), + GoRoute( + name: Routes.downloadPage, + path: Routes.downloadPage, + builder: (context, state) => BlocProvider( + create: (context) => DownloadBloc(locator(), locator(), locator()), + child: const DownloadPage(), + ), + ), GoRoute( name: Routes.languagePage, path: Routes.languagePage, diff --git a/lib/core/utils/local_storage.dart b/lib/core/utils/local_storage.dart index b44e69b..c6302e7 100644 --- a/lib/core/utils/local_storage.dart +++ b/lib/core/utils/local_storage.dart @@ -15,8 +15,8 @@ class LocalStorage { await _box.setString(key, value); } - static String readData({required String key}) { - return _box.getString(key) ?? ''; + static String? readData({required String key}) { + return _box.getString(key); } static Future deleteData({required String key}) async { diff --git a/lib/features/download/data/datasource/download_datasource.dart b/lib/features/download/data/datasource/download_datasource.dart new file mode 100644 index 0000000..def3308 --- /dev/null +++ b/lib/features/download/data/datasource/download_datasource.dart @@ -0,0 +1,116 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter_archive/flutter_archive.dart'; +import 'package:hadi_hoda_flutter/core/constants/my_api.dart'; +import 'package:hadi_hoda_flutter/core/constants/my_constants.dart'; +import 'package:hadi_hoda_flutter/core/error_handler/my_exception.dart'; +import 'package:hadi_hoda_flutter/core/network/http_request.dart'; +import 'package:hadi_hoda_flutter/core/response/base_response.dart'; +import 'package:hadi_hoda_flutter/core/utils/local_storage.dart'; +import 'package:hadi_hoda_flutter/core/utils/storage_path.dart'; +import 'package:hadi_hoda_flutter/features/download/domain/entities/download_entity.dart'; +import 'package:hadi_hoda_flutter/features/level/data/model/level_model.dart'; +import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart'; +import 'package:hadi_hoda_flutter/features/level/domain/entity/total_data_entity.dart'; +import 'package:hive/hive.dart'; + +abstract class IDownloadDatasource { + Future getAudios(); + Future saveLevels(); + Stream loadingStream(); +} + +class DownloadDatasourceImpl implements IDownloadDatasource { + final IHttpRequest httpRequest; + final StreamController streamController = StreamController.broadcast(); + + DownloadDatasourceImpl(this.httpRequest); + + @override + Future getAudios() async { + final String filePath = '${StoragePath.documentDir.path}/audios.zip'; + final String selectedLanguage = + LocalStorage.readData(key: MyConstants.selectLanguage) ?? 'fa'; + + if(LocalStorage.readData(key: MyConstants.downloadedAudio) != 'true'){ + await httpRequest.download( + urlPath: MyApi.audios, + savePath: filePath, + queryParameters: { + 'lang': selectedLanguage, + }, + onReceive: (count, total) { + streamController.add(DownloadEntity( + count: count / 1, + total: total / 1, + percent: (count / total) * 100, + )); + }, + ).then((value) async { + await LocalStorage.saveData( + key: MyConstants.downloadedAudio, + value: 'true', + ); + },); + } + + try{ + if (LocalStorage.readData(key: MyConstants.extractedAudio) != 'true') { + final File file = File(filePath); + final Directory directory = Directory( + '${StoragePath.documentDir.path}/$selectedLanguage/', + ); + await ZipFile.extractToDirectory( + zipFile: file, + destinationDir: directory, + onExtracting: (zipEntry, progress) { + return ZipFileOperation.includeItem; + }, + ).then((value) async { + await Future.wait([ + LocalStorage.saveData( + key: MyConstants.extractedAudio, + value: 'true', + ), + file.delete(recursive: true), + ]); + }); + } else { + streamController.add(DownloadEntity(percent: 50)); + await Future.delayed(Duration(milliseconds: 150)); + streamController.add(DownloadEntity(percent: 100)); + } + } catch (e){ + throw MyException(errorMessage: '$e'); + } + } + + @override + Future saveLevels() async { + final String selectedLanguage = + LocalStorage.readData(key: MyConstants.selectLanguage) ?? 'fa'; + final Box data = Hive.box(MyConstants.levelBox); + final TotalDataEntity findData = data.values.singleWhere( + (e) => e.code == selectedLanguage, + orElse: () => TotalDataEntity(), + ); + + if (findData.code != selectedLanguage) { + final response = await httpRequest.get( + path: MyApi.levels, + queryParameters: {'lang': selectedLanguage}, + ); + final List levels = BaseResponse.getDataList( + response?['result'], + (json) => LevelModel.fromJson(json), + ); + await Future.wait([ + data.add(TotalDataEntity(code: selectedLanguage, levels: levels)), + ]); + } + } + + @override + Stream loadingStream() => streamController.stream; +} diff --git a/lib/features/download/data/repository_impl/download_repository_impl.dart b/lib/features/download/data/repository_impl/download_repository_impl.dart new file mode 100644 index 0000000..9fd8b54 --- /dev/null +++ b/lib/features/download/data/repository_impl/download_repository_impl.dart @@ -0,0 +1,50 @@ +import 'package:flutter/foundation.dart'; +import 'package:hadi_hoda_flutter/core/error_handler/my_exception.dart'; +import 'package:hadi_hoda_flutter/core/params/no_params.dart'; +import 'package:hadi_hoda_flutter/core/utils/data_state.dart'; +import 'package:hadi_hoda_flutter/features/download/data/datasource/download_datasource.dart'; +import 'package:hadi_hoda_flutter/features/download/domain/entities/download_entity.dart'; +import 'package:hadi_hoda_flutter/features/download/domain/repository/download_repository.dart'; + +class DownloadRepositoryImpl implements IDownloadRepository { + final IDownloadDatasource datasource; + + const DownloadRepositoryImpl(this.datasource); + + @override + Future> getAudios() async { + try { + await datasource.getAudios(); + return DataState.success(NoParams()); + } on MyException catch (e) { + return DataState.error(e); + } catch (e) { + if (kDebugMode) { + rethrow; + } else { + return DataState.error(MyException(errorMessage: '$e')); + } + } + } + + @override + Future> saveLevels() async { + try { + await datasource.saveLevels(); + return DataState.success(NoParams()); + } on MyException catch (e) { + return DataState.error(e); + } catch (e) { + if (kDebugMode) { + rethrow; + } else { + return DataState.error(MyException(errorMessage: '$e')); + } + } + } + + @override + Stream loadingStream() { + return datasource.loadingStream(); + } +} diff --git a/lib/features/intro/domain/entities/download_entity.dart b/lib/features/download/domain/entities/download_entity.dart similarity index 100% rename from lib/features/intro/domain/entities/download_entity.dart rename to lib/features/download/domain/entities/download_entity.dart diff --git a/lib/features/download/domain/repository/download_repository.dart b/lib/features/download/domain/repository/download_repository.dart new file mode 100644 index 0000000..b7ffbd5 --- /dev/null +++ b/lib/features/download/domain/repository/download_repository.dart @@ -0,0 +1,10 @@ +import 'package:hadi_hoda_flutter/core/error_handler/my_exception.dart'; +import 'package:hadi_hoda_flutter/core/params/no_params.dart'; +import 'package:hadi_hoda_flutter/core/utils/data_state.dart'; +import 'package:hadi_hoda_flutter/features/download/domain/entities/download_entity.dart'; + +abstract class IDownloadRepository { + Future> getAudios(); + Future> saveLevels(); + Stream loadingStream(); +} diff --git a/lib/features/download/domain/usecases/get_audios_usecase.dart b/lib/features/download/domain/usecases/get_audios_usecase.dart new file mode 100644 index 0000000..a113927 --- /dev/null +++ b/lib/features/download/domain/usecases/get_audios_usecase.dart @@ -0,0 +1,16 @@ +import 'package:hadi_hoda_flutter/core/error_handler/my_exception.dart'; +import 'package:hadi_hoda_flutter/core/params/no_params.dart'; +import 'package:hadi_hoda_flutter/core/usecase/usecase.dart'; +import 'package:hadi_hoda_flutter/core/utils/data_state.dart'; +import 'package:hadi_hoda_flutter/features/download/domain/repository/download_repository.dart'; + +class GetAudiosUseCase implements UseCase { + final IDownloadRepository repository; + + const GetAudiosUseCase(this.repository); + + @override + Future> call(NoParams params) { + return repository.getAudios(); + } +} diff --git a/lib/features/download/domain/usecases/loading_stream_audio_usecase.dart b/lib/features/download/domain/usecases/loading_stream_audio_usecase.dart new file mode 100644 index 0000000..4af13e0 --- /dev/null +++ b/lib/features/download/domain/usecases/loading_stream_audio_usecase.dart @@ -0,0 +1,12 @@ +import 'package:hadi_hoda_flutter/features/download/domain/entities/download_entity.dart'; +import 'package:hadi_hoda_flutter/features/download/domain/repository/download_repository.dart'; + +class LoadingStreamAudioUseCase { + final IDownloadRepository repository; + + const LoadingStreamAudioUseCase(this.repository); + + Stream call() { + return repository.loadingStream(); + } +} diff --git a/lib/features/intro/domain/usecases/save_levels_usecase.dart b/lib/features/download/domain/usecases/save_levels_usecase.dart similarity index 78% rename from lib/features/intro/domain/usecases/save_levels_usecase.dart rename to lib/features/download/domain/usecases/save_levels_usecase.dart index 87f385b..034e60a 100644 --- a/lib/features/intro/domain/usecases/save_levels_usecase.dart +++ b/lib/features/download/domain/usecases/save_levels_usecase.dart @@ -2,10 +2,10 @@ import 'package:hadi_hoda_flutter/core/error_handler/my_exception.dart'; import 'package:hadi_hoda_flutter/core/params/no_params.dart'; import 'package:hadi_hoda_flutter/core/usecase/usecase.dart'; import 'package:hadi_hoda_flutter/core/utils/data_state.dart'; -import 'package:hadi_hoda_flutter/features/intro/domain/repository/intro_repository.dart'; +import 'package:hadi_hoda_flutter/features/download/domain/repository/download_repository.dart'; class SaveLevelsUseCase implements UseCase { - final IIntroRepository repository; + final IDownloadRepository repository; const SaveLevelsUseCase(this.repository); diff --git a/lib/features/download/presentation/bloc/download_bloc.dart b/lib/features/download/presentation/bloc/download_bloc.dart new file mode 100644 index 0000000..8672d8e --- /dev/null +++ b/lib/features/download/presentation/bloc/download_bloc.dart @@ -0,0 +1,74 @@ +import 'dart:async'; + +import 'package:bloc/bloc.dart'; +import 'package:go_router/go_router.dart'; +import 'package:hadi_hoda_flutter/core/params/no_params.dart'; +import 'package:hadi_hoda_flutter/core/routers/my_routes.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/features/download/domain/entities/download_entity.dart'; +import 'package:hadi_hoda_flutter/features/download/domain/usecases/get_audios_usecase.dart'; +import 'package:hadi_hoda_flutter/features/download/domain/usecases/loading_stream_audio_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._getAudiosUseCase, + this._loadingStreamUseCase, + this._saveLevelsUseCase, + ) + : super(const DownloadState()) { + on(_getAudiosEvent); + on(_saveLevelsEvent); + loadingStream = _loadingStreamUseCase(); + } + + /// ------------UseCases------------ + final GetAudiosUseCase _getAudiosUseCase; + final SaveLevelsUseCase _saveLevelsUseCase; + final LoadingStreamAudioUseCase _loadingStreamUseCase; + + /// ------------Variables------------ + Stream loadingStream = Stream.empty(); + + /// ------------Controllers------------ + + /// ------------Functions------------ + + /// ------------Api Calls------------ + FutureOr _getAudiosEvent( + GetAudiosEvent event, + Emitter emit, + ) async { + emit(state.copyWith(getFilesStatus: BaseInit())); + await _getAudiosUseCase(NoParams()).then((value) { + value.fold( + (data) async { + add(SaveLevelsEvent()); + }, + (error) async { + emit(state.copyWith(getFilesStatus: BaseError(error.errorMessage))); + }, + ); + }); + } + + FutureOr _saveLevelsEvent( + SaveLevelsEvent event, + Emitter emit, + ) async { + await _saveLevelsUseCase(NoParams()).then((value) => + value.fold( + (data) async { + ContextProvider.context.goNamed(Routes.homePage); + }, + (error) { + emit(state.copyWith(getFilesStatus: BaseError(error.errorMessage))); + }, + ), + ); + } +} diff --git a/lib/features/download/presentation/bloc/download_event.dart b/lib/features/download/presentation/bloc/download_event.dart new file mode 100644 index 0000000..9d05b54 --- /dev/null +++ b/lib/features/download/presentation/bloc/download_event.dart @@ -0,0 +1,5 @@ +sealed class DownloadEvent { + const DownloadEvent(); +} +class GetAudiosEvent extends DownloadEvent {} +class SaveLevelsEvent extends DownloadEvent {} diff --git a/lib/features/download/presentation/bloc/download_state.dart b/lib/features/download/presentation/bloc/download_state.dart new file mode 100644 index 0000000..6ed7abc --- /dev/null +++ b/lib/features/download/presentation/bloc/download_state.dart @@ -0,0 +1,15 @@ +import 'package:hadi_hoda_flutter/core/status/base_status.dart'; + +class DownloadState { + final BaseStatus getFilesStatus; + + const DownloadState({this.getFilesStatus = const BaseInit()}); + + DownloadState copyWith({ + BaseStatus? getFilesStatus, + }) { + return DownloadState( + getFilesStatus: getFilesStatus ?? this.getFilesStatus, + ); + } +} diff --git a/lib/features/download/presentation/ui/download_page.dart b/lib/features/download/presentation/ui/download_page.dart new file mode 100644 index 0000000..c7533b3 --- /dev/null +++ b/lib/features/download/presentation/ui/download_page.dart @@ -0,0 +1,130 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; +import 'package:hadi_hoda_flutter/common_ui/resources/my_colors.dart'; +import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart'; +import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart'; +import 'package:hadi_hoda_flutter/core/status/base_status.dart'; +import 'package:hadi_hoda_flutter/core/utils/convert_size.dart'; +import 'package:hadi_hoda_flutter/core/utils/my_image.dart'; +import 'package:hadi_hoda_flutter/core/utils/my_localization.dart'; +import 'package:hadi_hoda_flutter/core/utils/screen_size.dart'; +import 'package:hadi_hoda_flutter/core/widgets/error/error_state.dart'; +import 'package:hadi_hoda_flutter/features/download/domain/entities/download_entity.dart'; +import 'package:hadi_hoda_flutter/features/download/presentation/bloc/download_bloc.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'; +import 'package:hadi_hoda_flutter/features/download/presentation/ui/widgets/download_loading_widget.dart'; + +class DownloadPage extends StatefulWidget { + const DownloadPage({super.key}); + + @override + State createState() => _DownloadPageState(); +} + +class _DownloadPageState extends State { + @override + void initState() { + super.initState(); + context.read().add(GetAudiosEvent()); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Container( + height: context.heightScreen, + width: context.widthScreen, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Color(0XFF00154C), + Color(0XFF150532), + ], + ), + image: DecorationImage( + image: AssetImage(MyAssets.pattern), + scale: 3, + repeat: ImageRepeat.repeat, + colorFilter: ColorFilter.mode( + Colors.white.withValues(alpha: 0.2), + BlendMode.srcIn, + ), + ), + ), + child: BlocBuilder( + buildWhen: (previous, current) => + previous.getFilesStatus != current.getFilesStatus, + builder: (context, state) { + if (state.getFilesStatus is BaseError) { + return Padding( + padding: EdgeInsets.symmetric( + vertical: MediaQuery.viewPaddingOf(context).bottom + MySpaces.s16, + horizontal: 60, + ), + child: ErrorState( + onTap: () => context.read().add(GetAudiosEvent()), + ), + ); + } else { + return Stack( + alignment: Alignment.center, + children: [ + _image(), + _text(context), + _loading(context), + ], + ); + } + } + ), + ), + ); + } + + MyImage _image() { + return MyImage( + image: MyAssets.hadiHoda, + size: 200, + ); + } + + Widget _text(BuildContext context) { + return PositionedDirectional( + bottom: 130, + child: Column( + spacing: MySpaces.s6, + children: [ + Text( + context.translate.please_wait, + style: Marhey.medium22.copyWith( + color: MyColors.white, + ), + ), + StreamBuilder( + initialData: DownloadEntity(), + stream: context.read().loadingStream, + builder: (context, snapshot) => Text( + '${context.translate.downloading_data} (${snapshot.data?.count.toMB ?? 0.0}mb / ${snapshot.data?.total.toMB ?? 0.0}mb)', + style: Marhey.medium12.copyWith( + color: MyColors.white, + ), + ), + ), + ], + ), + ); + } + + Positioned _loading(BuildContext context) { + return Positioned( + bottom: MediaQuery.viewPaddingOf(context).bottom + MySpaces.s16, + child: DownloadLoadingWidget( + loadingStream: context.read().loadingStream, + ), + ); + } +} diff --git a/lib/features/download/presentation/ui/widgets/download_loading_widget.dart b/lib/features/download/presentation/ui/widgets/download_loading_widget.dart new file mode 100644 index 0000000..ab69553 --- /dev/null +++ b/lib/features/download/presentation/ui/widgets/download_loading_widget.dart @@ -0,0 +1,136 @@ +import 'package:flutter/material.dart'; +import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart'; +import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart'; +import 'package:hadi_hoda_flutter/features/download/domain/entities/download_entity.dart'; + + +class DownloadLoadingWidget extends StatelessWidget { + const DownloadLoadingWidget({ + super.key, + this.loadingStream, + }); + + final Stream? loadingStream; + + @override + Widget build(BuildContext context) { + return ClipPath( + clipper: BubbleClip(), + child: Container( + width: 300, + height: 60, + padding: EdgeInsets.symmetric( + vertical: MySpaces.s4, + horizontal: MySpaces.s2, + ), + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment(0, 1), // bottom + end: Alignment(0, -1), // top + colors: [ + Color(0xFFCADCFF), // #CADCFF + Colors.white, // #FFFFFF + ], + ), + ), + child: StreamBuilder( + initialData: DownloadEntity(), + stream: loadingStream, + builder: (context, snapshot) { + return Row( + children: [ + Expanded( + flex: 85, + child: ClipPath( + clipper: BubbleClip(), + child: AnimatedContainer( + duration: const Duration(milliseconds: 300), + padding: EdgeInsetsDirectional.only( + end: 260 - ((snapshot.data?.percent ?? 0) * 260 / 100), + ), + decoration: BoxDecoration( + color: Color(0xFF1F59BD).withValues(alpha: 0.25), + ), + child: ClipPath( + clipper: BubbleClip(), + child: Container( + decoration: BoxDecoration( + gradient: RadialGradient( + radius: 2, + colors: [ + Color(0xFFFFBD00), // #CADCFF + Color(0xFFFF772C), // #CADCFF + ], + ), + ), + ), + ), + ), + ), + ), + Expanded( + flex: 15, + child: Center( + child: Text( + '${snapshot.data?.percent?.toInt() ?? 0}%', + style: DinoKids.regular17.copyWith( + color: Color(0XFF6E83A8), + ), + ), + ), + ), + ], + ); + } + ), + ), + ); + } +} + +class BubbleClip extends CustomClipper { + @override + Path getClip(Size size) { + // Original SVG viewBox: 334 x 60 + const double w0 = 334.0; + const double h0 = 60.0; + final sx = size.width / w0; + final sy = size.height / h0; + + // SVG path: + // M9.82057 10.3597 + // C -1.70838 17.1589 -3.47995 44.4301 6.60447 53.1719 + // C 16.0075 61.291 305.076 61.9385 323.201 53.4956 + // C 341.326 45.0527 332.116 8.04571 324.829 5.7273 + // C 307.985 -2.06805 28.6539 -0.77294 9.82057 10.3597 + // Z + final p = Path() + ..moveTo(9.82057 * sx, 10.3597 * sy) + ..cubicTo( + -1.70838 * sx, 17.1589 * sy, + -3.47995 * sx, 44.4301 * sy, + 6.60447 * sx, 53.1719 * sy, + ) + ..cubicTo( + 16.0075 * sx, 61.291 * sy, + 305.076 * sx, 61.9385 * sy, + 323.201 * sx, 53.4956 * sy, + ) + ..cubicTo( + 341.326 * sx, 45.0527 * sy, + 332.116 * sx, 8.04571 * sy, + 324.829 * sx, 5.7273 * sy, + ) + ..cubicTo( + 307.985 * sx, -2.06805 * sy, + 28.6539 * sx, -0.77294 * sy, + 9.82057 * sx, 10.3597 * sy, + ) + ..close(); + + return p; + } + + @override + bool shouldReclip(covariant CustomClipper oldClipper) => false; +} diff --git a/lib/features/intro/data/datasource/intro_datasource.dart b/lib/features/intro/data/datasource/intro_datasource.dart index d43a046..7a48b46 100644 --- a/lib/features/intro/data/datasource/intro_datasource.dart +++ b/lib/features/intro/data/datasource/intro_datasource.dart @@ -6,18 +6,12 @@ import 'package:hadi_hoda_flutter/core/constants/my_api.dart'; import 'package:hadi_hoda_flutter/core/constants/my_constants.dart'; import 'package:hadi_hoda_flutter/core/error_handler/my_exception.dart'; import 'package:hadi_hoda_flutter/core/network/http_request.dart'; -import 'package:hadi_hoda_flutter/core/response/base_response.dart'; import 'package:hadi_hoda_flutter/core/utils/local_storage.dart'; import 'package:hadi_hoda_flutter/core/utils/storage_path.dart'; -import 'package:hadi_hoda_flutter/features/intro/domain/entities/download_entity.dart'; -import 'package:hadi_hoda_flutter/features/level/data/model/level_model.dart'; -import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart'; -import 'package:hadi_hoda_flutter/features/level/domain/entity/total_data_entity.dart'; -import 'package:hive/hive.dart'; +import 'package:hadi_hoda_flutter/features/download/domain/entities/download_entity.dart'; abstract class IIntroDatasource { - Future getFiles(); - Future saveLevels(); + Future getImages(); Stream loadingStream(); } @@ -28,20 +22,13 @@ class IntroDatasourceImpl implements IIntroDatasource { IntroDatasourceImpl(this.httpRequest); @override - Future getFiles() async { - final String filePath = '${StoragePath.documentDir.path}/files.zip'; - final String selectedLanguage = - LocalStorage.readData(key: MyConstants.selectLanguage).isEmpty - ? 'fa' - : LocalStorage.readData(key: MyConstants.selectLanguage); + Future getImages() async { + final String filePath = '${StoragePath.documentDir.path}/images.zip'; - if (LocalStorage.readData(key: MyConstants.downloadCompleted) != 'true') { + if (LocalStorage.readData(key: MyConstants.downloadedImage) != 'true') { await httpRequest.download( - urlPath: MyApi.files, + urlPath: MyApi.images, savePath: filePath, - queryParameters: { - 'lang': selectedLanguage, - }, onReceive: (count, total) { streamController.add(DownloadEntity( count: count / 1, @@ -51,16 +38,16 @@ class IntroDatasourceImpl implements IIntroDatasource { }, ).then((value) async { await LocalStorage.saveData( - key: MyConstants.downloadCompleted, + key: MyConstants.downloadedImage, value: 'true', ); }); } try{ - if (LocalStorage.readData(key: MyConstants.extractCompleted) != 'true') { + if (LocalStorage.readData(key: MyConstants.extractedImage) != 'true') { final File file = File(filePath); - final Directory directory = Directory('${StoragePath.documentDir.path}/$selectedLanguage/files/'); + final Directory directory = Directory('${StoragePath.documentDir.path}/'); await ZipFile.extractToDirectory( zipFile: file, destinationDir: directory, @@ -70,7 +57,7 @@ class IntroDatasourceImpl implements IIntroDatasource { ).then((value) async { await Future.wait([ LocalStorage.saveData( - key: MyConstants.extractCompleted, + key: MyConstants.extractedImage, value: 'true', ), file.delete(recursive: true), @@ -86,33 +73,6 @@ class IntroDatasourceImpl implements IIntroDatasource { } } - @override - Future saveLevels() async { - final String selectedLanguage = - LocalStorage.readData(key: MyConstants.selectLanguage).isEmpty - ? 'fa' - : LocalStorage.readData(key: MyConstants.selectLanguage); - final Box data = Hive.box(MyConstants.levelBox); - final TotalDataEntity findData = data.values.singleWhere( - (e) => e.code == selectedLanguage, - orElse: () => TotalDataEntity(), - ); - - if (findData.code != selectedLanguage) { - final response = await httpRequest.get( - path: MyApi.levels, - queryParameters: {'lang': selectedLanguage}, - ); - final List levels = BaseResponse.getDataList( - response?['result'], - (json) => LevelModel.fromJson(json), - ); - await Future.wait([ - data.add(TotalDataEntity(code: selectedLanguage, levels: levels)), - ]); - } - } - @override Stream loadingStream() => streamController.stream; } diff --git a/lib/features/intro/data/repository_impl/intro_repository_impl.dart b/lib/features/intro/data/repository_impl/intro_repository_impl.dart index fbae565..f6e5c0d 100644 --- a/lib/features/intro/data/repository_impl/intro_repository_impl.dart +++ b/lib/features/intro/data/repository_impl/intro_repository_impl.dart @@ -2,8 +2,8 @@ import 'package:flutter/foundation.dart'; import 'package:hadi_hoda_flutter/core/error_handler/my_exception.dart'; import 'package:hadi_hoda_flutter/core/params/no_params.dart'; import 'package:hadi_hoda_flutter/core/utils/data_state.dart'; +import 'package:hadi_hoda_flutter/features/download/domain/entities/download_entity.dart'; import 'package:hadi_hoda_flutter/features/intro/data/datasource/intro_datasource.dart'; -import 'package:hadi_hoda_flutter/features/intro/domain/entities/download_entity.dart'; import 'package:hadi_hoda_flutter/features/intro/domain/repository/intro_repository.dart'; class IntroRepositoryImpl implements IIntroRepository { @@ -12,25 +12,9 @@ class IntroRepositoryImpl implements IIntroRepository { const IntroRepositoryImpl(this.datasource); @override - Future> getFiles() async { + Future> getImages() async { try { - await datasource.getFiles(); - return DataState.success(NoParams()); - } on MyException catch (e) { - return DataState.error(e); - } catch (e) { - if (kDebugMode) { - rethrow; - } else { - return DataState.error(MyException(errorMessage: '$e')); - } - } - } - - @override - Future> saveLevels() async { - try { - await datasource.saveLevels(); + await datasource.getImages(); return DataState.success(NoParams()); } on MyException catch (e) { return DataState.error(e); diff --git a/lib/features/intro/domain/repository/intro_repository.dart b/lib/features/intro/domain/repository/intro_repository.dart index adb6554..4ee9181 100644 --- a/lib/features/intro/domain/repository/intro_repository.dart +++ b/lib/features/intro/domain/repository/intro_repository.dart @@ -1,10 +1,9 @@ import 'package:hadi_hoda_flutter/core/error_handler/my_exception.dart'; import 'package:hadi_hoda_flutter/core/params/no_params.dart'; import 'package:hadi_hoda_flutter/core/utils/data_state.dart'; -import 'package:hadi_hoda_flutter/features/intro/domain/entities/download_entity.dart'; +import 'package:hadi_hoda_flutter/features/download/domain/entities/download_entity.dart'; abstract class IIntroRepository { - Future> getFiles(); - Future> saveLevels(); + Future> getImages(); Stream loadingStream(); } diff --git a/lib/features/intro/domain/usecases/get_files_usecase.dart b/lib/features/intro/domain/usecases/get_images_usecase.dart similarity index 77% rename from lib/features/intro/domain/usecases/get_files_usecase.dart rename to lib/features/intro/domain/usecases/get_images_usecase.dart index ca5c7ea..3774894 100644 --- a/lib/features/intro/domain/usecases/get_files_usecase.dart +++ b/lib/features/intro/domain/usecases/get_images_usecase.dart @@ -4,13 +4,13 @@ import 'package:hadi_hoda_flutter/core/usecase/usecase.dart'; import 'package:hadi_hoda_flutter/core/utils/data_state.dart'; import 'package:hadi_hoda_flutter/features/intro/domain/repository/intro_repository.dart'; -class GetFilesUseCase implements UseCase { +class GetImagesUseCase implements UseCase { final IIntroRepository repository; - const GetFilesUseCase(this.repository); + const GetImagesUseCase(this.repository); @override Future> call(NoParams params) { - return repository.getFiles(); + return repository.getImages(); } } diff --git a/lib/features/intro/domain/usecases/loading_stream_usecase.dart b/lib/features/intro/domain/usecases/loading_stream_usecase.dart index 5c44199..1d81d89 100644 --- a/lib/features/intro/domain/usecases/loading_stream_usecase.dart +++ b/lib/features/intro/domain/usecases/loading_stream_usecase.dart @@ -1,4 +1,4 @@ -import 'package:hadi_hoda_flutter/features/intro/domain/entities/download_entity.dart'; +import 'package:hadi_hoda_flutter/features/download/domain/entities/download_entity.dart'; import 'package:hadi_hoda_flutter/features/intro/domain/repository/intro_repository.dart'; class LoadingStreamUseCase { diff --git a/lib/features/intro/presentation/bloc/intro_bloc.dart b/lib/features/intro/presentation/bloc/intro_bloc.dart index 8ff8483..0bc25e2 100644 --- a/lib/features/intro/presentation/bloc/intro_bloc.dart +++ b/lib/features/intro/presentation/bloc/intro_bloc.dart @@ -8,29 +8,25 @@ import 'package:hadi_hoda_flutter/core/routers/my_routes.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/features/intro/domain/entities/download_entity.dart'; -import 'package:hadi_hoda_flutter/features/intro/domain/usecases/get_files_usecase.dart'; +import 'package:hadi_hoda_flutter/features/download/domain/entities/download_entity.dart'; +import 'package:hadi_hoda_flutter/features/intro/domain/usecases/get_images_usecase.dart'; import 'package:hadi_hoda_flutter/features/intro/domain/usecases/loading_stream_usecase.dart'; -import 'package:hadi_hoda_flutter/features/intro/domain/usecases/save_levels_usecase.dart'; import 'package:hadi_hoda_flutter/features/intro/presentation/bloc/intro_event.dart'; import 'package:hadi_hoda_flutter/features/intro/presentation/bloc/intro_state.dart'; class IntroBloc extends Bloc { /// ------------constructor------------ IntroBloc( - this._getFilesUseCase, + this._getImagesUseCase, this._loadingStreamUseCase, - this._saveLevelsUseCase, ) : super(const IntroState()) { - on(_getFilesEvent); - on(_saveLevelsEvent); + on(_getImagesEvent); loadingStream = _loadingStreamUseCase(); } /// ------------UseCases------------ - final GetFilesUseCase _getFilesUseCase; - final SaveLevelsUseCase _saveLevelsUseCase; + final GetImagesUseCase _getImagesUseCase; final LoadingStreamUseCase _loadingStreamUseCase; /// ------------Variables------------ @@ -41,15 +37,23 @@ class IntroBloc extends Bloc { /// ------------Functions------------ /// ------------Api Calls------------ - FutureOr _getFilesEvent( - GetFilesEvent event, + FutureOr _getImagesEvent( + GetImagesEvent event, Emitter emit, ) async { emit(state.copyWith(getFilesStatus: BaseInit())); - await _getFilesUseCase(NoParams()).then((value) { + await _getImagesUseCase(NoParams()).then((value) { value.fold( (data) async { - add(SaveLevelsEvent()); + if(LocalStorage.readData(key: MyConstants.firstLanguagePage) != 'true'){ + ContextProvider.context.goNamed(Routes.languagePage); + } else { + await Future.delayed(Duration(milliseconds: 500), () { + if (ContextProvider.context.mounted) { + ContextProvider.context.goNamed(Routes.homePage); + } + }); + } }, (error) async { emit(state.copyWith(getFilesStatus: BaseError(error.errorMessage))); @@ -57,26 +61,4 @@ class IntroBloc extends Bloc { ); }); } - - FutureOr _saveLevelsEvent(SaveLevelsEvent event, - Emitter emit) async { - await _saveLevelsUseCase(NoParams()).then((value) => - value.fold( - (data) async { - if(LocalStorage.readData(key: MyConstants.firstLanguagePage) != 'true'){ - ContextProvider.context.goNamed(Routes.languagePage); - } else { - await Future.delayed(Duration(milliseconds: 500), () { - if (ContextProvider.context.mounted) { - ContextProvider.context.goNamed(Routes.homePage); - } - }); - } - }, - (error) { - emit(state.copyWith(getFilesStatus: BaseError(error.errorMessage))); - }, - ), - ); - } } diff --git a/lib/features/intro/presentation/bloc/intro_event.dart b/lib/features/intro/presentation/bloc/intro_event.dart index 271b2a0..610637a 100644 --- a/lib/features/intro/presentation/bloc/intro_event.dart +++ b/lib/features/intro/presentation/bloc/intro_event.dart @@ -1,5 +1,4 @@ sealed class IntroEvent { const IntroEvent(); } -class GetFilesEvent extends IntroEvent {} -class SaveLevelsEvent extends IntroEvent {} +class GetImagesEvent extends IntroEvent {} diff --git a/lib/features/intro/presentation/ui/intro_page.dart b/lib/features/intro/presentation/ui/intro_page.dart index bec6972..8d0d38b 100644 --- a/lib/features/intro/presentation/ui/intro_page.dart +++ b/lib/features/intro/presentation/ui/intro_page.dart @@ -10,7 +10,7 @@ import 'package:hadi_hoda_flutter/core/utils/my_image.dart'; import 'package:hadi_hoda_flutter/core/utils/my_localization.dart'; import 'package:hadi_hoda_flutter/core/utils/screen_size.dart'; import 'package:hadi_hoda_flutter/core/widgets/error/error_state.dart'; -import 'package:hadi_hoda_flutter/features/intro/domain/entities/download_entity.dart'; +import 'package:hadi_hoda_flutter/features/download/domain/entities/download_entity.dart'; import 'package:hadi_hoda_flutter/features/intro/presentation/bloc/intro_bloc.dart'; import 'package:hadi_hoda_flutter/features/intro/presentation/bloc/intro_event.dart'; import 'package:hadi_hoda_flutter/features/intro/presentation/bloc/intro_state.dart'; @@ -27,7 +27,7 @@ class _IntroPageState extends State { @override void initState() { super.initState(); - context.read().add(GetFilesEvent()); + context.read().add(GetImagesEvent()); } @override @@ -66,7 +66,7 @@ class _IntroPageState extends State { horizontal: 60, ), child: ErrorState( - onTap: () => context.read().add(GetFilesEvent()), + onTap: () => context.read().add(GetImagesEvent()), ), ); } else { diff --git a/lib/features/intro/presentation/ui/widgets/intro_loading_widget.dart b/lib/features/intro/presentation/ui/widgets/intro_loading_widget.dart index 3d2667b..12e4568 100644 --- a/lib/features/intro/presentation/ui/widgets/intro_loading_widget.dart +++ b/lib/features/intro/presentation/ui/widgets/intro_loading_widget.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart'; -import 'package:hadi_hoda_flutter/features/intro/domain/entities/download_entity.dart'; +import 'package:hadi_hoda_flutter/features/download/domain/entities/download_entity.dart'; class IntroLoadingWidget extends StatelessWidget { diff --git a/lib/features/language/presentation/bloc/language_bloc.dart b/lib/features/language/presentation/bloc/language_bloc.dart index 2dbf38f..364160a 100644 --- a/lib/features/language/presentation/bloc/language_bloc.dart +++ b/lib/features/language/presentation/bloc/language_bloc.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:io'; + import 'package:bloc/bloc.dart'; import 'package:go_router/go_router.dart'; import 'package:hadi_hoda_flutter/core/constants/my_constants.dart'; @@ -14,7 +15,6 @@ import 'package:hadi_hoda_flutter/features/language/presentation/bloc/language_s class LanguageBloc extends Bloc { /// ------------constructor------------ LanguageBloc() : super(const LanguageState()) { - LocalStorage.saveData(key: MyConstants.firstLanguagePage, value: 'true'); on(_changeLanguageEvent); on(_saveLevelsEvent); on(_initLanguageEvent); @@ -44,31 +44,34 @@ class LanguageBloc extends Bloc { SaveLevelsEvent event, Emitter emit, ) async { - await LocalStorage.saveData( - key: MyConstants.selectLanguage, - value: state.selectedLang.code ?? 'fa', - ); + await Future.wait([ + LocalStorage.saveData( + key: MyConstants.selectLanguage, + value: state.selectedLang.code ?? 'fa', + ), + LocalStorage.saveData(key: MyConstants.firstLanguagePage, value: 'true') + ]); + if (Directory( - '${StoragePath.documentDir.path}/${state.selectedLang.code}/files') + '${StoragePath.documentDir.path}/${state.selectedLang.code}/audio') .existsSync()) { if (ContextProvider.context.mounted) { ContextProvider.context.goNamed(Routes.homePage); } } else { await Future.wait([ - LocalStorage.deleteData(key: MyConstants.downloadCompleted), - LocalStorage.deleteData(key: MyConstants.extractCompleted), + LocalStorage.deleteData(key: MyConstants.downloadedAudio), + LocalStorage.deleteData(key: MyConstants.extractedAudio), ]); if (ContextProvider.context.mounted) { - ContextProvider.context.goNamed(Routes.introPage); + ContextProvider.context.goNamed(Routes.downloadPage); } } } FutureOr _initLanguageEvent(InitLanguageEvent event, Emitter emit) { - final String selectedLanguage = LocalStorage.readData(key: MyConstants.selectLanguage).isEmpty - ? 'fa' - : LocalStorage.readData(key: MyConstants.selectLanguage); + final String selectedLanguage = LocalStorage.readData( + key: MyConstants.selectLanguage) ?? 'fa'; emit(state.copyWith(selectedLang: LanguageEntity(code: selectedLanguage))); } } diff --git a/lib/features/level/data/datasource/level_datasource.dart b/lib/features/level/data/datasource/level_datasource.dart index a1b6bea..a8fb58b 100644 --- a/lib/features/level/data/datasource/level_datasource.dart +++ b/lib/features/level/data/datasource/level_datasource.dart @@ -18,7 +18,7 @@ class LocalLevelDatasourceImpl implements ILevelDatasource { Future> getLevels({required LevelParams params}) async { try { final String selectedLanguage = LocalStorage.readData( - key: MyConstants.selectLanguage); + key: MyConstants.selectLanguage) ?? 'fa'; final Box levelBox = Hive.box(MyConstants.levelBox); final TotalDataEntity findData = levelBox.values.singleWhere( (e) => e.code == selectedLanguage, diff --git a/lib/features/level/presentation/bloc/level_bloc.dart b/lib/features/level/presentation/bloc/level_bloc.dart index 29c071b..ee6461e 100644 --- a/lib/features/level/presentation/bloc/level_bloc.dart +++ b/lib/features/level/presentation/bloc/level_bloc.dart @@ -1,4 +1,5 @@ import 'dart:async'; + import 'package:bloc/bloc.dart'; import 'package:flutter/cupertino.dart'; import 'package:go_router/go_router.dart'; @@ -83,7 +84,7 @@ class LevelBloc extends Bloc { LevelType getLevelType(int index) { final int currentLevel = int.parse( - LocalStorage.readData(key: MyConstants.currentLevel), + LocalStorage.readData(key: MyConstants.currentLevel) ?? '1', ); if (index < currentLevel) { @@ -99,7 +100,7 @@ class LevelBloc extends Bloc { FutureOr _getLevelListEvent(GetLevelListEvent event, Emitter emit) async { final int currentLevel = int.parse( - LocalStorage.readData(key: MyConstants.currentLevel), + LocalStorage.readData(key: MyConstants.currentLevel) ?? '1', ); await _getLeveslUseCase(LevelParams()).then((value) { value.fold( @@ -124,7 +125,7 @@ class LevelBloc extends Bloc { Emitter emit, ) async { final int currentLevel = int.parse( - LocalStorage.readData(key: MyConstants.currentLevel), + LocalStorage.readData(key: MyConstants.currentLevel) ?? '1', ); await Future.delayed(const Duration(seconds: 1)); @@ -147,9 +148,9 @@ class LevelBloc extends Bloc { FutureOr _setCurrentLevelEvent(SetCurrentLevelEvent event, Emitter emit) async { - final String currentLevel = LocalStorage.readData( + final String? currentLevel = LocalStorage.readData( key: MyConstants.currentLevel); - if (currentLevel.isEmpty) { + if (currentLevel == null || currentLevel.isEmpty) { await LocalStorage.saveData(key: MyConstants.currentLevel, value: '1'); } add(GetLevelListEvent()); diff --git a/lib/features/question/data/datasource/question_datasource.dart b/lib/features/question/data/datasource/question_datasource.dart index f83a2b7..cb4357c 100644 --- a/lib/features/question/data/datasource/question_datasource.dart +++ b/lib/features/question/data/datasource/question_datasource.dart @@ -18,7 +18,7 @@ class QuestionDatasourceImpl implements IQuestionDatasource { Future getLevel({required QuestionParams params}) async { try { final String selectedLanguage = LocalStorage.readData( - key: MyConstants.selectLanguage); + key: MyConstants.selectLanguage) ?? 'fa'; final Box levelBox = Hive.box(MyConstants.levelBox); final TotalDataEntity findData = levelBox.values.singleWhere( (e) => e.code == selectedLanguage, diff --git a/lib/init_bindings.dart b/lib/init_bindings.dart index 444b3d7..e936c1b 100644 --- a/lib/init_bindings.dart +++ b/lib/init_bindings.dart @@ -1,14 +1,20 @@ import 'dart:io'; +import 'package:get_it/get_it.dart'; import 'package:hadi_hoda_flutter/core/constants/my_constants.dart'; import 'package:hadi_hoda_flutter/core/network/http_request.dart'; import 'package:hadi_hoda_flutter/core/network/http_request_impl.dart'; +import 'package:hadi_hoda_flutter/features/download/data/datasource/download_datasource.dart'; +import 'package:hadi_hoda_flutter/features/download/data/repository_impl/download_repository_impl.dart'; +import 'package:hadi_hoda_flutter/features/download/domain/repository/download_repository.dart'; +import 'package:hadi_hoda_flutter/features/download/domain/usecases/get_audios_usecase.dart'; +import 'package:hadi_hoda_flutter/features/download/domain/usecases/loading_stream_audio_usecase.dart'; +import 'package:hadi_hoda_flutter/features/download/domain/usecases/save_levels_usecase.dart'; import 'package:hadi_hoda_flutter/features/intro/data/datasource/intro_datasource.dart'; import 'package:hadi_hoda_flutter/features/intro/data/repository_impl/intro_repository_impl.dart'; import 'package:hadi_hoda_flutter/features/intro/domain/repository/intro_repository.dart'; -import 'package:hadi_hoda_flutter/features/intro/domain/usecases/get_files_usecase.dart'; +import 'package:hadi_hoda_flutter/features/intro/domain/usecases/get_images_usecase.dart'; import 'package:hadi_hoda_flutter/features/intro/domain/usecases/loading_stream_usecase.dart'; -import 'package:hadi_hoda_flutter/features/intro/domain/usecases/save_levels_usecase.dart'; import 'package:hadi_hoda_flutter/features/level/data/datasource/level_datasource.dart'; import 'package:hadi_hoda_flutter/features/level/data/repository_impl/level_repository_impl.dart'; import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart'; @@ -26,7 +32,6 @@ import 'package:hadi_hoda_flutter/features/sample/data/datasource/sample_datasou import 'package:hadi_hoda_flutter/features/sample/data/repository_impl/sample_repository_impl.dart'; import 'package:hadi_hoda_flutter/features/sample/domain/repository/sample_repository.dart'; import 'package:hadi_hoda_flutter/features/sample/domain/usecases/get_sample_usecase.dart'; -import 'package:get_it/get_it.dart'; import 'package:hive/hive.dart'; import 'package:path_provider/path_provider.dart'; @@ -44,10 +49,16 @@ void initBindings() { /// Intro Feature locator.registerLazySingleton(() => IntroDatasourceImpl(locator())); locator.registerLazySingleton(() => IntroRepositoryImpl(locator())); - locator.registerLazySingleton(() => GetFilesUseCase(locator())); - locator.registerLazySingleton(() => SaveLevelsUseCase(locator())); + locator.registerLazySingleton(() => GetImagesUseCase(locator())); locator.registerLazySingleton(() => LoadingStreamUseCase(locator())); + /// Download Feature + locator.registerLazySingleton(() => DownloadDatasourceImpl(locator())); + locator.registerLazySingleton(() => DownloadRepositoryImpl(locator())); + locator.registerLazySingleton(() => GetAudiosUseCase(locator())); + locator.registerLazySingleton(() => SaveLevelsUseCase(locator())); + locator.registerLazySingleton(() => LoadingStreamAudioUseCase(locator())); + /// Language Feature /// Home Feature diff --git a/lib/main.dart b/lib/main.dart index 7740c6e..dae1cbf 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,13 +1,12 @@ -import 'package:hadi_hoda_flutter/common_ui/theme/theme_service.dart'; -import 'package:hadi_hoda_flutter/core/utils/local_storage.dart'; -import 'package:hadi_hoda_flutter/core/utils/storage_path.dart'; -import 'package:hadi_hoda_flutter/l10n/app_localizations.dart'; -import 'package:hadi_hoda_flutter/init_bindings.dart'; import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:hadi_hoda_flutter/common_ui/theme/my_theme.dart'; -import 'package:hadi_hoda_flutter/core/auth_storage/auth_storage.dart'; +import 'package:hadi_hoda_flutter/common_ui/theme/theme_service.dart'; import 'package:hadi_hoda_flutter/core/routers/my_routes.dart'; +import 'package:hadi_hoda_flutter/core/utils/local_storage.dart'; +import 'package:hadi_hoda_flutter/core/utils/storage_path.dart'; +import 'package:hadi_hoda_flutter/init_bindings.dart'; +import 'package:hadi_hoda_flutter/l10n/app_localizations.dart'; Future main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -17,7 +16,6 @@ Future main() async { StoragePath.getDocumentDir(), initDataBase(), ]); - AuthStorage.loadData(); runApp(const MainApp()); } -- 2.30.2