From 789f74b93d1f4b0a193ce73642421ca842a979a3 Mon Sep 17 00:00:00 2001 From: AmirrezaChegini Date: Sat, 11 Oct 2025 14:33:22 +0330 Subject: [PATCH] fix: download flow --- assets/images/loading.png | Bin 0 -> 8419 bytes lib/core/constants/my_constants.dart | 2 +- lib/core/middlewares/my_middlewares.dart | 23 +++ lib/core/routers/my_routes.dart | 12 +- .../widgets/animations/rotation_anim.dart | 50 +++++++ .../data/datasource/download_datasource.dart | 59 ++++++-- .../download_repository_impl.dart | 16 +++ .../repository/download_repository.dart | 1 + .../domain/usecases/get_images_usecase.dart | 4 +- ...ecase.dart => loading_stream_usecase.dart} | 4 +- .../presentation/bloc/download_bloc.dart | 31 +++- .../presentation/bloc/download_event.dart | 1 + .../presentation/ui/download_page.dart | 15 +- .../home/presentation/bloc/home_bloc.dart | 16 ++- .../data/datasource/intro_datasource.dart | 78 ---------- .../intro_repository_impl.dart | 34 ----- .../domain/repository/intro_repository.dart | 9 -- .../usecases/loading_stream_usecase.dart | 12 -- .../intro/presentation/bloc/intro_event.dart | 1 - .../ui/widgets/intro_loading_widget.dart | 136 ------------------ .../presentation/bloc/language_bloc.dart | 1 - lib/init_bindings.dart | 15 +- 22 files changed, 206 insertions(+), 314 deletions(-) create mode 100644 assets/images/loading.png create mode 100644 lib/core/middlewares/my_middlewares.dart create mode 100644 lib/core/widgets/animations/rotation_anim.dart rename lib/features/{intro => download}/domain/usecases/get_images_usecase.dart (78%) rename lib/features/download/domain/usecases/{loading_stream_audio_usecase.dart => loading_stream_usecase.dart} (78%) delete mode 100644 lib/features/intro/data/datasource/intro_datasource.dart delete mode 100644 lib/features/intro/data/repository_impl/intro_repository_impl.dart delete mode 100644 lib/features/intro/domain/repository/intro_repository.dart delete mode 100644 lib/features/intro/domain/usecases/loading_stream_usecase.dart delete mode 100644 lib/features/intro/presentation/ui/widgets/intro_loading_widget.dart diff --git a/assets/images/loading.png b/assets/images/loading.png new file mode 100644 index 0000000000000000000000000000000000000000..cf919725c9f6fe30f7cfe79b94bf5f317f6f422f GIT binary patch literal 8419 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 A#{d8T literal 0 HcmV?d00001 diff --git a/lib/core/constants/my_constants.dart b/lib/core/constants/my_constants.dart index 15c0889..c157bdd 100644 --- a/lib/core/constants/my_constants.dart +++ b/lib/core/constants/my_constants.dart @@ -11,6 +11,6 @@ class MyConstants { 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 firstDownload = 'FIRST_DOWNLOAD'; static const String currentLevel = 'CURRENT_LEVEL'; } \ No newline at end of file diff --git a/lib/core/middlewares/my_middlewares.dart b/lib/core/middlewares/my_middlewares.dart new file mode 100644 index 0000000..cad56f8 --- /dev/null +++ b/lib/core/middlewares/my_middlewares.dart @@ -0,0 +1,23 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:hadi_hoda_flutter/core/constants/my_constants.dart'; +import 'package:hadi_hoda_flutter/core/routers/my_routes.dart'; +import 'package:hadi_hoda_flutter/core/utils/local_storage.dart'; + +class MyMiddlewares { + static const MyMiddlewares _i = MyMiddlewares._internal(); + const MyMiddlewares._internal(); + factory MyMiddlewares() => _i; + + static FutureOr intro(BuildContext context, GoRouterState state) { + final String? firstDownload = LocalStorage.readData( + key: MyConstants.firstDownload); + if (firstDownload != 'true') { + return Routes.languagePage; + } else { + return null; + } + } +} diff --git a/lib/core/routers/my_routes.dart b/lib/core/routers/my_routes.dart index 0f9ef04..406fe07 100644 --- a/lib/core/routers/my_routes.dart +++ b/lib/core/routers/my_routes.dart @@ -1,7 +1,9 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; +import 'package:hadi_hoda_flutter/core/middlewares/my_middlewares.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/bloc/download_event.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'; @@ -38,8 +40,9 @@ GoRouter get appPages => GoRouter( GoRoute( name: Routes.introPage, path: Routes.introPage, + redirect: MyMiddlewares.intro, builder: (context, state) => BlocProvider( - create: (context) => IntroBloc(locator(), locator()), + create: (context) => IntroBloc(), child: const IntroPage(), ), ), @@ -47,7 +50,12 @@ GoRouter get appPages => GoRouter( name: Routes.downloadPage, path: Routes.downloadPage, builder: (context, state) => BlocProvider( - create: (context) => DownloadBloc(locator(), locator(), locator()), + create: (context) => DownloadBloc( + locator(), + locator(), + locator(), + locator(), + )..add(GetImagesEvent()), child: const DownloadPage(), ), ), diff --git a/lib/core/widgets/animations/rotation_anim.dart b/lib/core/widgets/animations/rotation_anim.dart new file mode 100644 index 0000000..ea4d9e9 --- /dev/null +++ b/lib/core/widgets/animations/rotation_anim.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; + +class RotationAnim extends StatefulWidget { + const RotationAnim({super.key, required this.child}); + + final Widget child; + + @override + State createState() => _RotationAnimState(); +} + +class _RotationAnimState extends State + with SingleTickerProviderStateMixin { + late AnimationController _controller; + late Animation _animation; + + @override + void initState() { + super.initState(); + _controller = AnimationController( + vsync: this, + duration: Duration(seconds: 20), + ); + _animation = Tween( + begin: 0, + end: 20, + ).animate(CurvedAnimation(parent: _controller, curve: Curves.linear)); + + _controller.repeat(); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return AnimatedBuilder( + animation: _controller, + child: widget.child, + builder: (context, child) => RotationTransition( + turns: _animation, + alignment: Alignment.center, + child: child, + ), + ); + } +} diff --git a/lib/features/download/data/datasource/download_datasource.dart b/lib/features/download/data/datasource/download_datasource.dart index def3308..5c3ba31 100644 --- a/lib/features/download/data/datasource/download_datasource.dart +++ b/lib/features/download/data/datasource/download_datasource.dart @@ -16,6 +16,7 @@ import 'package:hadi_hoda_flutter/features/level/domain/entity/total_data_entity import 'package:hive/hive.dart'; abstract class IDownloadDatasource { + Future getImages(); Future getAudios(); Future saveLevels(); Stream loadingStream(); @@ -27,6 +28,54 @@ class DownloadDatasourceImpl implements IDownloadDatasource { DownloadDatasourceImpl(this.httpRequest); + @override + Future getImages() async { + final String filePath = '${StoragePath.documentDir.path}/images.zip'; + + if (LocalStorage.readData(key: MyConstants.downloadedImage) != 'true') { + await httpRequest.download( + urlPath: MyApi.images, + savePath: filePath, + onReceive: (count, total) { + streamController.add(DownloadEntity( + count: count / 1, + total: total / 1, + percent: (count / total) * 100, + )); + }, + ).then((value) async { + await LocalStorage.saveData( + key: MyConstants.downloadedImage, + value: 'true', + ); + }); + } + + try{ + if (LocalStorage.readData(key: MyConstants.extractedImage) != 'true') { + final File file = File(filePath); + final Directory directory = Directory('${StoragePath.documentDir.path}/'); + await ZipFile.extractToDirectory( + zipFile: file, + destinationDir: directory, + onExtracting: (zipEntry, progress) { + return ZipFileOperation.includeItem; + }, + ).then((value) async { + await Future.wait([ + LocalStorage.saveData( + key: MyConstants.extractedImage, + value: 'true', + ), + file.delete(recursive: true), + ]); + }); + } + } catch (e){ + throw MyException(errorMessage: '$e'); + } + } + @override Future getAudios() async { final String filePath = '${StoragePath.documentDir.path}/audios.zip'; @@ -52,7 +101,7 @@ class DownloadDatasourceImpl implements IDownloadDatasource { key: MyConstants.downloadedAudio, value: 'true', ); - },); + }); } try{ @@ -76,10 +125,6 @@ class DownloadDatasourceImpl implements IDownloadDatasource { 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'); @@ -105,9 +150,7 @@ class DownloadDatasourceImpl implements IDownloadDatasource { response?['result'], (json) => LevelModel.fromJson(json), ); - await Future.wait([ - data.add(TotalDataEntity(code: selectedLanguage, levels: levels)), - ]); + await data.add(TotalDataEntity(code: selectedLanguage, levels: levels)); } } diff --git a/lib/features/download/data/repository_impl/download_repository_impl.dart b/lib/features/download/data/repository_impl/download_repository_impl.dart index 9fd8b54..13917f6 100644 --- a/lib/features/download/data/repository_impl/download_repository_impl.dart +++ b/lib/features/download/data/repository_impl/download_repository_impl.dart @@ -11,6 +11,22 @@ class DownloadRepositoryImpl implements IDownloadRepository { const DownloadRepositoryImpl(this.datasource); + @override + Future> getImages() async { + try { + await datasource.getImages(); + 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> getAudios() async { try { diff --git a/lib/features/download/domain/repository/download_repository.dart b/lib/features/download/domain/repository/download_repository.dart index b7ffbd5..ab53e49 100644 --- a/lib/features/download/domain/repository/download_repository.dart +++ b/lib/features/download/domain/repository/download_repository.dart @@ -4,6 +4,7 @@ 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> getImages(); Future> getAudios(); Future> saveLevels(); Stream loadingStream(); diff --git a/lib/features/intro/domain/usecases/get_images_usecase.dart b/lib/features/download/domain/usecases/get_images_usecase.dart similarity index 78% rename from lib/features/intro/domain/usecases/get_images_usecase.dart rename to lib/features/download/domain/usecases/get_images_usecase.dart index 3774894..f91e1d6 100644 --- a/lib/features/intro/domain/usecases/get_images_usecase.dart +++ b/lib/features/download/domain/usecases/get_images_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 GetImagesUseCase implements UseCase { - final IIntroRepository repository; + final IDownloadRepository repository; const GetImagesUseCase(this.repository); diff --git a/lib/features/download/domain/usecases/loading_stream_audio_usecase.dart b/lib/features/download/domain/usecases/loading_stream_usecase.dart similarity index 78% rename from lib/features/download/domain/usecases/loading_stream_audio_usecase.dart rename to lib/features/download/domain/usecases/loading_stream_usecase.dart index 4af13e0..cb0fa54 100644 --- a/lib/features/download/domain/usecases/loading_stream_audio_usecase.dart +++ b/lib/features/download/domain/usecases/loading_stream_usecase.dart @@ -1,10 +1,10 @@ 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 { +class LoadingStreamUseCase { final IDownloadRepository repository; - const LoadingStreamAudioUseCase(this.repository); + const LoadingStreamUseCase(this.repository); Stream call() { return repository.loadingStream(); diff --git a/lib/features/download/presentation/bloc/download_bloc.dart b/lib/features/download/presentation/bloc/download_bloc.dart index 8672d8e..09ed742 100644 --- a/lib/features/download/presentation/bloc/download_bloc.dart +++ b/lib/features/download/presentation/bloc/download_bloc.dart @@ -2,13 +2,16 @@ import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:go_router/go_router.dart'; +import 'package:hadi_hoda_flutter/core/constants/my_constants.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/core/utils/local_storage.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/get_images_usecase.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'; @@ -16,20 +19,23 @@ import 'package:hadi_hoda_flutter/features/download/presentation/bloc/download_s class DownloadBloc extends Bloc { /// ------------constructor------------ DownloadBloc( + this._getImagesUseCase, this._getAudiosUseCase, this._loadingStreamUseCase, this._saveLevelsUseCase, ) : super(const DownloadState()) { + on(_getImagesEvent); on(_getAudiosEvent); on(_saveLevelsEvent); loadingStream = _loadingStreamUseCase(); } /// ------------UseCases------------ + final GetImagesUseCase _getImagesUseCase; final GetAudiosUseCase _getAudiosUseCase; final SaveLevelsUseCase _saveLevelsUseCase; - final LoadingStreamAudioUseCase _loadingStreamUseCase; + final LoadingStreamUseCase _loadingStreamUseCase; /// ------------Variables------------ Stream loadingStream = Stream.empty(); @@ -39,6 +45,22 @@ class DownloadBloc extends Bloc { /// ------------Functions------------ /// ------------Api Calls------------ + FutureOr _getImagesEvent( + GetImagesEvent event, + Emitter emit, + ) async { + await _getImagesUseCase(NoParams()).then((value) { + value.fold( + (data) { + add(GetAudiosEvent()); + }, + (error) async { + emit(state.copyWith(getFilesStatus: BaseError(error.errorMessage))); + }, + ); + }); + } + FutureOr _getAudiosEvent( GetAudiosEvent event, Emitter emit, @@ -63,7 +85,10 @@ class DownloadBloc extends Bloc { await _saveLevelsUseCase(NoParams()).then((value) => value.fold( (data) async { - ContextProvider.context.goNamed(Routes.homePage); + await LocalStorage.saveData(key: MyConstants.firstDownload, value: 'true'); + if(ContextProvider.context.mounted){ + 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 index 9d05b54..ec62661 100644 --- a/lib/features/download/presentation/bloc/download_event.dart +++ b/lib/features/download/presentation/bloc/download_event.dart @@ -1,5 +1,6 @@ sealed class DownloadEvent { const DownloadEvent(); } +class GetImagesEvent extends DownloadEvent {} class GetAudiosEvent extends DownloadEvent {} class SaveLevelsEvent extends DownloadEvent {} diff --git a/lib/features/download/presentation/ui/download_page.dart b/lib/features/download/presentation/ui/download_page.dart index c7533b3..c289fb6 100644 --- a/lib/features/download/presentation/ui/download_page.dart +++ b/lib/features/download/presentation/ui/download_page.dart @@ -16,20 +16,9 @@ import 'package:hadi_hoda_flutter/features/download/presentation/bloc/download_e 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 { +class DownloadPage extends StatelessWidget { 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( @@ -66,7 +55,7 @@ class _DownloadPageState extends State { horizontal: 60, ), child: ErrorState( - onTap: () => context.read().add(GetAudiosEvent()), + onTap: () => context.read().add(GetImagesEvent()), ), ); } else { diff --git a/lib/features/home/presentation/bloc/home_bloc.dart b/lib/features/home/presentation/bloc/home_bloc.dart index 1ba9839..219c7ed 100644 --- a/lib/features/home/presentation/bloc/home_bloc.dart +++ b/lib/features/home/presentation/bloc/home_bloc.dart @@ -3,11 +3,15 @@ import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:flutter/cupertino.dart'; import 'package:go_router/go_router.dart'; +import 'package:hadi_hoda_flutter/core/constants/my_constants.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/utils/local_storage.dart'; import 'package:hadi_hoda_flutter/core/widgets/about_us_dialog/about_us_dialog.dart'; import 'package:hadi_hoda_flutter/features/home/presentation/bloc/home_event.dart'; import 'package:hadi_hoda_flutter/features/home/presentation/bloc/home_state.dart'; +import 'package:hadi_hoda_flutter/features/level/domain/entity/total_data_entity.dart'; +import 'package:hive/hive.dart'; class HomeBloc extends Bloc { /// ------------constructor------------ @@ -28,7 +32,17 @@ class HomeBloc extends Bloc { /// ------------Functions------------ void goToLevelPage(BuildContext context){ - context.pushNamed(Routes.levelPage); + final String? selectedLanguage = LocalStorage.readData(key: MyConstants.selectLanguage); + final Box dataBox = Hive.box(MyConstants.levelBox); + final TotalDataEntity findData = dataBox.values.singleWhere( + (e) => e.code == selectedLanguage, + orElse: () => TotalDataEntity(), + ); + if (findData.levels?.isNotEmpty ?? false) { + context.pushNamed(Routes.levelPage); + } else { + context.goNamed(Routes.downloadPage); + } } void goToLanguagePage(BuildContext context){ diff --git a/lib/features/intro/data/datasource/intro_datasource.dart b/lib/features/intro/data/datasource/intro_datasource.dart deleted file mode 100644 index 7a48b46..0000000 --- a/lib/features/intro/data/datasource/intro_datasource.dart +++ /dev/null @@ -1,78 +0,0 @@ -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/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'; - -abstract class IIntroDatasource { - Future getImages(); - Stream loadingStream(); -} - -class IntroDatasourceImpl implements IIntroDatasource { - final IHttpRequest httpRequest; - final StreamController streamController = StreamController.broadcast(); - - IntroDatasourceImpl(this.httpRequest); - - @override - Future getImages() async { - final String filePath = '${StoragePath.documentDir.path}/images.zip'; - - if (LocalStorage.readData(key: MyConstants.downloadedImage) != 'true') { - await httpRequest.download( - urlPath: MyApi.images, - savePath: filePath, - onReceive: (count, total) { - streamController.add(DownloadEntity( - count: count / 1, - total: total / 1, - percent: (count / total) * 100, - )); - }, - ).then((value) async { - await LocalStorage.saveData( - key: MyConstants.downloadedImage, - value: 'true', - ); - }); - } - - try{ - if (LocalStorage.readData(key: MyConstants.extractedImage) != 'true') { - final File file = File(filePath); - final Directory directory = Directory('${StoragePath.documentDir.path}/'); - await ZipFile.extractToDirectory( - zipFile: file, - destinationDir: directory, - onExtracting: (zipEntry, progress) { - return ZipFileOperation.includeItem; - }, - ).then((value) async { - await Future.wait([ - LocalStorage.saveData( - key: MyConstants.extractedImage, - 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 - 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 deleted file mode 100644 index f6e5c0d..0000000 --- a/lib/features/intro/data/repository_impl/intro_repository_impl.dart +++ /dev/null @@ -1,34 +0,0 @@ -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/repository/intro_repository.dart'; - -class IntroRepositoryImpl implements IIntroRepository { - final IIntroDatasource datasource; - - const IntroRepositoryImpl(this.datasource); - - @override - Future> getImages() async { - try { - await datasource.getImages(); - 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/repository/intro_repository.dart b/lib/features/intro/domain/repository/intro_repository.dart deleted file mode 100644 index 4ee9181..0000000 --- a/lib/features/intro/domain/repository/intro_repository.dart +++ /dev/null @@ -1,9 +0,0 @@ -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 IIntroRepository { - Future> getImages(); - Stream loadingStream(); -} diff --git a/lib/features/intro/domain/usecases/loading_stream_usecase.dart b/lib/features/intro/domain/usecases/loading_stream_usecase.dart deleted file mode 100644 index 1d81d89..0000000 --- a/lib/features/intro/domain/usecases/loading_stream_usecase.dart +++ /dev/null @@ -1,12 +0,0 @@ -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 { - final IIntroRepository repository; - - const LoadingStreamUseCase(this.repository); - - Stream call() { - return repository.loadingStream(); - } -} diff --git a/lib/features/intro/presentation/bloc/intro_event.dart b/lib/features/intro/presentation/bloc/intro_event.dart index 610637a..f9f7a91 100644 --- a/lib/features/intro/presentation/bloc/intro_event.dart +++ b/lib/features/intro/presentation/bloc/intro_event.dart @@ -1,4 +1,3 @@ sealed class IntroEvent { const IntroEvent(); } -class GetImagesEvent extends IntroEvent {} diff --git a/lib/features/intro/presentation/ui/widgets/intro_loading_widget.dart b/lib/features/intro/presentation/ui/widgets/intro_loading_widget.dart deleted file mode 100644 index 12e4568..0000000 --- a/lib/features/intro/presentation/ui/widgets/intro_loading_widget.dart +++ /dev/null @@ -1,136 +0,0 @@ -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 IntroLoadingWidget extends StatelessWidget { - const IntroLoadingWidget({ - 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/language/presentation/bloc/language_bloc.dart b/lib/features/language/presentation/bloc/language_bloc.dart index 364160a..f907683 100644 --- a/lib/features/language/presentation/bloc/language_bloc.dart +++ b/lib/features/language/presentation/bloc/language_bloc.dart @@ -49,7 +49,6 @@ class LanguageBloc extends Bloc { key: MyConstants.selectLanguage, value: state.selectedLang.code ?? 'fa', ), - LocalStorage.saveData(key: MyConstants.firstLanguagePage, value: 'true') ]); if (Directory( diff --git a/lib/init_bindings.dart b/lib/init_bindings.dart index 4741241..931d43b 100644 --- a/lib/init_bindings.dart +++ b/lib/init_bindings.dart @@ -9,13 +9,9 @@ import 'package:hadi_hoda_flutter/features/download/data/datasource/download_dat 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/get_images_usecase.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/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_images_usecase.dart'; -import 'package:hadi_hoda_flutter/features/intro/domain/usecases/loading_stream_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'; @@ -50,17 +46,14 @@ void initBindings() { locator.registerLazySingleton(() => GetSampleUseCase(locator())); /// Intro Feature - locator.registerLazySingleton(() => IntroDatasourceImpl(locator())); - locator.registerLazySingleton(() => IntroRepositoryImpl(locator())); - locator.registerLazySingleton(() => GetImagesUseCase(locator())); - locator.registerLazySingleton(() => LoadingStreamUseCase(locator())); /// Download Feature locator.registerLazySingleton(() => DownloadDatasourceImpl(locator())); locator.registerLazySingleton(() => DownloadRepositoryImpl(locator())); + locator.registerLazySingleton(() => GetImagesUseCase(locator())); locator.registerLazySingleton(() => GetAudiosUseCase(locator())); locator.registerLazySingleton(() => SaveLevelsUseCase(locator())); - locator.registerLazySingleton(() => LoadingStreamAudioUseCase(locator())); + locator.registerLazySingleton(() => LoadingStreamUseCase(locator())); /// Language Feature