From 79d7c34bf1edcd97edd223eb34dbaa2904011a10 Mon Sep 17 00:00:00 2001 From: zemanux Date: Thu, 15 Feb 2024 19:10:09 +0100 Subject: [PATCH 1/6] refactor: class ValueState is replaced by Value BREAKING CHANGE: complete code refactoring --- .github/workflows/test.yml | 6 +- README.md | 4 +- packages/analysis_options.yaml | 7 - packages/flutter_value_state/.gitignore | 30 - packages/flutter_value_state/.metadata | 10 - packages/flutter_value_state/CHANGELOG.md | 39 -- packages/flutter_value_state/LICENSE | 21 - packages/flutter_value_state/README.md | 128 ----- .../flutter_value_state/analysis_options.yaml | 1 - .../example/analysis_options.yaml | 1 - .../example/lib/cubit.dart | 24 - .../flutter_value_state/example/lib/main.dart | 94 --- .../flutter_value_state/example/pubspec.yaml | 27 - .../example/test/widget_test.dart | 97 ---- .../lib/flutter_value_state.dart | 6 - .../lib/src/configuration.dart | 162 ------ .../flutter_value_state/lib/src/widgets.dart | 149 ----- packages/flutter_value_state/pubspec.yaml | 22 - .../test/flutter_value_state_test.dart | 401 ------------- packages/value_cubit/.gitignore | 29 - packages/value_cubit/CHANGELOG.md | 80 --- packages/value_cubit/LICENSE | 21 - packages/value_cubit/README.md | 127 ---- packages/value_cubit/analysis_options.yaml | 1 - packages/value_cubit/example/.gitignore | 46 -- packages/value_cubit/example/.metadata | 10 - packages/value_cubit/example/README.md | 10 - .../value_cubit/example/analysis_options.yaml | 1 - packages/value_cubit/example/lib/cubit.dart | 24 - packages/value_cubit/example/lib/main.dart | 83 --- packages/value_cubit/example/pubspec.yaml | 26 - packages/value_cubit/example/web/favicon.png | Bin 917 -> 0 bytes .../example/web/icons/Icon-192.png | Bin 5292 -> 0 bytes .../example/web/icons/Icon-512.png | Bin 8252 -> 0 bytes .../example/web/icons/Icon-maskable-192.png | Bin 5594 -> 0 bytes .../example/web/icons/Icon-maskable-512.png | Bin 20998 -> 0 bytes packages/value_cubit/example/web/index.html | 104 ---- .../value_cubit/example/web/manifest.json | 35 -- packages/value_cubit/lib/src/cubit.dart | 92 --- packages/value_cubit/lib/src/extensions.dart | 65 --- packages/value_cubit/lib/value_cubit.dart | 6 - packages/value_cubit/pubspec.yaml | 20 - packages/value_cubit/test/cubit.dart | 46 -- .../value_cubit/test/helpers_cubit_test.dart | 275 --------- .../value_cubit/test/value_cubit_test.dart | 211 ------- packages/value_state/README.md | 174 +++--- packages/value_state/analysis_options.yaml | 8 +- .../value_state/doc/class_diagram.mermaid | 52 -- packages/value_state/doc/class_diagram.png | Bin 136846 -> 0 bytes .../value_state/doc/state_diagram.mermaid | 15 - packages/value_state/doc/state_diagram.png | Bin 88453 -> 0 bytes .../example/.gitignore | 0 .../example/.metadata | 0 .../example/README.md | 2 +- .../value_state/example/analysis_options.yaml | 15 + .../example/lib/logic/counter_cubit.dart | 13 + .../example/lib/logic/counter_notifier.dart | 31 + .../example/lib/logic/counter_notifier.g.dart | 42 ++ .../lib/logic/counter_value_notifier.dart | 21 + .../example/lib/logic/repository.dart | 15 + packages/value_state/example/lib/main.dart | 52 ++ .../value_state/example/lib/main_cubit.dart | 56 ++ .../example/lib/main_riverpod.dart | 59 ++ .../example/lib/widgets/action_button.dart | 18 + .../example/lib/widgets/app_root.dart | 22 + .../example/lib/widgets/counter_notifier.dart | 11 + .../example/lib/widgets/default_error.dart | 17 + .../example/lib/widgets/formatted_column.dart | 18 + .../example/lib/widgets/loader.dart | 27 + packages/value_state/example/main.dart | 71 --- packages/value_state/example/pubspec.yaml | 35 ++ .../example/test/widget_test.dart | 66 ++- .../example/web/favicon.png | Bin .../example/web/icons/Icon-192.png | Bin .../example/web/icons/Icon-512.png | Bin .../example/web/icons/Icon-maskable-192.png | Bin .../example/web/icons/Icon-maskable-512.png | Bin .../example/web/index.html | 0 .../example/web/manifest.json | 0 packages/value_state/lib/src/extensions.dart | 129 ++--- packages/value_state/lib/src/fetch.dart | 54 ++ .../value_state/lib/src/fetch_on_value.dart | 43 ++ packages/value_state/lib/src/helpers.dart | 131 ----- packages/value_state/lib/src/perform.dart | 93 --- packages/value_state/lib/src/states.dart | 336 ----------- packages/value_state/lib/src/value.dart | 209 +++++++ packages/value_state/lib/value_state.dart | 7 +- packages/value_state/pubspec.yaml | 5 +- .../value_state/test/extensions_test.dart | 186 ++---- packages/value_state/test/fetch_test.dart | 63 ++ .../value_state/test/helpers_cubit_test.dart | 229 -------- packages/value_state/test/helpers_test.dart | 96 +++- packages/value_state/test/stream.dart | 38 +- .../value_state/test/value_state_test.dart | 541 ++++++++++++------ pubspec.lock | 122 ++-- pubspec.yaml | 4 +- 96 files changed, 1587 insertions(+), 4080 deletions(-) delete mode 100644 packages/analysis_options.yaml delete mode 100644 packages/flutter_value_state/.gitignore delete mode 100644 packages/flutter_value_state/.metadata delete mode 100644 packages/flutter_value_state/CHANGELOG.md delete mode 100644 packages/flutter_value_state/LICENSE delete mode 100644 packages/flutter_value_state/README.md delete mode 100644 packages/flutter_value_state/analysis_options.yaml delete mode 100644 packages/flutter_value_state/example/analysis_options.yaml delete mode 100644 packages/flutter_value_state/example/lib/cubit.dart delete mode 100644 packages/flutter_value_state/example/lib/main.dart delete mode 100644 packages/flutter_value_state/example/pubspec.yaml delete mode 100644 packages/flutter_value_state/example/test/widget_test.dart delete mode 100644 packages/flutter_value_state/lib/flutter_value_state.dart delete mode 100644 packages/flutter_value_state/lib/src/configuration.dart delete mode 100644 packages/flutter_value_state/lib/src/widgets.dart delete mode 100644 packages/flutter_value_state/pubspec.yaml delete mode 100644 packages/flutter_value_state/test/flutter_value_state_test.dart delete mode 100644 packages/value_cubit/.gitignore delete mode 100644 packages/value_cubit/CHANGELOG.md delete mode 100644 packages/value_cubit/LICENSE delete mode 100644 packages/value_cubit/README.md delete mode 100644 packages/value_cubit/analysis_options.yaml delete mode 100644 packages/value_cubit/example/.gitignore delete mode 100644 packages/value_cubit/example/.metadata delete mode 100644 packages/value_cubit/example/README.md delete mode 100644 packages/value_cubit/example/analysis_options.yaml delete mode 100644 packages/value_cubit/example/lib/cubit.dart delete mode 100644 packages/value_cubit/example/lib/main.dart delete mode 100644 packages/value_cubit/example/pubspec.yaml delete mode 100644 packages/value_cubit/example/web/favicon.png delete mode 100644 packages/value_cubit/example/web/icons/Icon-192.png delete mode 100644 packages/value_cubit/example/web/icons/Icon-512.png delete mode 100644 packages/value_cubit/example/web/icons/Icon-maskable-192.png delete mode 100644 packages/value_cubit/example/web/icons/Icon-maskable-512.png delete mode 100644 packages/value_cubit/example/web/index.html delete mode 100644 packages/value_cubit/example/web/manifest.json delete mode 100644 packages/value_cubit/lib/src/cubit.dart delete mode 100644 packages/value_cubit/lib/src/extensions.dart delete mode 100644 packages/value_cubit/lib/value_cubit.dart delete mode 100644 packages/value_cubit/pubspec.yaml delete mode 100644 packages/value_cubit/test/cubit.dart delete mode 100644 packages/value_cubit/test/helpers_cubit_test.dart delete mode 100644 packages/value_cubit/test/value_cubit_test.dart delete mode 100644 packages/value_state/doc/class_diagram.mermaid delete mode 100644 packages/value_state/doc/class_diagram.png delete mode 100644 packages/value_state/doc/state_diagram.mermaid delete mode 100644 packages/value_state/doc/state_diagram.png rename packages/{flutter_value_state => value_state}/example/.gitignore (100%) rename packages/{flutter_value_state => value_state}/example/.metadata (100%) rename packages/{flutter_value_state => value_state}/example/README.md (64%) create mode 100644 packages/value_state/example/analysis_options.yaml create mode 100644 packages/value_state/example/lib/logic/counter_cubit.dart create mode 100644 packages/value_state/example/lib/logic/counter_notifier.dart create mode 100644 packages/value_state/example/lib/logic/counter_notifier.g.dart create mode 100644 packages/value_state/example/lib/logic/counter_value_notifier.dart create mode 100644 packages/value_state/example/lib/logic/repository.dart create mode 100644 packages/value_state/example/lib/main.dart create mode 100644 packages/value_state/example/lib/main_cubit.dart create mode 100644 packages/value_state/example/lib/main_riverpod.dart create mode 100644 packages/value_state/example/lib/widgets/action_button.dart create mode 100644 packages/value_state/example/lib/widgets/app_root.dart create mode 100644 packages/value_state/example/lib/widgets/counter_notifier.dart create mode 100644 packages/value_state/example/lib/widgets/default_error.dart create mode 100644 packages/value_state/example/lib/widgets/formatted_column.dart create mode 100644 packages/value_state/example/lib/widgets/loader.dart delete mode 100644 packages/value_state/example/main.dart create mode 100644 packages/value_state/example/pubspec.yaml rename packages/{value_cubit => value_state}/example/test/widget_test.dart (53%) rename packages/{flutter_value_state => value_state}/example/web/favicon.png (100%) rename packages/{flutter_value_state => value_state}/example/web/icons/Icon-192.png (100%) rename packages/{flutter_value_state => value_state}/example/web/icons/Icon-512.png (100%) rename packages/{flutter_value_state => value_state}/example/web/icons/Icon-maskable-192.png (100%) rename packages/{flutter_value_state => value_state}/example/web/icons/Icon-maskable-512.png (100%) rename packages/{flutter_value_state => value_state}/example/web/index.html (100%) rename packages/{flutter_value_state => value_state}/example/web/manifest.json (100%) create mode 100644 packages/value_state/lib/src/fetch.dart create mode 100644 packages/value_state/lib/src/fetch_on_value.dart delete mode 100644 packages/value_state/lib/src/helpers.dart delete mode 100644 packages/value_state/lib/src/perform.dart delete mode 100644 packages/value_state/lib/src/states.dart create mode 100644 packages/value_state/lib/src/value.dart create mode 100644 packages/value_state/test/fetch_test.dart delete mode 100644 packages/value_state/test/helpers_cubit_test.dart diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 83c3f00..f48e122 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,11 +15,11 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: subosito/flutter-action@v2 - - uses: bluefireteam/melos-action@v1 + - uses: bluefireteam/melos-action@v3 with: - melos-version: '3.1.0' + melos-version: '6.3.2' - name: Disable analytics run: flutter config --no-analytics diff --git a/README.md b/README.md index 0d725e8..c8490a5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -A dart package that helps to implement basic states for [Flutter](https://flutter.dev/) and/or [BLoC library](https://pub.dev/packages/bloc) to perform, load and fetch data. +A dart package that helps to implement basic states for [Flutter](https://flutter.dev/) to load and fetch data. [![Test](https://github.com/devobs/value_state/actions/workflows/test.yml/badge.svg)](https://github.com/devobs/value_state/actions/workflows/test.yml) @@ -11,8 +11,6 @@ A dart package that helps to implement basic states for [Flutter](https://flutte | Package | Pub | | ------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------- | | [value_state](https://github.com/devobs/value_state/tree/main/packages/value_state) | [![pub package](https://img.shields.io/pub/v/value_state.svg)](https://pub.dev/packages/value_state) | -| [value_cubit](https://github.com/devobs/value_state/tree/main/packages/value_cubit) | [![pub package](https://img.shields.io/pub/v/value_cubit.svg)](https://pub.dev/packages/value_cubit) | -| [flutter_value_state](https://github.com/devobs/value_state/tree/main/packages/flutter_value_state) | [![pub package](https://img.shields.io/pub/v/flutter_value_state.svg)](https://pub.dev/packages/flutter_value_state) | diff --git a/packages/analysis_options.yaml b/packages/analysis_options.yaml deleted file mode 100644 index 17ccefe..0000000 --- a/packages/analysis_options.yaml +++ /dev/null @@ -1,7 +0,0 @@ -include: package:lints/recommended.yaml - -linter: - rules: - - prefer_const_constructors - - prefer_const_declarations - - prefer_single_quotes diff --git a/packages/flutter_value_state/.gitignore b/packages/flutter_value_state/.gitignore deleted file mode 100644 index e420cab..0000000 --- a/packages/flutter_value_state/.gitignore +++ /dev/null @@ -1,30 +0,0 @@ -# Miscellaneous -*.class -*.log -*.pyc -*.swp -.DS_Store -.atom/ -.buildlog/ -.history -.svn/ -migrate_working_dir/ - -# IntelliJ related -*.iml -*.ipr -*.iws -.idea/ - -# The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line -# is commented out by default. -#.vscode/ - -# Flutter/Dart/Pub related -# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. -pubspec.lock -**/doc/api/ -.dart_tool/ -.packages -build/ diff --git a/packages/flutter_value_state/.metadata b/packages/flutter_value_state/.metadata deleted file mode 100644 index e7011f6..0000000 --- a/packages/flutter_value_state/.metadata +++ /dev/null @@ -1,10 +0,0 @@ -# This file tracks properties of this Flutter project. -# Used by Flutter tool to assess capabilities and perform upgrades etc. -# -# This file should be version controlled and should not be manually edited. - -version: - revision: f1875d570e39de09040c8f79aa13cc56baab8db1 - channel: stable - -project_type: package diff --git a/packages/flutter_value_state/CHANGELOG.md b/packages/flutter_value_state/CHANGELOG.md deleted file mode 100644 index 29d76ec..0000000 --- a/packages/flutter_value_state/CHANGELOG.md +++ /dev/null @@ -1,39 +0,0 @@ -## 1.3.6 - - - **FIX**: buildWidget with child class as value. - -## 1.3.5 - - - Update a dependency to the latest release. - -## 1.3.4 - - - **FIX**: onDefault not triggered. - -## 1.3.3 - - - **FIX**: ValueStateConfiguration merge broken. - -## 1.3.2 - - - Update a dependency to the latest release. - -## 1.3.1 - - - Update a dependency to the latest release. - -## 1.3.0 - - - **FEAT**: added onValue and wrapper parameters. - -## 1.2.2 - - - **FIX**: deprecation messages. - -## 1.2.1 - - - Update a dependency to the latest release. - -## 1.2.0 - -Initial version of the library. diff --git a/packages/flutter_value_state/LICENSE b/packages/flutter_value_state/LICENSE deleted file mode 100644 index d3f2fb0..0000000 --- a/packages/flutter_value_state/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) -Copyright (c) 2022 Emmanuel LEFEBVRE - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without restriction, -including without limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of the Software, -and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/packages/flutter_value_state/README.md b/packages/flutter_value_state/README.md deleted file mode 100644 index 2f88bd6..0000000 --- a/packages/flutter_value_state/README.md +++ /dev/null @@ -1,128 +0,0 @@ -A dart package that helps to implement basic states for [BLoC library](https://pub.dev/packages/bloc) to perform, load and fetch data. - - -[![pub package](https://img.shields.io/pub/v/flutter_value_state.svg)](https://pub.dev/packages/flutter_value_state) -[![Test](https://github.com/devobs/value_state/actions/workflows/test.yml/badge.svg)](https://github.com/devobs/value_state/actions/workflows/test.yml) -[![codecov](https://codecov.io/gh/devobs/value_state/branch/main/graph/badge.svg)](https://app.codecov.io/gh/devobs/value_state/tree/main/packages/flutter_value_state) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) - -## Features - -* Provides all necessary states for data : init, waiting, value/no value and error states (from [value_state](https://pub.dev/packages/value_state)), -* Provides `BaseState.buildWidget` that build a widget depending on its state. The `WithValueState` case is mandatory (first ordered parameter). Other states that are not passed as parameter are handled by `ValueStateConfiguration`. If no `ValueStateConfiguration` is in ascendant tree, a `SizedBox`is returned, -* `ValueStateConfiguration` provides a default behavior for null parameters in `BaseState.buildWidget`. - -## Usage - -This example show in the Flutter app, how pattern matching is used to handles the different states. - -```dart -class CounterCubit extends ValueCubit { - var _value = 0; - - // Put your WS call that can be refreshed - Future _getCounterValueFromWebService() async => _value++; - - Future increment() => perform(() async { - // [perform] generate intermediate or final states such as PendingState, - // concrete subclass of ReadyState with right [ReadyState.refreshing] value - // or ErrorState if an error is raised. - final result = await _getCounterValueFromWebService(); - - emit(ValueState(result)); - }); - - void clear() { - _value = 0; - emit(const PendingState()); - } -} - -class MyApp extends StatelessWidget { - const MyApp({super.key}); - - @override - Widget build(BuildContext context) { - return BlocProvider( - create: (_) => CounterCubit(), - child: MaterialApp( - title: 'Value Cubit Demo', - builder: (context, child) => child == null - ? const SizedBox.shrink() - : ValueStateConfiguration( - configuration: ValueStateConfigurationData( - builderWaiting: (context, state) => - const Center(child: CircularProgressIndicator()), - builderError: (context, state) => Center( - child: Text('Expected error.', - style: - TextStyle(color: Theme.of(context).colorScheme.error)), - ), - builderNoValue: (context, state) => - const Center(child: Text('No value.')), - wrapper: (context, state, child) => AnimatedSwitcher( - duration: const Duration(milliseconds: 300), - child: child), - ), - child: child, - ), - home: const MyHomePage(), - )); - } -} - -class MyHomePage extends StatelessWidget { - const MyHomePage({super.key}); - - @override - Widget build(BuildContext context) { - final theme = Theme.of(context); - - return BlocBuilder>(builder: (context, state) { - return Scaffold( - appBar: AppBar( - title: const Text('Flutter Demo Home Page'), - ), - body: DefaultTextStyle( - style: const TextStyle(fontSize: 24), - textAlign: TextAlign.center, - child: state.buildWidget( - (context, state, error) => Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - if (state.refreshing) const LinearProgressIndicator(), - const Spacer(), - if (error != null) error, - const Text('Counter value :'), - Text( - state.value.toString(), - style: theme.textTheme.headlineMedium, - ), - const Spacer(), - ]), - valueMixedWithError: true), - ), - floatingActionButton: state is! ReadyState - ? null - : FloatingActionButton( - onPressed: state.refreshing - ? null - : context.read().increment, - tooltip: 'Increment', - child: state.refreshing - ? SizedBox.square( - dimension: 20, - child: CircularProgressIndicator( - color: theme.colorScheme.onPrimary)) - : const Icon(Icons.refresh)), - ); - }); - } -} -``` - -The whole code of this example is available in [example](example). - -## Feedback - -Please file any issues, bugs or feature requests as an issue on the [Github page](https://github.com/devobs/value_state/issues). diff --git a/packages/flutter_value_state/analysis_options.yaml b/packages/flutter_value_state/analysis_options.yaml deleted file mode 100644 index 0caccc8..0000000 --- a/packages/flutter_value_state/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../analysis_options.yaml \ No newline at end of file diff --git a/packages/flutter_value_state/example/analysis_options.yaml b/packages/flutter_value_state/example/analysis_options.yaml deleted file mode 100644 index 0caccc8..0000000 --- a/packages/flutter_value_state/example/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../analysis_options.yaml \ No newline at end of file diff --git a/packages/flutter_value_state/example/lib/cubit.dart b/packages/flutter_value_state/example/lib/cubit.dart deleted file mode 100644 index 593d101..0000000 --- a/packages/flutter_value_state/example/lib/cubit.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:value_cubit/value_cubit.dart'; - -class CounterCubit extends ValueCubit { - var _value = 0; - Future _getMyValueFromRepository() async => _value++; - - CounterCubit() { - increment(); - } - - Future increment() => perform(() async { - await Future.delayed(const Duration(seconds: 1)); - - final result = await _getMyValueFromRepository(); - - if (result == 2) { - throw 'Error'; - } else if (result > 4) { - emit(const NoValueState()); - } else { - emit(ValueState(result)); - } - }); -} diff --git a/packages/flutter_value_state/example/lib/main.dart b/packages/flutter_value_state/example/lib/main.dart deleted file mode 100644 index cbbabea..0000000 --- a/packages/flutter_value_state/example/lib/main.dart +++ /dev/null @@ -1,94 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_value_state/flutter_value_state.dart'; - -import 'cubit.dart'; - -// coverage:ignore-start -void main() { - runApp(const MyApp()); -} -// coverage:ignore-end - -class MyApp extends StatelessWidget { - const MyApp({super.key}); - - // This widget is the root of your application. - @override - Widget build(BuildContext context) { - return BlocProvider( - create: (_) => CounterCubit(), - child: MaterialApp( - title: 'Value Cubit Demo', - builder: (context, child) => child == null - ? const SizedBox.shrink() - : ValueStateConfiguration( - configuration: ValueStateConfigurationData( - builderWaiting: (context, state) => - const Center(child: CircularProgressIndicator()), - builderError: (context, state) => Center( - child: Text('Expected error.', - style: TextStyle( - color: Theme.of(context).colorScheme.error)), - ), - builderNoValue: (context, state) => - const Center(child: Text('No value.')), - wrapper: (context, state, child) => AnimatedSwitcher( - duration: const Duration(milliseconds: 300), - child: child), - ), - child: child, - ), - home: const MyHomePage(), - )); - } -} - -class MyHomePage extends StatelessWidget { - const MyHomePage({super.key}); - - @override - Widget build(BuildContext context) { - final theme = Theme.of(context); - - return BlocBuilder>(builder: (context, state) { - return Scaffold( - appBar: AppBar( - title: const Text('Flutter Demo Home Page'), - ), - body: DefaultTextStyle( - style: const TextStyle(fontSize: 24), - textAlign: TextAlign.center, - child: state.buildWidget( - onValue: (context, state, error) => Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - if (state.refreshing) const LinearProgressIndicator(), - const Spacer(), - if (error != null) error, - const Text('Counter value :'), - Text( - state.value.toString(), - style: theme.textTheme.headlineMedium, - ), - const Spacer(), - ]), - valueMixedWithError: true), - ), - floatingActionButton: state is! ReadyState - ? null - : FloatingActionButton( - onPressed: state.refreshing - ? null - : context.read().increment, - tooltip: 'Increment', - child: state.refreshing - ? SizedBox.square( - dimension: 20, - child: CircularProgressIndicator( - color: theme.colorScheme.onPrimary)) - : const Icon(Icons.refresh)), - ); - }); - } -} diff --git a/packages/flutter_value_state/example/pubspec.yaml b/packages/flutter_value_state/example/pubspec.yaml deleted file mode 100644 index 8bf775a..0000000 --- a/packages/flutter_value_state/example/pubspec.yaml +++ /dev/null @@ -1,27 +0,0 @@ -name: flutter_state_value_example -description: A sample with a basic example - -publish_to: 'none' - -version: 1.0.0+1 - -environment: - sdk: ">=2.17.1 <3.0.0" - -dependencies: - flutter: - sdk: flutter - - flutter_bloc: ^8.0.1 - - value_state: 1.5.1 - flutter_value_state: 1.3.6 - value_cubit: 1.3.3 -dev_dependencies: - flutter_test: - sdk: flutter - - flutter_lints: ^2.0.0 - -flutter: - uses-material-design: true diff --git a/packages/flutter_value_state/example/test/widget_test.dart b/packages/flutter_value_state/example/test/widget_test.dart deleted file mode 100644 index 027da59..0000000 --- a/packages/flutter_value_state/example/test/widget_test.dart +++ /dev/null @@ -1,97 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility that Flutter provides. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'package:flutter_state_value_example/main.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() { - testWidgets( - 'Counter increments test', - (WidgetTester tester) async { - // Build our app and trigger a frame. - runZonedGuarded( - () async { - await tester.pumpWidget(const MyApp()); - await tester.pumpAndSettle(); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - expect(find.byType(LinearProgressIndicator), findsNothing); - - // Tap the refresh icon. - await tester.tap(find.byIcon(Icons.refresh)); - await tester.pump(); - - expect(find.byType(LinearProgressIndicator), findsOneWidget); - - await tester.pumpAndSettle(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); - expect(find.text('Expected error.'), findsNothing); - - // Tap the refresh icon. - await tester.tap(find.byIcon(Icons.refresh)); - await tester.pump(); - - expect(find.byType(LinearProgressIndicator), findsOneWidget); - - await tester.pumpAndSettle(); - - // Verify that our counter has incremented. - expect(find.text('Expected error.'), findsOneWidget); - expect(find.text('1'), findsOneWidget); - - // Tap the refresh icon. - await tester.tap(find.byIcon(Icons.refresh)); - await tester.pump(); - - expect(find.byType(LinearProgressIndicator), findsOneWidget); - - await tester.pumpAndSettle(); - - // Verify that our counter has incremented. - expect(find.text('Expected error.'), findsNothing); - expect(find.text('3'), findsOneWidget); - - await tester.tap(find.byIcon(Icons.refresh)); - await tester.pump(); - - expect(find.byType(LinearProgressIndicator), findsOneWidget); - - await tester.pumpAndSettle(); - - // Verify that our counter has incremented. - expect(find.text('Expected error.'), findsNothing); - expect(find.text('4'), findsOneWidget); - - await tester.tap(find.byIcon(Icons.refresh)); - await tester.pump(); - - expect(find.byType(LinearProgressIndicator), findsOneWidget); - - await tester.pumpAndSettle(); - - // Verify that our counter has incremented. - expect(find.text('Expected error.'), findsNothing); - expect(find.text('5'), findsNothing); - }, - (error, stack) { - if (error != 'Error') { - // 'Error' expected - throw error; - } - }, - ); - }, - ); -} diff --git a/packages/flutter_value_state/lib/flutter_value_state.dart b/packages/flutter_value_state/lib/flutter_value_state.dart deleted file mode 100644 index de34753..0000000 --- a/packages/flutter_value_state/lib/flutter_value_state.dart +++ /dev/null @@ -1,6 +0,0 @@ -library flutter_value_state; - -export 'package:value_state/value_state.dart'; - -export 'src/configuration.dart'; -export 'src/widgets.dart'; diff --git a/packages/flutter_value_state/lib/src/configuration.dart b/packages/flutter_value_state/lib/src/configuration.dart deleted file mode 100644 index 449c5fd..0000000 --- a/packages/flutter_value_state/lib/src/configuration.dart +++ /dev/null @@ -1,162 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:value_state/value_state.dart'; - -Widget _defaultBuilder(BuildContext context, BaseState state) => - const SizedBox.shrink(); - -Widget _defaultWrapper( - BuildContext context, BaseState state, Widget child) => - child; - -typedef OnValueStateWaiting = Widget Function( - BuildContext context, WaitingState state); - -typedef OnValueStateWithValue = Widget Function( - BuildContext context, WithValueState state, Widget? error); -typedef OnValueStateNoValue = Widget Function( - BuildContext context, NoValueState state); -typedef OnValueStateError = Widget Function( - BuildContext context, ErrorState state); -typedef OnValueStateDefault = Widget Function( - BuildContext context, BaseState state); -typedef OnValueStateWrapper = Widget Function( - BuildContext context, BaseState state, Widget child); - -/// Define default behavior for the states [WaitingState], [NoValueState], [ErrorState]. -/// [builderDefault] can be used when none of this callback is mentionned. -class ValueStateConfigurationData { - const ValueStateConfigurationData({ - OnValueStateWrapper? wrapper, - OnValueStateWaiting? builderWaiting, - OnValueStateNoValue? builderNoValue, - OnValueStateError? builderError, - OnValueStateDefault? builderDefault, - }) : _wrapper = wrapper, - _builderWaiting = builderWaiting, - _builderNoValue = builderNoValue, - _builderError = builderError, - _builderDefault = builderDefault; - - /// Builder for all states that will be wrapped by this builder. - OnValueStateWrapper get wrapper => _wrapper ?? _defaultWrapper; - final OnValueStateWrapper? _wrapper; - - /// Builder for [WaitingState]. - OnValueStateWaiting get builderWaiting => _builderWaiting ?? builderDefault; - final OnValueStateWaiting? _builderWaiting; - - /// Builder for [NoValueState]. - OnValueStateNoValue get builderNoValue => _builderNoValue ?? builderDefault; - final OnValueStateNoValue? _builderNoValue; - - /// Builder for [ErrorState]. - OnValueStateError get builderError => _builderError ?? builderDefault; - final OnValueStateError? _builderError; - - /// Fallback builder when one of the state builder is empty. - OnValueStateDefault get builderDefault => _builderDefault ?? _defaultBuilder; - final OnValueStateDefault? _builderDefault; - - /// Creates a copy of this [ValueStateConfigurationData] but with the given - /// fields replaced with the new values. - ValueStateConfigurationData copyWith({ - OnValueStateWrapper? wrapper, - OnValueStateWaiting? builderWaiting, - OnValueStateNoValue? builderNoValue, - OnValueStateError? builderError, - OnValueStateDefault? builderDefault, - }) => - ValueStateConfigurationData( - wrapper: wrapper ?? this.wrapper, - builderWaiting: builderWaiting ?? this.builderWaiting, - builderNoValue: builderNoValue ?? this.builderNoValue, - builderError: builderError ?? this.builderError, - builderDefault: builderDefault ?? this.builderDefault, - ); - - /// Creates a new [ValueStateConfigurationData] where each parameter - /// from this object has been merged with the matching attribute. - ValueStateConfigurationData merge( - ValueStateConfigurationData? configuration) { - final baseConfiguration = - configuration ?? const ValueStateConfigurationData(); - - return baseConfiguration.copyWith( - wrapper: _wrapper, - builderWaiting: _builderWaiting, - builderNoValue: _builderNoValue, - builderError: _builderError, - builderDefault: _builderDefault, - ); - } - - @override - bool operator ==(other) => - identical(this, other) || - runtimeType == other.runtimeType && - other is ValueStateConfigurationData && - wrapper == other.wrapper && - builderWaiting == other.builderWaiting && - builderNoValue == other.builderNoValue && - builderError == other.builderError && - builderDefault == other.builderDefault; - - @override - int get hashCode => Object.hash( - wrapper, - builderNoValue, - builderWaiting, - builderError, - builderDefault, - ); -} - -/// Provide a [ValueStateConfigurationData] for all inherited widget to define -/// default behavior for any state of [BaseState] except [ValueState]. -/// -/// If this configuration is in a subtree of another [ValueStateConfiguration], -/// the configuration will be merged with the parent one. -class ValueStateConfiguration extends StatelessWidget { - const ValueStateConfiguration({ - super.key, - required this.configuration, - required this.child, - }); - - /// The default to configuration. - final ValueStateConfigurationData configuration; - final Widget child; - - @override - Widget build(BuildContext context) { - final inheritedConfiguration = maybeOf(context); - - return _ValueStateConfiguration( - configuration: configuration.merge(inheritedConfiguration), - child: child); - } - - static ValueStateConfigurationData? maybeOf(BuildContext context) => context - .dependOnInheritedWidgetOfExactType<_ValueStateConfiguration>() - ?.configuration; - - static ValueStateConfigurationData of(BuildContext context) { - final ValueStateConfigurationData? configuration = maybeOf(context); - - assert( - configuration != null, 'No $ValueStateConfiguration found in context'); - - return configuration!; - } -} - -class _ValueStateConfiguration extends InheritedWidget { - const _ValueStateConfiguration( - {required this.configuration, required super.child}); - - final ValueStateConfigurationData configuration; - - @override - bool updateShouldNotify(covariant _ValueStateConfiguration oldWidget) => - configuration != oldWidget.configuration; -} diff --git a/packages/flutter_value_state/lib/src/widgets.dart b/packages/flutter_value_state/lib/src/widgets.dart deleted file mode 100644 index 6200e0c..0000000 --- a/packages/flutter_value_state/lib/src/widgets.dart +++ /dev/null @@ -1,149 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:value_state/value_state.dart'; - -import 'configuration.dart'; - -extension StateConfigurationExtensions on BuildContext { - ValueStateConfigurationData get stateConfiguration => - ValueStateConfiguration.maybeOf(this) ?? - const ValueStateConfigurationData(); -} - -extension ValueStateBuilderExtension on BaseState { - Widget buildWidget({ - Key? key, - OnValueStateWithValue? onValue, - OnValueStateWaiting? onWaiting, - OnValueStateNoValue? onNoValue, - OnValueStateError? onError, - OnValueStateDefault? onDefault, - OnValueStateWrapper? wrapper, - bool wrapped = true, - bool valueMixedWithError = false, - }) => - ValueStateWidget( - state: this, - onDefault: onDefault, - onError: onError, - onWithValue: onValue, - onNoValue: onNoValue, - onWaiting: onWaiting, - valueMixedWithError: valueMixedWithError, - wrapped: wrapped, - wrapper: wrapper, - ); -} - -class ValueStateWidget extends StatelessWidget { - const ValueStateWidget({ - required this.state, - this.onWithValue, - this.onWaiting, - this.onNoValue, - this.onError, - this.onDefault, - this.wrapper, - this.wrapped = true, - this.valueMixedWithError = false, - }); - - final BaseState state; - - final OnValueStateWithValue? onWithValue; - - final OnValueStateWaiting? onWaiting; - - final OnValueStateNoValue? onNoValue; - final OnValueStateError? onError; - final OnValueStateDefault? onDefault; - final OnValueStateWrapper? wrapper; - - final bool wrapped; - final bool valueMixedWithError; - - @override - Widget build(BuildContext context) { - final state = this.state; - if (state is WaitingState) { - return _buildWaitingState(context, state); - } else if (state is NoValueState) { - return _buildNoValueState(context, state); - } else if (state is ValueState) { - return _buildWithValueState(context, state); - } else if (state is ErrorState) { - return _buildErrorState(context, state); - } - - // coverage:ignore-start - throw UnimplementedError(); - // coverage:ignore-end - } - - Widget _builder( - BuildContext context, - BaseState state, - Widget Function( - BuildContext context, - ValueStateConfigurationData valueStateConfiguration, - OnValueStateDefault? onDefault, - ) builder, - ) { - final valueStateConfiguration = context.stateConfiguration; - Widget child = builder(context, valueStateConfiguration, onDefault); - - if (wrapper != null) { - child = wrapper!(context, state, child); - } - - return wrapped - ? valueStateConfiguration.wrapper(context, state, child) - : child; - } - - Widget _buildWaitingState(BuildContext context, WaitingState state) => - _builder(context, state, (context, valueStateConfiguration, onDefault) { - final onWaiting = this.onWaiting ?? - onDefault ?? - valueStateConfiguration.builderWaiting; - - return onWaiting(context, state); - }); - - Widget _buildNoValueState(BuildContext context, NoValueState state) => - _builder(context, state, (context, valueStateConfiguration, onDefault) { - final onNoValue = this.onNoValue ?? - onDefault ?? - valueStateConfiguration.builderNoValue; - - return onNoValue(context, state); - }); - - Widget _buildWithValueState(BuildContext context, WithValueState state) => - _builder(context, state, (context, valueStateConfiguration, onDefault) { - final onError = - this.onError ?? onDefault ?? valueStateConfiguration.builderError; - Widget? error; - - if (state is ErrorWithPreviousValue) { - error = onError(context, state); - } - - return onWithValue?.call(context, state, error) ?? - onDefault?.call(context, state) ?? - valueStateConfiguration.builderDefault(context, state); - }); - - Widget _buildErrorState(BuildContext context, ErrorState state) { - if (valueMixedWithError && state is ErrorWithPreviousValue) { - return _buildWithValueState(context, state); - } - - return _builder(context, state, - (context, valueStateConfiguration, onDefault) { - final onError = - this.onError ?? onDefault ?? valueStateConfiguration.builderError; - - return onError(context, state); - }); - } -} diff --git a/packages/flutter_value_state/pubspec.yaml b/packages/flutter_value_state/pubspec.yaml deleted file mode 100644 index 22f7889..0000000 --- a/packages/flutter_value_state/pubspec.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: flutter_value_state -description: A dart package that helps implements basic states for BLoC library -repository: https://github.com/devobs/value_state -homepage: https://github.com/devobs -version: 1.3.6 - -environment: - sdk: ">=2.17.5 <3.0.0" - flutter: ">=3.0.0" - -dependencies: - flutter: - sdk: flutter - - value_state: ^1.5.1 -dev_dependencies: - flutter_test: - sdk: flutter - - flutter_lints: ^2.0.0 - test: ^1.20.1 - stream_transform: ^2.0.0 diff --git a/packages/flutter_value_state/test/flutter_value_state_test.dart b/packages/flutter_value_state/test/flutter_value_state_test.dart deleted file mode 100644 index 2adad88..0000000 --- a/packages/flutter_value_state/test/flutter_value_state_test.dart +++ /dev/null @@ -1,401 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:flutter_value_state/flutter_value_state.dart'; - -const _buildWidgetKey = ValueKey('buildWidget'); -const _defaultWidgetKey = ValueKey('defaultWidget'); -const _errorWidgetKey = ValueKey('errorWidget'); -const _noValueWidgetKey = ValueKey('noValueWidget'); -const _waitingWidgetKey = ValueKey('waitingWidget'); -const _wrapperWidgetKey = ValueKey('wrapperWidget'); -const _defaultWidgetType = SizedBox; - -late ValueStateConfigurationData _valueStateConfigurationData; - -class _TestWidget> extends StatelessWidget { - const _TestWidget({ - required this.state, - this.valueMixedWithError = false, - this.child, - this.onWaiting, - this.onNoValue, - this.onError, - this.onDefault, - this.wrapper, - this.wrapped = true, - this.onValueEnabled = true, - }); - - final T state; - final bool valueMixedWithError; - - final Widget? child; - - final OnValueStateWaiting? onWaiting; - final OnValueStateNoValue? onNoValue; - final OnValueStateError? onError; - final OnValueStateDefault? onDefault; - final OnValueStateWrapper? wrapper; - - final bool wrapped; - final bool onValueEnabled; - - @override - Widget build(BuildContext context) { - return state.buildWidget( - onValue: onValueEnabled - ? (context, state, error) { - return Column( - children: [ - if (error != null) error, - child ?? const SizedBox.shrink(key: _buildWidgetKey), - ], - ); - } - : null, - valueMixedWithError: valueMixedWithError, - onDefault: onDefault, - onError: onError, - onNoValue: onNoValue, - onWaiting: onWaiting, - wrapper: wrapper, - wrapped: wrapped, - ); - } -} - -class _TestConfigurationWidget> - extends StatefulWidget { - const _TestConfigurationWidget({ - super.key, - required this.state, - this.valueMixedWithError = false, - this.child, - this.onWaiting, - this.onNoValue, - this.onError, - this.onDefault, - this.wrapper, - this.wrapped = true, - this.onValueEnabled = true, - }); - - final T state; - final bool valueMixedWithError; - - final Widget? child; - - final OnValueStateWaiting? onWaiting; - final OnValueStateNoValue? onNoValue; - final OnValueStateError? onError; - final OnValueStateDefault? onDefault; - final OnValueStateWrapper? wrapper; - - final bool wrapped; - final bool onValueEnabled; - - @override - State<_TestConfigurationWidget> createState() => - _TestConfigurationWidgetState(); -} - -class _TestConfigurationWidgetState> - extends State<_TestConfigurationWidget> { - OnValueStateError _onError = - (context, state) => const SizedBox.shrink(key: _errorWidgetKey); - - void updateOnError(OnValueStateError onError) { - setState(() { - _onError = onError; - }); - } - - @override - Widget build(BuildContext context) { - _valueStateConfigurationData = ValueStateConfigurationData( - builderDefault: (context, state) => - const SizedBox.shrink(key: _defaultWidgetKey), - builderError: _onError, - builderNoValue: (context, state) => - const SizedBox.shrink(key: _noValueWidgetKey), - builderWaiting: (context, state) => - const SizedBox.shrink(key: _waitingWidgetKey), - wrapper: (context, state, child) => - KeyedSubtree(key: _wrapperWidgetKey, child: child), - ); - - return ValueStateConfiguration( - configuration: _valueStateConfigurationData, - child: _TestWidget( - state: widget.state, - valueMixedWithError: widget.valueMixedWithError, - child: widget.child, - onDefault: widget.onDefault, - onError: widget.onError, - onNoValue: widget.onNoValue, - onWaiting: widget.onWaiting, - wrapper: widget.wrapper, - wrapped: widget.wrapped, - onValueEnabled: widget.onValueEnabled, - )); - } -} - -void main() { - test('$ValueStateConfiguration.copyWith without parameter', () { - const configuration = ValueStateConfigurationData(); - - expect(configuration.copyWith(), configuration); - }); - - group('without configuration', () { - testWidgets('buildWidget with ${ValueState}', (tester) async { - await tester.pumpWidget(const _TestWidget(state: ValueState(1))); - - expect(find.byKey(_buildWidgetKey), findsOneWidget); - expect(find.byType(_defaultWidgetType), findsOneWidget); - }); - - testWidgets('buildWidget without parameter with ${ValueState}', - (tester) async { - await tester.pumpWidget(const ValueState(1).buildWidget()); - - expect(find.byKey(_buildWidgetKey), findsNothing); - expect(find.byType(_defaultWidgetType), findsOneWidget); - }); - - for (final state in >[ - const InitState(), - const PendingState(), - // const ValueState(1), - const NoValueState(), - ErrorState( - previousState: const InitState(), - error: 'Error', - refreshing: false) - ]) { - testWidgets('defaultBuilder with ${state.runtimeType}', (tester) async { - await tester.pumpWidget(_TestWidget(state: state)); - - expect(find.byKey(_buildWidgetKey), findsNothing); - expect(find.byType(_defaultWidgetType), findsOneWidget); - }); - - testWidgets( - 'defaultBuilder with empty configuration ${state.runtimeType}', - (tester) async { - await tester.pumpWidget( - ValueStateConfiguration( - configuration: const ValueStateConfigurationData(), - child: _TestWidget(state: state)), - ); - - expect(find.byKey(_buildWidgetKey), findsNothing); - expect(find.byType(_defaultWidgetType), findsOneWidget); - }); - } - - for (final state in >[ - const InitState(), - const PendingState(), - // const ValueState(1), - const NoValueState(), - ErrorState( - previousState: const InitState(), - error: 'Error', - refreshing: false) - ]) { - testWidgets('defaultBuilder with ${state.runtimeType}', (tester) async { - await tester.pumpWidget(_TestWidget(state: state)); - - expect(find.byKey(_buildWidgetKey), findsNothing); - expect(find.byType(_defaultWidgetType), findsOneWidget); - }); - } - }); - - group('with configuration', () { - testWidgets('should get ValueStateConfigurationData', (tester) async { - late ValueStateConfigurationData valueStateConfigurationData; - await tester.pumpWidget(_TestConfigurationWidget( - state: const ValueState(1), - child: Builder(builder: (context) { - valueStateConfigurationData = ValueStateConfiguration.of(context); - return const SizedBox.shrink(); - }))); - - expect(valueStateConfigurationData, _valueStateConfigurationData); - expect(valueStateConfigurationData.hashCode, - _valueStateConfigurationData.hashCode); - }); - - testWidgets('buildWidget with ${ValueState}', (tester) async { - final testKey = GlobalKey<_TestConfigurationWidgetState>(); - await tester.pumpWidget(_TestConfigurationWidget( - key: testKey, - state: const ValueState(1), - )); - - expect(find.byKey(_buildWidgetKey), findsOneWidget); - expect(find.byType(_defaultWidgetType), findsOneWidget); - - testKey.currentState!.updateOnError((context, state) { - return Container(key: _errorWidgetKey); - }); - - expect(find.byKey(_buildWidgetKey), findsOneWidget); - expect(find.byType(_defaultWidgetType), findsOneWidget); - }); - - for (final state in , Key>{ - const InitState(): _waitingWidgetKey, - const PendingState(): _waitingWidgetKey, - const NoValueState(): _noValueWidgetKey, - ErrorState( - previousState: const InitState(), - error: 'Error', - refreshing: false): _errorWidgetKey, - ErrorState( - previousState: 0.toState(), - error: 'Error', - refreshing: false): _errorWidgetKey, - }.entries) { - testWidgets('build with ${state.key.runtimeType}', (tester) async { - await tester.pumpWidget(_TestConfigurationWidget(state: state.key)); - - expect(find.byKey(state.value), findsOneWidget); - expect(find.byType(_defaultWidgetType), findsOneWidget); - }); - - testWidgets('build with ${state.key.runtimeType} and onDefault', - (tester) async { - await tester.pumpWidget(_TestConfigurationWidget( - state: state.key, - onDefault: (context, state) => Container(key: _defaultWidgetKey), - valueMixedWithError: true, - onValueEnabled: false, - )); - - expect(find.byKey(state.value), findsNothing); - expect(find.byKey(_defaultWidgetKey), findsOneWidget); - }); - } - - for (final state in , Key>{ - const InitState(): _waitingWidgetKey, - const PendingState(): _waitingWidgetKey, - const NoValueState(): _noValueWidgetKey, - ErrorState( - previousState: const InitState(), - error: 'Error', - refreshing: false): _errorWidgetKey, - }.entries) { - const wrapperKey = Key('innerWrapperWidget'); - testWidgets( - 'build with ${state.key.runtimeType} and callbacks and wrapper', - (tester) async { - await tester.pumpWidget(_TestConfigurationWidget( - state: state.key, - onDefault: (context, state) => Container(key: _defaultWidgetKey), - onError: (context, state) => Container(key: _errorWidgetKey), - onNoValue: (context, state) => Container(key: _noValueWidgetKey), - onWaiting: (context, state) => Container(key: _waitingWidgetKey), - wrapper: (context, state, child) => - Center(key: wrapperKey, child: child), - )); - - expect(find.byKey(state.value), findsOneWidget); - expect(find.byKey(wrapperKey), findsOneWidget); - expect(find.byKey(_defaultWidgetKey), findsNothing); - expect(find.byType(_defaultWidgetType), findsNothing); - expect(find.byType(Container), findsOneWidget); - expect(find.byType(Center), findsOneWidget); - }); - - testWidgets( - 'build with ${state.key.runtimeType} and callbacks and wrapper disabled', - (tester) async { - await tester.pumpWidget(_TestConfigurationWidget( - state: state.key, - onDefault: (context, state) => Container(key: _defaultWidgetKey), - onError: (context, state) => Container(key: _errorWidgetKey), - onNoValue: (context, state) => Container(key: _noValueWidgetKey), - onWaiting: (context, state) => Container(key: _waitingWidgetKey), - wrapper: (context, state, child) => - Center(key: wrapperKey, child: child), - wrapped: false, - )); - - expect(find.byKey(state.value), findsOneWidget); - expect(find.byKey(wrapperKey), findsOneWidget); - expect(find.byKey(_defaultWidgetKey), findsNothing); - expect(find.byType(_defaultWidgetType), findsNothing); - expect(find.byType(Container), findsOneWidget); - expect(find.byType(Center), findsOneWidget); - }); - } - - testWidgets( - 'build with ${ErrorWithoutPreviousValue} with and valueMixedWithError', - (tester) async { - final testKey = GlobalKey<_TestConfigurationWidgetState>(); - await tester.pumpWidget(_TestConfigurationWidget( - key: testKey, - state: ErrorState( - previousState: const InitState(), - error: 'Error', - refreshing: false, - ), - valueMixedWithError: true, - )); - - expect(find.byKey(_buildWidgetKey), findsNothing); - expect(find.byKey(_errorWidgetKey), findsOneWidget); - expect(find.byType(_defaultWidgetType), findsOneWidget); - }); - - testWidgets('build with ${ErrorWithPreviousValue}', (tester) async { - final testKey = GlobalKey<_TestConfigurationWidgetState>(); - await tester.pumpWidget(_TestConfigurationWidget( - key: testKey, - state: ErrorState( - previousState: const ValueState(1), - error: 'Error', - refreshing: false, - ), - )); - - expect(find.byKey(_errorWidgetKey), findsOneWidget); - expect(find.byType(_defaultWidgetType), findsOneWidget); - - testKey.currentState!.updateOnError((context, state) { - return Container(key: _errorWidgetKey); - }); - - await tester.pumpAndSettle(); - - expect(find.byKey(_errorWidgetKey), findsOneWidget); - expect(find.byType(Container), findsOneWidget); - expect(find.byType(_defaultWidgetType), findsNothing); - }); - - testWidgets( - 'build with ${ErrorWithPreviousValue} with and valueMixedWithError', - (tester) async { - final testKey = GlobalKey<_TestConfigurationWidgetState>(); - await tester.pumpWidget(_TestConfigurationWidget( - key: testKey, - state: ErrorState( - previousState: const ValueState(1), - error: 'Error', - refreshing: false, - ), - valueMixedWithError: true, - )); - - expect(find.byKey(_buildWidgetKey), findsOneWidget); - expect(find.byKey(_errorWidgetKey), findsOneWidget); - expect(find.byType(_defaultWidgetType), findsNWidgets(2)); - }); - }); -} diff --git a/packages/value_cubit/.gitignore b/packages/value_cubit/.gitignore deleted file mode 100644 index c3e6113..0000000 --- a/packages/value_cubit/.gitignore +++ /dev/null @@ -1,29 +0,0 @@ -# Miscellaneous -*.class -*.log -*.pyc -*.swp -.DS_Store -.atom/ -.buildlog/ -.history -.svn/ - -# IntelliJ related -*.iml -*.ipr -*.iws -.idea/ - -# The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line -# is commented out by default. -#.vscode/ - -# Flutter/Dart/Pub related -# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. -pubspec.lock -**/doc/api/ -.dart_tool/ -.packages -build/ diff --git a/packages/value_cubit/CHANGELOG.md b/packages/value_cubit/CHANGELOG.md deleted file mode 100644 index 98ef2ae..0000000 --- a/packages/value_cubit/CHANGELOG.md +++ /dev/null @@ -1,80 +0,0 @@ -## 1.3.3 - - - **FIX**: no emit on closed bloc. - -## 1.3.2 - - - Update a dependency to the latest release. - -## 1.3.1 - - - **DOCS**: fix performOnState reference. - -## 1.3.0 - - - **FEAT**: added ValueCubitMixin.waitReady. - -## 1.2.5 - - - Update a dependency to the latest release. - -## 1.2.4 - - - Update a dependency to the latest release. - -## 1.2.3 - - - **FIX**: deprecation messages. - -## 1.2.2 - - - Update a dependency to the latest release. - -## 1.2.0 - -* Added pattern matching - -## 1.1.6 - -* Update README exemple - -## 1.1.5 - -* Simplification of the example - -## 1.1.4 - -* Fix diagram images - -## 1.1.2 - -* Simplify BaseState -* More understable example messages -* States modelization - -## 1.1.1 - -* Sanitize emitMappedState parameters - -## 1.1.0 - -* Upgrade depedencies to Flutter 3.0.1 -* `emitMappedState` on ValueState - -## 1.0.1 - -* Update documentation - -## 1.0.0 - -* New cubit class `RefreshValueCubit` -* New states `WithValueState`, `PendingState`, `ReadyState` and `ErrorWithPreviousValue` -* New helpers `StreamInputCubitMixin` (mixin on `ValueCubit`) -* New extension `behaviorSubject` (current state with stream) on `StateAndStream` -* Add examples : basic and flutter - -## 0.9.0 - -Initial version of the library. -* Includes states -* ValueCubit and mixins diff --git a/packages/value_cubit/LICENSE b/packages/value_cubit/LICENSE deleted file mode 100644 index d3f2fb0..0000000 --- a/packages/value_cubit/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) -Copyright (c) 2022 Emmanuel LEFEBVRE - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without restriction, -including without limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of the Software, -and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/packages/value_cubit/README.md b/packages/value_cubit/README.md deleted file mode 100644 index cc59f03..0000000 --- a/packages/value_cubit/README.md +++ /dev/null @@ -1,127 +0,0 @@ -A dart package that helps to implement basic states for [BLoC library](https://pub.dev/packages/bloc) to perform, load and fetch data. - - -[![pub package](https://img.shields.io/pub/v/value_cubit.svg)](https://pub.dev/packages/value_cubit) -[![Test](https://github.com/devobs/value_state/actions/workflows/test.yml/badge.svg)](https://github.com/devobs/value_state/actions/workflows/test.yml) -[![codecov](https://codecov.io/gh/devobs/value_state/branch/main/graph/badge.svg)](https://app.codecov.io/gh/devobs/value_state/tree/main/packages/value_cubit) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) - -## Features - -* Provides all necessary states for data : init, waiting, value/no value and error states (from [value_state](https://pub.dev/packages/value_state)), -* A `ValueCubit` class to simplify `Cubit` subclassing, -* A `RefreshValueCubit` class like `ValueCubit` with refreshing capabilities, -* Some helpers `perform` (an extension on `Cubit`) to emit intermediate states while an action is intended to update state : the same state is reemitted with attribute `refreshing` at `true`. - -## Usage - -This example shows how different value states from this library help developpers to show load step data widgets. - - -```dart -class CounterCubit extends ValueCubit { - var _value = 0; - - // Put your WS call that can be refreshed - Future _getCounterValueFromWebService() async => _value++; - - Future increment() => perform(() async { - // [perform] generate intermediate or final states such as PendingState, - // concrete subclass of ReadyState with right [ReadyState.refreshing] value - // or ErrorState if an error is raised. - final result = await _getCounterValueFromWebService(); - - emit(ValueState(result)); - }); - - void clear() { - _value = 0; - emit(const PendingState()); - } -} - -class MyHomePage extends StatelessWidget { - const MyHomePage({super.key}); - - @override - Widget build(BuildContext context) { - final theme = Theme.of(context); - - return BlocBuilder>(builder: (context, state) { - return Scaffold( - appBar: AppBar( - title: const Text('Flutter Demo Home Page'), - ), - body: DefaultTextStyle( - style: const TextStyle(fontSize: 24), - textAlign: TextAlign.center, - child: state is ReadyState - ? Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - if (state.refreshing) const LinearProgressIndicator(), - const Spacer(), - if (state.hasError) - Text('Expected error.', - style: TextStyle(color: theme.colorScheme.error)), - if (state is WithValueState) ...[ - if (state.hasError) - const Text('Previous counter value :') - else - const Text('Actual counter value :'), - Text( - state.value.toString(), - style: theme.textTheme.headlineMedium, - ), - ], - if (state is NoValueState) const Text('No Value'), - const Spacer(), - ], - ) - : const Center(child: CircularProgressIndicator()), - ), - floatingActionButton: state is! ReadyState - ? null - : FloatingActionButton( - onPressed: state.refreshing - ? null - : context.read().increment, - tooltip: 'Increment', - child: state.refreshing - ? SizedBox.square( - dimension: 20, - child: CircularProgressIndicator( - color: theme.colorScheme.onPrimary)) - : const Icon(Icons.refresh)), - ); - }); - } -} -``` - -The whole code of this example is available in [example](example). - - -If your cubit is only a getter with the need to refresh your cubit state, you can simplify the implementation `ValueCubit` with `RefreshValueCubit`. - -```dart -class CounterCubit extends RefreshValueCubit { - var _value = 0; - - // Put your WS call that can be refreshed - Future _getCounterValueFromWebService() async => _value++; - - @override - Future emitValues() async { - final result = await _getCounterValueFromWebService(); - - emit(ValueState(result)); - } -} -``` - -Update your value (increment in our example) by calling `myCubit.refresh()`. - -## Feedback - -Please file any issues, bugs or feature requests as an issue on the [Github page](https://github.com/devobs/value_state/issues). diff --git a/packages/value_cubit/analysis_options.yaml b/packages/value_cubit/analysis_options.yaml deleted file mode 100644 index 0caccc8..0000000 --- a/packages/value_cubit/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../analysis_options.yaml \ No newline at end of file diff --git a/packages/value_cubit/example/.gitignore b/packages/value_cubit/example/.gitignore deleted file mode 100644 index 0fa6b67..0000000 --- a/packages/value_cubit/example/.gitignore +++ /dev/null @@ -1,46 +0,0 @@ -# Miscellaneous -*.class -*.log -*.pyc -*.swp -.DS_Store -.atom/ -.buildlog/ -.history -.svn/ - -# IntelliJ related -*.iml -*.ipr -*.iws -.idea/ - -# The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line -# is commented out by default. -#.vscode/ - -# Flutter/Dart/Pub related -**/doc/api/ -**/ios/Flutter/.last_build_id -.dart_tool/ -.flutter-plugins -.flutter-plugins-dependencies -.packages -.pub-cache/ -.pub/ -/build/ - -# Web related -lib/generated_plugin_registrant.dart - -# Symbolication related -app.*.symbols - -# Obfuscation related -app.*.map.json - -# Android Studio will place build artifacts here -/android/app/debug -/android/app/profile -/android/app/release diff --git a/packages/value_cubit/example/.metadata b/packages/value_cubit/example/.metadata deleted file mode 100644 index 166a998..0000000 --- a/packages/value_cubit/example/.metadata +++ /dev/null @@ -1,10 +0,0 @@ -# This file tracks properties of this Flutter project. -# Used by Flutter tool to assess capabilities and perform upgrades etc. -# -# This file should be version controlled and should not be manually edited. - -version: - revision: c860cba910319332564e1e9d470a17074c1f2dfd - channel: stable - -project_type: app diff --git a/packages/value_cubit/example/README.md b/packages/value_cubit/example/README.md deleted file mode 100644 index b96eb8d..0000000 --- a/packages/value_cubit/example/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# flutter_basic - -A basic example of value cubit usage. - -## Getting Started - -Just type to launch in navigator : -```bash -flutter run -``` \ No newline at end of file diff --git a/packages/value_cubit/example/analysis_options.yaml b/packages/value_cubit/example/analysis_options.yaml deleted file mode 100644 index 0caccc8..0000000 --- a/packages/value_cubit/example/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../analysis_options.yaml \ No newline at end of file diff --git a/packages/value_cubit/example/lib/cubit.dart b/packages/value_cubit/example/lib/cubit.dart deleted file mode 100644 index 593d101..0000000 --- a/packages/value_cubit/example/lib/cubit.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:value_cubit/value_cubit.dart'; - -class CounterCubit extends ValueCubit { - var _value = 0; - Future _getMyValueFromRepository() async => _value++; - - CounterCubit() { - increment(); - } - - Future increment() => perform(() async { - await Future.delayed(const Duration(seconds: 1)); - - final result = await _getMyValueFromRepository(); - - if (result == 2) { - throw 'Error'; - } else if (result > 4) { - emit(const NoValueState()); - } else { - emit(ValueState(result)); - } - }); -} diff --git a/packages/value_cubit/example/lib/main.dart b/packages/value_cubit/example/lib/main.dart deleted file mode 100644 index 7b94733..0000000 --- a/packages/value_cubit/example/lib/main.dart +++ /dev/null @@ -1,83 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:value_state/value_state.dart'; - -import 'cubit.dart'; - -// coverage:ignore-start -void main() { - runApp(const MyApp()); -} -// coverage:ignore-end - -class MyApp extends StatelessWidget { - const MyApp({super.key}); - - // This widget is the root of your application. - @override - Widget build(BuildContext context) { - return BlocProvider( - create: (_) => CounterCubit(), - child: const MaterialApp( - title: 'Value Cubit Demo', - home: MyHomePage(), - )); - } -} - -class MyHomePage extends StatelessWidget { - const MyHomePage({super.key}); - - @override - Widget build(BuildContext context) { - final theme = Theme.of(context); - - return BlocBuilder>(builder: (context, state) { - return Scaffold( - appBar: AppBar( - title: const Text('Flutter Demo Home Page'), - ), - body: DefaultTextStyle( - style: const TextStyle(fontSize: 24), - textAlign: TextAlign.center, - child: state is ReadyState - ? Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - if (state.refreshing) const LinearProgressIndicator(), - const Spacer(), - if (state.hasError) - Text('Expected error.', - style: TextStyle(color: theme.colorScheme.error)), - if (state is WithValueState) ...[ - if (state.hasError) - const Text('Previous counter value :') - else - const Text('Actual counter value :'), - Text( - state.value.toString(), - style: theme.textTheme.headlineMedium, - ), - ], - const Spacer(), - ], - ) - : const Center(child: CircularProgressIndicator()), - ), - floatingActionButton: state is! ReadyState - ? null - : FloatingActionButton( - onPressed: state.refreshing - ? null - : context.read().increment, - tooltip: 'Increment', - child: state.refreshing - ? SizedBox.square( - dimension: 20, - child: CircularProgressIndicator( - color: theme.colorScheme.onPrimary)) - : const Icon(Icons.refresh)), - ); - }); - } -} diff --git a/packages/value_cubit/example/pubspec.yaml b/packages/value_cubit/example/pubspec.yaml deleted file mode 100644 index 2da97da..0000000 --- a/packages/value_cubit/example/pubspec.yaml +++ /dev/null @@ -1,26 +0,0 @@ -name: flutter_basic -description: A sample with a basic example - -publish_to: 'none' - -version: 1.0.0+1 - -environment: - sdk: ">=2.17.1 <3.0.0" - -dependencies: - flutter: - sdk: flutter - - flutter_bloc: ^8.0.1 - - value_state: 1.5.1 - value_cubit: 1.3.3 -dev_dependencies: - flutter_test: - sdk: flutter - - flutter_lints: ^2.0.0 - -flutter: - uses-material-design: true diff --git a/packages/value_cubit/example/web/favicon.png b/packages/value_cubit/example/web/favicon.png deleted file mode 100644 index 8aaa46ac1ae21512746f852a42ba87e4165dfdd1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM diff --git a/packages/value_cubit/example/web/icons/Icon-192.png b/packages/value_cubit/example/web/icons/Icon-192.png deleted file mode 100644 index b749bfef07473333cf1dd31e9eed89862a5d52aa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5292 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 diff --git a/packages/value_cubit/example/web/icons/Icon-512.png b/packages/value_cubit/example/web/icons/Icon-512.png deleted file mode 100644 index 88cfd48dff1169879ba46840804b412fe02fefd6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8252 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 diff --git a/packages/value_cubit/example/web/icons/Icon-maskable-192.png b/packages/value_cubit/example/web/icons/Icon-maskable-192.png deleted file mode 100644 index eb9b4d76e525556d5d89141648c724331630325d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5594 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 X>c!)3PgKfL&lX^$Z>Cpu&6)6jvi^Z! diff --git a/packages/value_cubit/example/web/icons/Icon-maskable-512.png b/packages/value_cubit/example/web/icons/Icon-maskable-512.png deleted file mode 100644 index d69c56691fbdb0b7efa65097c7cc1edac12a6d3e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20998 zcmeFZ_gj-)&^4Nb2tlbLMU<{!p(#yjqEe+=0IA_oih%ScH9@5#MNp&}Y#;;(h=A0@ zh7{>lT2MkSQ344eAvrhici!td|HJuyvJm#Y_w1Q9Yu3!26dNlO-oxUDK_C#XnW^Co z5C{VN6#{~B0)K2j7}*1Xq(Nqemv23A-6&=ZpEijkVnSwVGqLv40?n0=p;k3-U5e5+ z+z3>aS`u9DS=!wg8ROu?X4TFoW6CFLL&{GzoVT)ldhLekLM|+j3tIxRd|*5=c{=s&*vfPdBr(Fyj(v@%eQj1Soy7m4^@VRl1~@-PV7y+c!xz$8436WBn$t{=}mEdK#k`aystimGgI{(IBx$!pAwFoE9Y`^t^;> zKAD)C(Dl^s%`?q5$P|fZf8Xymrtu^Pv(7D`rn>Z-w$Ahs!z9!94WNVxrJuXfHAaxg zC6s@|Z1$7R$(!#t%Jb{{s6(Y?NoQXDYq)!}X@jKPhe`{9KQ@sAU8y-5`xt?S9$jKH zoi}6m5PcG*^{kjvt+kwPpyQzVg4o)a>;LK`aaN2x4@itBD3Aq?yWTM20VRn1rrd+2 zKO=P0rMjEGq_UqpMa`~7B|p?xAN1SCoCp}QxAv8O`jLJ5CVh@umR%c%i^)6!o+~`F zaalSTQcl5iwOLC&H)efzd{8(88mo`GI(56T<(&p7>Qd^;R1hn1Y~jN~tApaL8>##U zd65bo8)79CplWxr#z4!6HvLz&N7_5AN#x;kLG?zQ(#p|lj<8VUlKY=Aw!ATqeL-VG z42gA!^cMNPj>(`ZMEbCrnkg*QTsn*u(nQPWI9pA{MQ=IsPTzd7q5E#7+z>Ch=fx$~ z;J|?(5jTo5UWGvsJa(Sx0?S#56+8SD!I^tftyeh_{5_31l6&Hywtn`bbqYDqGZXI( zCG7hBgvksX2ak8+)hB4jnxlO@A32C_RM&g&qDSb~3kM&)@A_j1*oTO@nicGUyv+%^ z=vB)4(q!ykzT==Z)3*3{atJ5}2PV*?Uw+HhN&+RvKvZL3p9E?gHjv{6zM!A|z|UHK z-r6jeLxbGn0D@q5aBzlco|nG2tr}N@m;CJX(4#Cn&p&sLKwzLFx1A5izu?X_X4x8r@K*d~7>t1~ zDW1Mv5O&WOxbzFC`DQ6yNJ(^u9vJdj$fl2dq`!Yba_0^vQHXV)vqv1gssZYzBct!j zHr9>ydtM8wIs}HI4=E}qAkv|BPWzh3^_yLH(|kdb?x56^BlDC)diWyPd*|f!`^12_U>TD^^94OCN0lVv~Sgvs94ecpE^}VY$w`qr_>Ue zTfH~;C<3H<0dS5Rkf_f@1x$Gms}gK#&k()IC0zb^QbR!YLoll)c$Agfi6MKI0dP_L z=Uou&u~~^2onea2%XZ@>`0x^L8CK6=I{ge;|HXMj)-@o~h&O{CuuwBX8pVqjJ*o}5 z#8&oF_p=uSo~8vn?R0!AMWvcbZmsrj{ZswRt(aEdbi~;HeVqIe)-6*1L%5u$Gbs}| zjFh?KL&U(rC2izSGtwP5FnsR@6$-1toz?RvLD^k~h9NfZgzHE7m!!7s6(;)RKo2z} zB$Ci@h({l?arO+vF;s35h=|WpefaOtKVx>l399}EsX@Oe3>>4MPy%h&^3N_`UTAHJ zI$u(|TYC~E4)|JwkWW3F!Tib=NzjHs5ii2uj0^m|Qlh-2VnB#+X~RZ|`SA*}}&8j9IDv?F;(Y^1=Z0?wWz;ikB zewU>MAXDi~O7a~?jx1x=&8GcR-fTp>{2Q`7#BE#N6D@FCp`?ht-<1|y(NArxE_WIu zP+GuG=Qq>SHWtS2M>34xwEw^uvo4|9)4s|Ac=ud?nHQ>ax@LvBqusFcjH0}{T3ZPQ zLO1l<@B_d-(IS682}5KA&qT1+{3jxKolW+1zL4inqBS-D>BohA!K5++41tM@ z@xe<-qz27}LnV#5lk&iC40M||JRmZ*A##K3+!j93eouU8@q-`W0r%7N`V$cR&JV;iX(@cS{#*5Q>~4BEDA)EikLSP@>Oo&Bt1Z~&0d5)COI%3$cLB_M?dK# z{yv2OqW!al-#AEs&QFd;WL5zCcp)JmCKJEdNsJlL9K@MnPegK23?G|O%v`@N{rIRa zi^7a}WBCD77@VQ-z_v{ZdRsWYrYgC$<^gRQwMCi6);%R~uIi31OMS}=gUTE(GKmCI z$zM>mytL{uNN+a&S38^ez(UT=iSw=l2f+a4)DyCA1Cs_N-r?Q@$3KTYosY!;pzQ0k zzh1G|kWCJjc(oZVBji@kN%)UBw(s{KaYGy=i{g3{)Z+&H8t2`^IuLLKWT6lL<-C(! zSF9K4xd-|VO;4}$s?Z7J_dYqD#Mt)WCDnsR{Kpjq275uUq6`v0y*!PHyS(}Zmv)_{>Vose9-$h8P0|y;YG)Bo}$(3Z%+Gs0RBmFiW!^5tBmDK-g zfe5%B*27ib+7|A*Fx5e)2%kIxh7xWoc3pZcXS2zik!63lAG1;sC1ja>BqH7D zODdi5lKW$$AFvxgC-l-)!c+9@YMC7a`w?G(P#MeEQ5xID#<}W$3bSmJ`8V*x2^3qz zVe<^^_8GHqYGF$nIQm0Xq2kAgYtm#UC1A(=&85w;rmg#v906 zT;RyMgbMpYOmS&S9c38^40oUp?!}#_84`aEVw;T;r%gTZkWeU;;FwM@0y0adt{-OK z(vGnPSlR=Nv2OUN!2=xazlnHPM9EWxXg2EKf0kI{iQb#FoP>xCB<)QY>OAM$Dcdbm zU6dU|%Mo(~avBYSjRc13@|s>axhrPl@Sr81{RSZUdz4(=|82XEbV*JAX6Lfbgqgz584lYgi0 z2-E{0XCVON$wHfvaLs;=dqhQJ&6aLn$D#0i(FkAVrXG9LGm3pSTf&f~RQb6|1_;W> z?n-;&hrq*~L=(;u#jS`*Yvh@3hU-33y_Kv1nxqrsf>pHVF&|OKkoC)4DWK%I!yq?P z=vXo8*_1iEWo8xCa{HJ4tzxOmqS0&$q+>LroMKI*V-rxhOc%3Y!)Y|N6p4PLE>Yek>Y(^KRECg8<|%g*nQib_Yc#A5q8Io z6Ig&V>k|~>B6KE%h4reAo*DfOH)_01tE0nWOxX0*YTJgyw7moaI^7gW*WBAeiLbD?FV9GSB zPv3`SX*^GRBM;zledO`!EbdBO_J@fEy)B{-XUTVQv}Qf~PSDpK9+@I`7G7|>Dgbbu z_7sX9%spVo$%qwRwgzq7!_N;#Td08m5HV#?^dF-EV1o)Q=Oa+rs2xH#g;ykLbwtCh znUnA^dW!XjspJ;otq$yV@I^s9Up(5k7rqhQd@OLMyyxVLj_+$#Vc*}Usevp^I(^vH zmDgHc0VMme|K&X?9&lkN{yq_(If)O`oUPW8X}1R5pSVBpfJe0t{sPA(F#`eONTh_) zxeLqHMfJX#?P(@6w4CqRE@Eiza; z;^5)Kk=^5)KDvd9Q<`=sJU8rjjxPmtWMTmzcH={o$U)j=QBuHarp?=}c??!`3d=H$nrJMyr3L-& zA#m?t(NqLM?I3mGgWA_C+0}BWy3-Gj7bR+d+U?n*mN$%5P`ugrB{PeV>jDUn;eVc- zzeMB1mI4?fVJatrNyq|+zn=!AiN~<}eoM#4uSx^K?Iw>P2*r=k`$<3kT00BE_1c(02MRz4(Hq`L^M&xt!pV2 zn+#U3@j~PUR>xIy+P>51iPayk-mqIK_5rlQMSe5&tDkKJk_$i(X&;K(11YGpEc-K= zq4Ln%^j>Zi_+Ae9eYEq_<`D+ddb8_aY!N;)(&EHFAk@Ekg&41ABmOXfWTo)Z&KotA zh*jgDGFYQ^y=m)<_LCWB+v48DTJw*5dwMm_YP0*_{@HANValf?kV-Ic3xsC}#x2h8 z`q5}d8IRmqWk%gR)s~M}(Qas5+`np^jW^oEd-pzERRPMXj$kS17g?H#4^trtKtq;C?;c ztd|%|WP2w2Nzg@)^V}!Gv++QF2!@FP9~DFVISRW6S?eP{H;;8EH;{>X_}NGj^0cg@ z!2@A>-CTcoN02^r6@c~^QUa={0xwK0v4i-tQ9wQq^=q*-{;zJ{Qe%7Qd!&X2>rV@4 z&wznCz*63_vw4>ZF8~%QCM?=vfzW0r_4O^>UA@otm_!N%mH)!ERy&b!n3*E*@?9d^ zu}s^By@FAhG(%?xgJMuMzuJw2&@$-oK>n z=UF}rt%vuaP9fzIFCYN-1&b#r^Cl6RDFIWsEsM|ROf`E?O(cy{BPO2Ie~kT+^kI^i zp>Kbc@C?}3vy-$ZFVX#-cx)Xj&G^ibX{pWggtr(%^?HeQL@Z( zM-430g<{>vT*)jK4aY9(a{lSy{8vxLbP~n1MXwM527ne#SHCC^F_2@o`>c>>KCq9c(4c$VSyMl*y3Nq1s+!DF| z^?d9PipQN(mw^j~{wJ^VOXDCaL$UtwwTpyv8IAwGOg<|NSghkAR1GSNLZ1JwdGJYm zP}t<=5=sNNUEjc=g(y)1n5)ynX(_$1-uGuDR*6Y^Wgg(LT)Jp><5X|}bt z_qMa&QP?l_n+iVS>v%s2Li_;AIeC=Ca^v1jX4*gvB$?H?2%ndnqOaK5-J%7a} zIF{qYa&NfVY}(fmS0OmXA70{znljBOiv5Yod!vFU{D~*3B3Ka{P8?^ zfhlF6o7aNT$qi8(w<}OPw5fqA7HUje*r*Oa(YV%*l0|9FP9KW@U&{VSW{&b0?@y)M zs%4k1Ax;TGYuZ9l;vP5@?3oQsp3)rjBeBvQQ>^B;z5pc=(yHhHtq6|0m(h4envn_j787fizY@V`o(!SSyE7vlMT zbo=Z1c=atz*G!kwzGB;*uPL$Ei|EbZLh8o+1BUMOpnU(uX&OG1MV@|!&HOOeU#t^x zr9=w2ow!SsTuJWT7%Wmt14U_M*3XiWBWHxqCVZI0_g0`}*^&yEG9RK9fHK8e+S^m? zfCNn$JTswUVbiC#>|=wS{t>-MI1aYPLtzO5y|LJ9nm>L6*wpr_m!)A2Fb1RceX&*|5|MwrvOk4+!0p99B9AgP*9D{Yt|x=X}O% zgIG$MrTB=n-!q%ROT|SzH#A$Xm;|ym)0>1KR}Yl0hr-KO&qMrV+0Ej3d@?FcgZ+B3 ztEk16g#2)@x=(ko8k7^Tq$*5pfZHC@O@}`SmzT1(V@x&NkZNM2F#Q-Go7-uf_zKC( zB(lHZ=3@dHaCOf6C!6i8rDL%~XM@rVTJbZL09?ht@r^Z_6x}}atLjvH^4Vk#Ibf(^LiBJFqorm?A=lE zzFmwvp4bT@Nv2V>YQT92X;t9<2s|Ru5#w?wCvlhcHLcsq0TaFLKy(?nzezJ>CECqj zggrI~Hd4LudM(m{L@ezfnpELsRFVFw>fx;CqZtie`$BXRn#Ns%AdoE$-Pf~{9A8rV zf7FbgpKmVzmvn-z(g+&+-ID=v`;6=)itq8oM*+Uz**SMm_{%eP_c0{<%1JGiZS19o z@Gj7$Se~0lsu}w!%;L%~mIAO;AY-2i`9A*ZfFs=X!LTd6nWOZ7BZH2M{l2*I>Xu)0 z`<=;ObglnXcVk!T>e$H?El}ra0WmPZ$YAN0#$?|1v26^(quQre8;k20*dpd4N{i=b zuN=y}_ew9SlE~R{2+Rh^7%PA1H5X(p8%0TpJ=cqa$65XL)$#ign-y!qij3;2>j}I; ziO@O|aYfn&up5F`YtjGw68rD3{OSGNYmBnl?zdwY$=RFsegTZ=kkzRQ`r7ZjQP!H( zp4>)&zf<*N!tI00xzm-ME_a{_I!TbDCr;8E;kCH4LlL-tqLxDuBn-+xgPk37S&S2^ z2QZumkIimwz!c@!r0)j3*(jPIs*V!iLTRl0Cpt_UVNUgGZzdvs0(-yUghJfKr7;=h zD~y?OJ-bWJg;VdZ^r@vlDoeGV&8^--!t1AsIMZ5S440HCVr%uk- z2wV>!W1WCvFB~p$P$$_}|H5>uBeAe>`N1FI8AxM|pq%oNs;ED8x+tb44E) zTj{^fbh@eLi%5AqT?;d>Es5D*Fi{Bpk)q$^iF!!U`r2hHAO_?#!aYmf>G+jHsES4W zgpTKY59d?hsb~F0WE&dUp6lPt;Pm zcbTUqRryw^%{ViNW%Z(o8}dd00H(H-MmQmOiTq{}_rnwOr*Ybo7*}3W-qBT!#s0Ie z-s<1rvvJx_W;ViUD`04%1pra*Yw0BcGe)fDKUK8aF#BwBwMPU;9`!6E(~!043?SZx z13K%z@$$#2%2ovVlgFIPp7Q6(vO)ud)=*%ZSucL2Dh~K4B|%q4KnSpj#n@(0B})!9 z8p*hY@5)NDn^&Pmo;|!>erSYg`LkO?0FB@PLqRvc>4IsUM5O&>rRv|IBRxi(RX(gJ ztQ2;??L~&Mv;aVr5Q@(?y^DGo%pO^~zijld41aA0KKsy_6FeHIn?fNHP-z>$OoWer zjZ5hFQTy*-f7KENRiCE$ZOp4|+Wah|2=n@|W=o}bFM}Y@0e62+_|#fND5cwa3;P{^pEzlJbF1Yq^}>=wy8^^^$I2M_MH(4Dw{F6hm+vrWV5!q;oX z;tTNhz5`-V={ew|bD$?qcF^WPR{L(E%~XG8eJx(DoGzt2G{l8r!QPJ>kpHeOvCv#w zr=SSwMDaUX^*~v%6K%O~i)<^6`{go>a3IdfZ8hFmz&;Y@P%ZygShQZ2DSHd`m5AR= zx$wWU06;GYwXOf(%MFyj{8rPFXD};JCe85Bdp4$YJ2$TzZ7Gr#+SwCvBI1o$QP0(c zy`P51FEBV2HTisM3bHqpmECT@H!Y2-bv2*SoSPoO?wLe{M#zDTy@ujAZ!Izzky~3k zRA1RQIIoC*Mej1PH!sUgtkR0VCNMX(_!b65mo66iM*KQ7xT8t2eev$v#&YdUXKwGm z7okYAqYF&bveHeu6M5p9xheRCTiU8PFeb1_Rht0VVSbm%|1cOVobc8mvqcw!RjrMRM#~=7xibH&Fa5Imc|lZ{eC|R__)OrFg4@X_ ze+kk*_sDNG5^ELmHnZ7Ue?)#6!O)#Nv*Dl2mr#2)w{#i-;}0*_h4A%HidnmclH#;Q zmQbq+P4DS%3}PpPm7K_K3d2s#k~x+PlTul7+kIKol0@`YN1NG=+&PYTS->AdzPv!> zQvzT=)9se*Jr1Yq+C{wbK82gAX`NkbXFZ)4==j4t51{|-v!!$H8@WKA={d>CWRW+g z*`L>9rRucS`vbXu0rzA1#AQ(W?6)}1+oJSF=80Kf_2r~Qm-EJ6bbB3k`80rCv(0d` zvCf3;L2ovYG_TES%6vSuoKfIHC6w;V31!oqHM8-I8AFzcd^+_86!EcCOX|Ta9k1!s z_Vh(EGIIsI3fb&dF$9V8v(sTBC%!#<&KIGF;R+;MyC0~}$gC}}= zR`DbUVc&Bx`lYykFZ4{R{xRaUQkWCGCQlEc;!mf=+nOk$RUg*7 z;kP7CVLEc$CA7@6VFpsp3_t~m)W0aPxjsA3e5U%SfY{tp5BV5jH-5n?YX7*+U+Zs%LGR>U- z!x4Y_|4{gx?ZPJobISy991O znrmrC3otC;#4^&Rg_iK}XH(XX+eUHN0@Oe06hJk}F?`$)KmH^eWz@@N%wEc)%>?Ft z#9QAroDeyfztQ5Qe{m*#R#T%-h*&XvSEn@N$hYRTCMXS|EPwzF3IIysD2waj`vQD{ zv_#^Pgr?s~I*NE=acf@dWVRNWTr(GN0wrL)Z2=`Dr>}&ZDNX|+^Anl{Di%v1Id$_p zK5_H5`RDjJx`BW7hc85|> zHMMsWJ4KTMRHGu+vy*kBEMjz*^K8VtU=bXJYdhdZ-?jTXa$&n)C?QQIZ7ln$qbGlr zS*TYE+ppOrI@AoPP=VI-OXm}FzgXRL)OPvR$a_=SsC<3Jb+>5makX|U!}3lx4tX&L z^C<{9TggZNoeX!P1jX_K5HkEVnQ#s2&c#umzV6s2U-Q;({l+j^?hi7JnQ7&&*oOy9 z(|0asVTWUCiCnjcOnB2pN0DpuTglKq;&SFOQ3pUdye*eT<2()7WKbXp1qq9=bhMWlF-7BHT|i3TEIT77AcjD(v=I207wi-=vyiw5mxgPdTVUC z&h^FEUrXwWs9en2C{ywZp;nvS(Mb$8sBEh-*_d-OEm%~p1b2EpcwUdf<~zmJmaSTO zSX&&GGCEz-M^)G$fBvLC2q@wM$;n4jp+mt0MJFLuJ%c`tSp8$xuP|G81GEd2ci$|M z4XmH{5$j?rqDWoL4vs!}W&!?!rtj=6WKJcE>)?NVske(p;|#>vL|M_$as=mi-n-()a*OU3Okmk0wC<9y7t^D(er-&jEEak2!NnDiOQ99Wx8{S8}=Ng!e0tzj*#T)+%7;aM$ z&H}|o|J1p{IK0Q7JggAwipvHvko6>Epmh4RFRUr}$*2K4dz85o7|3#Bec9SQ4Y*;> zXWjT~f+d)dp_J`sV*!w>B%)#GI_;USp7?0810&3S=WntGZ)+tzhZ+!|=XlQ&@G@~3 z-dw@I1>9n1{+!x^Hz|xC+P#Ab`E@=vY?3%Bc!Po~e&&&)Qp85!I|U<-fCXy*wMa&t zgDk!l;gk;$taOCV$&60z+}_$ykz=Ea*)wJQ3-M|p*EK(cvtIre0Pta~(95J7zoxBN zS(yE^3?>88AL0Wfuou$BM{lR1hkrRibz=+I9ccwd`ZC*{NNqL)3pCcw^ygMmrG^Yp zn5f}Xf>%gncC=Yq96;rnfp4FQL#{!Y*->e82rHgY4Zwy{`JH}b9*qr^VA{%~Z}jtp z_t$PlS6}5{NtTqXHN?uI8ut8rOaD#F1C^ls73S=b_yI#iZDOGz3#^L@YheGd>L;<( z)U=iYj;`{>VDNzIxcjbTk-X3keXR8Xbc`A$o5# zKGSk-7YcoBYuAFFSCjGi;7b<;n-*`USs)IX z=0q6WZ=L!)PkYtZE-6)azhXV|+?IVGTOmMCHjhkBjfy@k1>?yFO3u!)@cl{fFAXnRYsWk)kpT?X{_$J=|?g@Q}+kFw|%n!;Zo}|HE@j=SFMvT8v`6Y zNO;tXN^036nOB2%=KzxB?n~NQ1K8IO*UE{;Xy;N^ZNI#P+hRZOaHATz9(=)w=QwV# z`z3+P>9b?l-@$@P3<;w@O1BdKh+H;jo#_%rr!ute{|YX4g5}n?O7Mq^01S5;+lABE+7`&_?mR_z7k|Ja#8h{!~j)| zbBX;*fsbUak_!kXU%HfJ2J+G7;inu#uRjMb|8a){=^))y236LDZ$$q3LRlat1D)%7K0!q5hT5V1j3qHc7MG9 z_)Q=yQ>rs>3%l=vu$#VVd$&IgO}Za#?aN!xY>-<3PhzS&q!N<=1Q7VJBfHjug^4|) z*fW^;%3}P7X#W3d;tUs3;`O&>;NKZBMR8au6>7?QriJ@gBaorz-+`pUWOP73DJL=M z(33uT6Gz@Sv40F6bN|H=lpcO z^AJl}&=TIjdevuDQ!w0K*6oZ2JBOhb31q!XDArFyKpz!I$p4|;c}@^bX{>AXdt7Bm zaLTk?c%h@%xq02reu~;t@$bv`b3i(P=g}~ywgSFpM;}b$zAD+=I!7`V~}ARB(Wx0C(EAq@?GuxOL9X+ffbkn3+Op0*80TqmpAq~EXmv%cq36celXmRz z%0(!oMp&2?`W)ALA&#|fu)MFp{V~~zIIixOxY^YtO5^FSox8v$#d0*{qk0Z)pNTt0QVZ^$`4vImEB>;Lo2!7K05TpY-sl#sWBz_W-aDIV`Ksabi zvpa#93Svo!70W*Ydh)Qzm{0?CU`y;T^ITg-J9nfWeZ-sbw)G@W?$Eomf%Bg2frfh5 zRm1{|E0+(4zXy){$}uC3%Y-mSA2-^I>Tw|gQx|7TDli_hB>``)Q^aZ`LJC2V3U$SABP}T)%}9g2pF9dT}aC~!rFFgkl1J$ z`^z{Arn3On-m%}r}TGF8KQe*OjSJ=T|caa_E;v89A{t@$yT^(G9=N9F?^kT*#s3qhJq!IH5|AhnqFd z0B&^gm3w;YbMNUKU>naBAO@fbz zqw=n!@--}o5;k6DvTW9pw)IJVz;X}ncbPVrmH>4x);8cx;q3UyiML1PWp%bxSiS|^ zC5!kc4qw%NSOGQ*Kcd#&$30=lDvs#*4W4q0u8E02U)7d=!W7+NouEyuF1dyH$D@G& zaFaxo9Ex|ZXA5y{eZT*i*dP~INSMAi@mvEX@q5i<&o&#sM}Df?Og8n8Ku4vOux=T% zeuw~z1hR}ZNwTn8KsQHKLwe2>p^K`YWUJEdVEl|mO21Bov!D0D$qPoOv=vJJ`)|%_ z>l%`eexY7t{BlVKP!`a^U@nM?#9OC*t76My_E_<16vCz1x_#82qj2PkWiMWgF8bM9 z(1t4VdHcJ;B~;Q%x01k_gQ0>u2*OjuEWNOGX#4}+N?Gb5;+NQMqp}Puqw2HnkYuKA zzKFWGHc&K>gwVgI1Sc9OT1s6fq=>$gZU!!xsilA$fF`kLdGoX*^t}ao@+^WBpk>`8 z4v_~gK|c2rCq#DZ+H)$3v~Hoi=)=1D==e3P zpKrRQ+>O^cyTuWJ%2}__0Z9SM_z9rptd*;-9uC1tDw4+A!=+K%8~M&+Zk#13hY$Y$ zo-8$*8dD5@}XDi19RjK6T^J~DIXbF5w&l?JLHMrf0 zLv0{7*G!==o|B%$V!a=EtVHdMwXLtmO~vl}P6;S(R2Q>*kTJK~!}gloxj)m|_LYK{ zl(f1cB=EON&wVFwK?MGn^nWuh@f95SHatPs(jcwSY#Dnl1@_gkOJ5=f`%s$ZHljRH0 z+c%lrb=Gi&N&1>^L_}#m>=U=(oT^vTA&3!xXNyqi$pdW1BDJ#^{h|2tZc{t^vag3& zAD7*8C`chNF|27itjBUo^CCDyEpJLX3&u+(L;YeeMwnXEoyN(ytoEabcl$lSgx~Ltatn}b$@j_yyMrBb03)shJE*$;Mw=;mZd&8e>IzE+4WIoH zCSZE7WthNUL$|Y#m!Hn?x7V1CK}V`KwW2D$-7&ODy5Cj;!_tTOOo1Mm%(RUt)#$@3 zhurA)t<7qik%%1Et+N1?R#hdBB#LdQ7{%-C zn$(`5e0eFh(#c*hvF>WT*07fk$N_631?W>kfjySN8^XC9diiOd#s?4tybICF;wBjp zIPzilX3{j%4u7blhq)tnaOBZ_`h_JqHXuI7SuIlNTgBk9{HIS&3|SEPfrvcE<@}E` zKk$y*nzsqZ{J{uWW9;#n=de&&h>m#A#q)#zRonr(?mDOYU&h&aQWD;?Z(22wY?t$U3qo`?{+amA$^TkxL+Ex2dh`q7iR&TPd0Ymwzo#b? zP$#t=elB5?k$#uE$K>C$YZbYUX_JgnXA`oF_Ifz4H7LEOW~{Gww&3s=wH4+j8*TU| zSX%LtJWqhr-xGNSe{;(16kxnak6RnZ{0qZ^kJI5X*It_YuynSpi(^-}Lolr{)#z_~ zw!(J-8%7Ybo^c3(mED`Xz8xecP35a6M8HarxRn%+NJBE;dw>>Y2T&;jzRd4FSDO3T zt*y+zXCtZQ0bP0yf6HRpD|WmzP;DR^-g^}{z~0x~z4j8m zucTe%k&S9Nt-?Jb^gYW1w6!Y3AUZ0Jcq;pJ)Exz%7k+mUOm6%ApjjSmflfKwBo6`B zhNb@$NHTJ>guaj9S{@DX)!6)b-Shav=DNKWy(V00k(D!v?PAR0f0vDNq*#mYmUp6> z76KxbFDw5U{{qx{BRj(>?|C`82ICKbfLxoldov-M?4Xl+3;I4GzLHyPOzYw7{WQST zPNYcx5onA%MAO9??41Po*1zW(Y%Zzn06-lUp{s<3!_9vv9HBjT02On0Hf$}NP;wF) zP<`2p3}A^~1YbvOh{ePMx$!JGUPX-tbBzp3mDZMY;}h;sQ->!p97GA)9a|tF(Gh{1$xk7 zUw?ELkT({Xw!KIr);kTRb1b|UL`r2_`a+&UFVCdJ)1T#fdh;71EQl9790Br0m_`$x z9|ZANuchFci8GNZ{XbP=+uXSJRe(;V5laQz$u18#?X*9}x7cIEbnr%<=1cX3EIu7$ zhHW6pe5M(&qEtsqRa>?)*{O;OJT+YUhG5{km|YI7I@JL_3Hwao9aXneiSA~a* z|Lp@c-oMNyeAEuUz{F?kuou3x#C*gU?lon!RC1s37gW^0Frc`lqQWH&(J4NoZg3m8 z;Lin#8Q+cFPD7MCzj}#|ws7b@?D9Q4dVjS4dpco=4yX5SSH=A@U@yqPdp@?g?qeia zH=Tt_9)G=6C2QIPsi-QipnK(mc0xXIN;j$WLf@n8eYvMk;*H-Q4tK%(3$CN}NGgO8n}fD~+>?<3UzvsrMf*J~%i;VKQHbF%TPalFi=#sgj)(P#SM^0Q=Tr>4kJVw8X3iWsP|e8tj}NjlMdWp z@2+M4HQu~3!=bZpjh;;DIDk&X}=c8~kn)FWWH z2KL1w^rA5&1@@^X%MjZ7;u(kH=YhH2pJPFQe=hn>tZd5RC5cfGYis8s9PKaxi*}-s6*W zRA^PwR=y^5Z){!(4D9-KC;0~;b*ploznFOaU`bJ_7U?qAi#mTo!&rIECRL$_y@yI27x2?W+zqDBD5~KCVYKFZLK+>ABC(Kj zeAll)KMgIlAG`r^rS{loBrGLtzhHY8$)<_S<(Dpkr(Ym@@vnQ&rS@FC*>2@XCH}M+an74WcRDcoQ+a3@A z9tYhl5$z7bMdTvD2r&jztBuo37?*k~wcU9GK2-)MTFS-lux-mIRYUuGUCI~V$?s#< z?1qAWb(?ZLm(N>%S%y10COdaq_Tm5c^%ooIxpR=`3e4C|@O5wY+eLik&XVi5oT7oe zmxH)Jd*5eo@!7t`x8!K=-+zJ-Sz)B_V$)s1pW~CDU$=q^&ABvf6S|?TOMB-RIm@CoFg>mjIQE)?+A1_3s6zmFU_oW&BqyMz1mY*IcP_2knjq5 zqw~JK(cVsmzc7*EvTT2rvpeqhg)W=%TOZ^>f`rD4|7Z5fq*2D^lpCttIg#ictgqZ$P@ru6P#f$x#KfnfTZj~LG6U_d-kE~`;kU_X)`H5so@?C zWmb!7x|xk@0L~0JFall*@ltyiL^)@3m4MqC7(7H0sH!WidId1#f#6R{Q&A!XzO1IAcIx;$k66dumt6lpUw@nL2MvqJ5^kbOVZ<^2jt5-njy|2@`07}0w z;M%I1$FCoLy`8xp8Tk)bFr;7aJeQ9KK6p=O$U0-&JYYy8woV*>b+FB?xLX`=pirYM z5K$BA(u)+jR{?O2r$c_Qvl?M{=Ar{yQ!UVsVn4k@0!b?_lA;dVz9uaQUgBH8Oz(Sb zrEs;&Ey>_ex8&!N{PmQjp+-Hlh|OA&wvDai#GpU=^-B70V0*LF=^bi+Nhe_o|azZ%~ZZ1$}LTmWt4aoB1 zPgccm$EwYU+jrdBaQFxQfn5gd(gM`Y*Ro1n&Zi?j=(>T3kmf94vdhf?AuS8>$Va#P zGL5F+VHpxdsCUa}+RqavXCobI-@B;WJbMphpK2%6t=XvKWWE|ruvREgM+|V=i6;;O zx$g=7^`$XWn0fu!gF=Xe9cMB8Z_SelD>&o&{1XFS`|nInK3BXlaeD*rc;R-#osyIS zWv&>~^TLIyBB6oDX+#>3<_0+2C4u2zK^wmHXXDD9_)kmLYJ!0SzM|%G9{pi)`X$uf zW}|%%#LgyK7m(4{V&?x_0KEDq56tk|0YNY~B(Sr|>WVz-pO3A##}$JCT}5P7DY+@W z#gJv>pA5>$|E3WO2tV7G^SuymB?tY`ooKcN3!vaQMnBNk-WATF{-$#}FyzgtJ8M^; zUK6KWSG)}6**+rZ&?o@PK3??uN{Q)#+bDP9i1W&j)oaU5d0bIWJ_9T5ac!qc?x66Q z$KUSZ`nYY94qfN_dpTFr8OW~A?}LD;Yty-BA)-be5Z3S#t2Io%q+cAbnGj1t$|qFR z9o?8B7OA^KjCYL=-!p}w(dkC^G6Nd%_I=1))PC0w5}ZZGJxfK)jP4Fwa@b-SYBw?% zdz9B-<`*B2dOn(N;mcTm%Do)rIvfXRNFX&1h`?>Rzuj~Wx)$p13nrDlS8-jwq@e@n zNIj_|8or==8~1h*Ih?w*8K7rYkGlwlTWAwLKc5}~dfz3y`kM&^Q|@C%1VAp_$wnw6zG~W4O+^ z>i?NY?oXf^Puc~+fDM$VgRNBpOZj{2cMP~gCqWAX4 z7>%$ux8@a&_B(pt``KSt;r+sR-$N;jdpY>|pyvPiN)9ohd*>mVST3wMo)){`B(&eX z1?zZJ-4u9NZ|~j1rdZYq4R$?swf}<6(#ex%7r{kh%U@kT)&kWuAszS%oJts=*OcL9 zaZwK<5DZw%1IFHXgFplP6JiL^dk8+SgM$D?8X+gE4172hXh!WeqIO>}$I9?Nry$*S zQ#f)RuH{P7RwA3v9f<-w>{PSzom;>(i&^l{E0(&Xp4A-*q-@{W1oE3K;1zb{&n28dSC2$N+6auXe0}e4b z)KLJ?5c*>@9K#I^)W;uU_Z`enquTUxr>mNq z1{0_puF-M7j${rs!dxxo3EelGodF1TvjV;Zpo;s{5f1pyCuRp=HDZ?s#IA4f?h|-p zGd|Mq^4hDa@Bh!c4ZE?O&x&XZ_ptZGYK4$9F4~{%R!}G1leCBx`dtNUS|K zL-7J5s4W@%mhXg1!}a4PD%!t&Qn%f_oquRajn3@C*)`o&K9o7V6DwzVMEhjVdDJ1fjhr#@=lp#@4EBqi=CCQ>73>R(>QKPNM&_Jpe5G`n4wegeC`FYEPJ{|vwS>$-`fuRSp3927qOv|NC3T3G-0 zA{K`|+tQy1yqE$ShWt8ny&5~)%ITb@^+x$w0)f&om;P8B)@}=Wzy59BwUfZ1vqw87 za2lB8J(&*l#(V}Id8SyQ0C(2amzkz3EqG&Ed0Jq1)$|&>4_|NIe=5|n=3?siFV0fI z{As5DLW^gs|B-b4C;Hd(SM-S~GQhzb>HgF2|2Usww0nL^;x@1eaB)=+Clj+$fF@H( z-fqP??~QMT$KI-#m;QC*&6vkp&8699G3)Bq0*kFZXINw=b9OVaed(3(3kS|IZ)CM? zJdnW&%t8MveBuK21uiYj)_a{Fnw0OErMzMN?d$QoPwkhOwcP&p+t>P)4tHlYw-pPN z^oJ=uc$Sl>pv@fZH~ZqxSvdhF@F1s=oZawpr^-#l{IIOGG=T%QXjtwPhIg-F@k@uIlr?J->Ia zpEUQ*=4g|XYn4Gez&aHr*;t$u3oODPmc2Ku)2Og|xjc%w;q!Zz+zY)*3{7V8bK4;& zYV82FZ+8?v)`J|G1w4I0fWdKg|2b#iaazCv;|?(W-q}$o&Y}Q5d@BRk^jL7#{kbCK zSgkyu;=DV+or2)AxCBgq-nj5=@n^`%T#V+xBGEkW4lCqrE)LMv#f;AvD__cQ@Eg3`~x| zW+h9mofSXCq5|M)9|ez(#X?-sxB%Go8};sJ?2abp(Y!lyi>k)|{M*Z$c{e1-K4ky` MPgg&ebxsLQ025IeI{*Lx diff --git a/packages/value_cubit/example/web/index.html b/packages/value_cubit/example/web/index.html deleted file mode 100644 index febe6a3..0000000 --- a/packages/value_cubit/example/web/index.html +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - - - - - - - - - - - - - - - flutter_basic - - - - - - - diff --git a/packages/value_cubit/example/web/manifest.json b/packages/value_cubit/example/web/manifest.json deleted file mode 100644 index b8dd7fc..0000000 --- a/packages/value_cubit/example/web/manifest.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "flutter_basic", - "short_name": "flutter_basic", - "start_url": ".", - "display": "standalone", - "background_color": "#0175C2", - "theme_color": "#0175C2", - "description": "A new Flutter project.", - "orientation": "portrait-primary", - "prefer_related_applications": false, - "icons": [ - { - "src": "icons/Icon-192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "icons/Icon-512.png", - "sizes": "512x512", - "type": "image/png" - }, - { - "src": "icons/Icon-maskable-192.png", - "sizes": "192x192", - "type": "image/png", - "purpose": "maskable" - }, - { - "src": "icons/Icon-maskable-512.png", - "sizes": "512x512", - "type": "image/png", - "purpose": "maskable" - } - ] -} diff --git a/packages/value_cubit/lib/src/cubit.dart b/packages/value_cubit/lib/src/cubit.dart deleted file mode 100644 index 445338b..0000000 --- a/packages/value_cubit/lib/src/cubit.dart +++ /dev/null @@ -1,92 +0,0 @@ -import 'dart:async'; - -import 'package:bloc/bloc.dart'; -import 'package:meta/meta.dart'; -import 'package:synchronized/synchronized.dart'; -import 'package:value_state/value_state.dart'; - -/// Shortbut to user [BaseState] with [Cubit] -abstract class ValueCubit extends Cubit> with ValueCubitMixin { - ValueCubit([BaseState? initState]) : super(initState ?? InitState()); -} - -/// Shared implementation to handle refresh capability on cubit -abstract class RefreshValueCubit extends ValueCubit - with RefreshValueCubitMixin { - RefreshValueCubit([BaseState? initState]) - : super(initState ?? InitState()); -} - -/// Shared implementation to handle refresh capability on cubit -mixin RefreshValueCubitMixin on ValueCubitMixin { - /// Refresh the cubit state. - Future refresh() async { - await perform(emitValues); - } - - /// Init the state of cubit. - void clear() { - emit(PendingState()); - } - - /// Get the value here and emit a [ValueState] if success. - @protected - Future emitValues(); -} - -@Deprecated( - 'CubitValueStateMixin will be dropped in 2.0, use ValueCubitMixin instead.') -typedef CubitValueStateMixin = ValueCubitMixin; - -/// Shared implementation of [perform]. -mixin ValueCubitMixin on BlocBase> { - /// Ensure that [perform] executions are sequential. - final _performValueCubitLock = Lock(reentrant: true); - - /// Handle states (waiting, refreshing, error...) while an [action] is - /// processed. - /// If [errorAsState] is `true` and [action] raise an exception then an - /// [ErrorState] is emitted. if `false`, nothing is emitted. The exception - /// is always rethrown by [perform] to be handled by the caller. - @protected - Future perform(FutureOr Function() action, - {bool errorAsState = true}) => - _performValueCubitLock.synchronized( - () => performOnState( - state: () => state, - emitter: (state) { - if (!isClosed) { - emit(state); - } - }, - action: (state, emitter) => action()), - ); - - /// Return `true` when a [ReadyState] is emitted. - /// Return `false` if this bloc is closed before a [ReadyState] is emitted. - Future waitReady() async { - if (state is! ReadyState) { - final result = await stream.firstWhere((state) => state is ReadyState, - orElse: () => PendingState()); - - return result is ReadyState; - } - - return true; - } -} - -/// Execute [ValueCubitMixin.perform] on each cubit of a list. -/// Useful for cubits that are suscribed to others. -@Deprecated('This feature will be dropped in 2.0.') -Future performOnIterable( - Iterable cubits, FutureOr Function() action, - {bool errorAsState = true}) async { - if (cubits.isEmpty) { - return await action(); - } - - return performOnIterable(cubits.skip(1), - () => cubits.first.perform(action, errorAsState: errorAsState), - errorAsState: errorAsState); -} diff --git a/packages/value_cubit/lib/src/extensions.dart b/packages/value_cubit/lib/src/extensions.dart deleted file mode 100644 index e186c82..0000000 --- a/packages/value_cubit/lib/src/extensions.dart +++ /dev/null @@ -1,65 +0,0 @@ -import 'dart:async'; - -import 'package:bloc/bloc.dart'; -import 'package:meta/meta.dart'; -import 'package:stream_transform/stream_transform.dart'; -import 'package:value_state/value_state.dart'; - -import 'cubit.dart'; - -/// Extensions for cubit to -extension StateAndStream on Cubit { - /// Get a new stream with current state as first value and the following - /// values - Stream get behaviorSubject => Stream.value(state).followedBy(stream); -} - -/// This mixin help to listen a stream an then update the current cubit -mixin StreamInputCubitMixin on ValueCubit { - late StreamSubscription _refreshStreamSubscription; - - /// Listen the [stream] and call [emitValuesFromStream] for every event. - @protected - void listenRefreshStream(Stream stream, - Future Function(EVENT event) emitValuesFromStream) { - _refreshStreamSubscription = stream.listen(emitValuesFromStream); - } - - @override - Future close() async { - await _refreshStreamSubscription.cancel(); - return super.close(); - } - - /// Helper to map from a state to other state. Useful to map "default" states - /// from original stream. - /// The [map] argument contains a function that map the origin event from the - /// stream to the value. If `null` is returned, then a [NoValueState] is - /// emitted. Else a [ValueState] is emitted with the value returned inside. - /// [fromState] is the origin state to map. - /// If the optional parameter [refreshingWithCurrentState] is `true` (default - /// value), then the cubit emit the current state refreshing if original - /// stream emit a refreshing state. Else, the refreshing is mapped from - /// original stream. - /// [mapInit], [mapPending], [mapNoValue] and [mapError] override the default - /// behavior of the mapper. - void emitMappedState( - T? Function(F from) map, - BaseState fromState, { - bool refreshingWithCurrentState = true, - WaitingMapperType? mapInit, - WaitingMapperType? mapPending, - RefreshingyMapperType? mapNoValue, - ErrorMapperType? mapError, - }) { - emit(mapState( - map, - fromState, - currentState: refreshingWithCurrentState ? state : null, - mapInit: mapInit, - mapPending: mapPending, - mapNoValue: mapNoValue, - mapError: mapError, - )); - } -} diff --git a/packages/value_cubit/lib/value_cubit.dart b/packages/value_cubit/lib/value_cubit.dart deleted file mode 100644 index a6d3d14..0000000 --- a/packages/value_cubit/lib/value_cubit.dart +++ /dev/null @@ -1,6 +0,0 @@ -library value_cubit; - -export 'package:value_state/value_state.dart'; - -export 'src/cubit.dart'; -export 'src/extensions.dart'; diff --git a/packages/value_cubit/pubspec.yaml b/packages/value_cubit/pubspec.yaml deleted file mode 100644 index f6b7f77..0000000 --- a/packages/value_cubit/pubspec.yaml +++ /dev/null @@ -1,20 +0,0 @@ -name: value_cubit -description: A dart package that helps implements basic states for BLoC library -repository: https://github.com/devobs/value_state -homepage: https://github.com/devobs -version: 1.3.3 - -environment: - sdk: ">=2.17.1 <3.0.0" - -dependencies: - bloc: ^8.1.0 - - meta: ^1.7.0 - stream_transform: ^2.0.0 - synchronized: ^3.0.0 - - value_state: ^1.5.1 -dev_dependencies: - lints: ^2.0.0 - test: ^1.20.1 diff --git a/packages/value_cubit/test/cubit.dart b/packages/value_cubit/test/cubit.dart deleted file mode 100644 index b83328a..0000000 --- a/packages/value_cubit/test/cubit.dart +++ /dev/null @@ -1,46 +0,0 @@ -import 'package:test/expect.dart'; -import 'package:value_cubit/value_cubit.dart'; - -class CounterCubit extends RefreshValueCubit { - var _value = 0; - Future _getMyValueFromRepository() async => _value++; - - @override - Future emitValues() => incrementValue(); - - Future incrementValue() async { - final result = await _getMyValueFromRepository(); - - switch (result) { - case 2: - emit(const NoValueState()); - break; - case 3: - case 4: - case 6: - fail('Error'); - default: - emit(ValueState(result)); - } - } -} - -void cubitStandardActions(CounterCubit counterCubit) { - counterCubit.refresh(); - counterCubit.refresh(); - counterCubit.refresh(); - counterCubit.refresh().onError((error, stackTrace) { - // Ignore error - }); - counterCubit.refresh().onError((error, stackTrace) { - // Ignore error - }); - counterCubit.refresh(); - counterCubit.refresh().onError((error, stackTrace) { - // Ignore error - }); - counterCubit.refresh(); - counterCubit.refresh().then((_) { - counterCubit.clear(); - }); -} diff --git a/packages/value_cubit/test/helpers_cubit_test.dart b/packages/value_cubit/test/helpers_cubit_test.dart deleted file mode 100644 index 083b284..0000000 --- a/packages/value_cubit/test/helpers_cubit_test.dart +++ /dev/null @@ -1,275 +0,0 @@ -import 'package:test/test.dart'; -import 'package:value_cubit/value_cubit.dart'; - -import 'cubit.dart'; - -class CounterCubitListener extends ValueCubit - with StreamInputCubitMixin> { - CounterCubitListener( - {required CounterCubit counterCubit, required bool variant}) { - var ignoreMapError = false; - listenRefreshStream(counterCubit.behaviorSubject, (state) async { - if (!ignoreMapError && state is WithValueState && state.value > 4) { - ignoreMapError = true; - } - - emitMappedState( - (from) => variant && from == 1 ? null : from + 1, state, - refreshingWithCurrentState: !variant, - mapError: variant && !ignoreMapError - ? (errorState) { - return NoValueState(refreshing: errorState.refreshing); - } - : null, - mapNoValue: variant - ? (refreshing) { - return ValueState(-1, refreshing: refreshing); - } - : null); - }); - } -} - -void main() { - late CounterCubit counterCubit; - late CounterCubitListener counterCubitListener; - late CounterCubitListener counterCubitListener2; - - setUp(() { - counterCubit = CounterCubit(); - counterCubitListener = - CounterCubitListener(counterCubit: counterCubit, variant: false); - counterCubitListener2 = - CounterCubitListener(counterCubit: counterCubit, variant: true); - }); - - tearDown(() async { - await counterCubitListener2.close(); - await counterCubitListener.close(); - await counterCubit.close(); - }); - - test('with values incremented', () { - expect(counterCubitListener.state, isA>()); - cubitStandardActions(counterCubit); - - expect( - counterCubitListener.stream, - emitsInOrder([ - isA>() - .having((state) => state.refreshing, 'first value not refreshing', - false) - .having((state) => state.value, 'first value', 1), - isA>() - .having((state) => state.refreshing, - 'second value not refreshing', true) - .having((state) => state.value, 'second value', 1), - isA>() - .having((state) => state.refreshing, - 'second value not refreshing', false) - .having((state) => state.value, 'second value', 2), - // refresh with no value after value - isA>() - .having( - (state) => state.refreshing, 'second value refreshing', true) - .having((state) => state.value, 'second value', 2), - isA>() - .having((state) => state.refreshing, 'no value', false), - isA>() - .having((state) => state.refreshing, 'no value refreshing', true), - isA>() - .having((state) => state.refreshing, - 'error for third value not refreshing', false) - .having( - (state) => state.stateBeforeError, - 'no value before erreur', - isA>() - .having((state) => state.refreshing, 'no value', false), - ) - .having((state) => state.hasValue, 'second value before erreur', - false), - // refresh with error after error - isA>().having( - (state) => state.refreshing, - 'error for third value refreshing', - true), - isA>() - .having((state) => state.refreshing, - 'error for fourth value not refreshing', false) - .having( - (state) => state.stateBeforeError, - 'no value before erreur', - isA>() - .having((state) => state.refreshing, 'no value', false), - ) - .having((state) => state.hasValue, 'second value before erreur', - false), - // refresh after arror - isA>().having( - (state) => state.refreshing, - 'error for fourth value refreshing', - true), - isA>() - .having((state) => state.refreshing, 'fifth value not refreshing', - false) - .having((state) => state.value, 'fifth value ', 6), - isA>() - .having( - (state) => state.refreshing, 'fifth value refreshing', true) - .having((state) => state.value, 'fifth value', 6), - isA>() - .having((state) => state.refreshing, - 'error for sixth value refreshing', false) - .having( - (state) => state.hasValue, 'error for sixth has value', true) - .having((state) => state.value, - 'error for sixth value refreshing', 6), - isA>().having((state) => state.refreshing, - 'error for sixth value refreshing', true), - isA>() - .having((state) => state.refreshing, - 'seventh value not refreshing', false) - .having((state) => state.value, 'seventh value', 8), - isA>() - .having( - (state) => state.refreshing, 'seventh value refreshing', true) - .having((state) => state.value, 'seventh value', 8), - isA>() - .having((state) => state.refreshing, - 'eighth value not refreshing', false) - .having((state) => state.value, 'eighth value', 9), - // after _myRefresh.clear() triggered - isA>(), - ])); - }); - - test('with values incremented and variant', () { - expect(counterCubitListener2.state, isA>()); - cubitStandardActions(counterCubit); - - expect( - counterCubitListener2.stream, - emitsInOrder([ - isA>() - .having((state) => state.refreshing, 'first value not refreshing', - false) - .having((state) => state.value, 'first value', 1), - isA>() - .having((state) => state.refreshing, - 'second value not refreshing', true) - .having((state) => state.value, 'second value', 1), - isA>().having((state) => state.refreshing, - 'error for fourth value not refreshing', false), - isA>().having((state) => state.refreshing, - 'error for fourth value refreshing', true), - isA>() - .having((state) => state.refreshing, - 'second value not refreshing', false) - .having((state) => state.value, 'second value', -1), - isA>() - .having( - (state) => state.refreshing, 'second value refreshing', true) - .having((state) => state.value, 'second value', -1), - // refresh with error after error - isA>().having((state) => state.refreshing, - 'error for fourth value not refreshing', false), - isA>().having((state) => state.refreshing, - 'error for fourth value refreshing', true), - isA>().having((state) => state.refreshing, - 'error for fourth value not refreshing', false), - isA>().having((state) => state.refreshing, - 'error for fourth value refreshing', true), - // refresh after arror - isA>() - .having((state) => state.refreshing, 'fifth value not refreshing', - false) - .having((state) => state.value, 'fifth value ', 6), - isA>() - .having( - (state) => state.refreshing, 'fifth value refreshing', true) - .having((state) => state.value, 'fifth value', 6), - isA>() - .having((state) => state.refreshing, - 'error for sixth value refreshing', false) - .having( - (state) => state.hasValue, 'error for sixth has value', true) - .having((state) => state.value, - 'error for sixth value refreshing', 6), - isA>().having((state) => state.refreshing, - 'error for sixth value refreshing', true), - isA>() - .having((state) => state.refreshing, - 'seventh value not refreshing', false) - .having((state) => state.value, 'seventh value', 8), - isA>() - .having( - (state) => state.refreshing, 'seventh value refreshing', true) - .having((state) => state.value, 'seventh value', 8), - isA>() - .having((state) => state.refreshing, - 'eighth value not refreshing', false) - .having((state) => state.value, 'eighth value', 9), - // after _myRefresh.clear() triggered - isA>(), - ])); - }); - - test('performIterable', () { - final counterCubit2 = CounterCubit(); - - expect(counterCubit2.state, isA>()); - - expect( - // ignore: deprecated_member_use_from_same_package - performOnIterable([counterCubit, counterCubit2], () async { - await counterCubit.refresh(); - await counterCubit2.refresh(); - - return 'Success'; - }).then((res) { - // ignore: deprecated_member_use_from_same_package - return performOnIterable([counterCubit, counterCubit2], () async { - await counterCubit.refresh(); - - return res; - }); - }), - completion('Success'), - ); - - expect( - counterCubit.stream, - emitsInOrder([ - const PendingState(), - isA>() - .having((state) => state.refreshing, 'first value not refreshing', - false) - .having((state) => state.value, 'first value', 0), - isA>() - .having((state) => state.refreshing, - 'second value not refreshing', true) - .having((state) => state.value, 'second value', 0), - isA>() - .having((state) => state.refreshing, - 'second value not refreshing', false) - .having((state) => state.value, 'second value', 1), - ])); - - expect( - counterCubit2.stream, - emitsInOrder([ - isA>() - .having((state) => state.refreshing, 'first value not refreshing', - false) - .having((state) => state.value, 'first value', 0), - isA>() - .having((state) => state.refreshing, - 'second value not refreshing', true) - .having((state) => state.value, 'second value', 0), - isA>() - .having((state) => state.refreshing, - 'second value not refreshing', false) - .having((state) => state.value, 'second value not changed', 0), - ])); - }); -} diff --git a/packages/value_cubit/test/value_cubit_test.dart b/packages/value_cubit/test/value_cubit_test.dart deleted file mode 100644 index 28c4242..0000000 --- a/packages/value_cubit/test/value_cubit_test.dart +++ /dev/null @@ -1,211 +0,0 @@ -import 'package:test/test.dart'; -import 'package:value_state/value_state.dart'; - -import 'cubit.dart'; - -void main() { - late CounterCubit counterCubit; - - setUp(() { - counterCubit = CounterCubit(); - }); - - tearDown(() async { - await counterCubit.close(); - }); - - test('with values incremented', () { - expect(counterCubit.state, isA>()); - - cubitStandardActions(counterCubit); - - // Ensure the current state is [WaitingState] instead of [InitState] - expect(counterCubit.state, isNot(isA>())); - expect(counterCubit.state, isA>()); - - expect( - counterCubit.stream, - emitsInOrder([ - isA>() - .having((state) => state.refreshing, 'first value not refreshing', - false) - .having((state) => state.value, 'first value', 0), - isA>() - .having((state) => state.refreshing, - 'second value not refreshing', true) - .having((state) => state.value, 'second value', 0), - isA>() - .having((state) => state.refreshing, - 'second value not refreshing', false) - .having((state) => state.value, 'second value', 1), - // refresh with no value after value - isA>() - .having( - (state) => state.refreshing, 'second value refreshing', true) - .having((state) => state.value, 'second value', 1), - isA>() - .having((state) => state.refreshing, 'no value', false), - isA>() - .having((state) => state.refreshing, 'no value refreshing', true), - isA>() - .having((state) => state.refreshing, - 'error for third value not refreshing', false) - .having( - (state) => state.stateBeforeError, - 'no value before erreur', - isA>() - .having((state) => state.refreshing, 'no value', false), - ) - .having((state) => state.hasValue, 'second value before erreur', - false), - // refresh with error after error - isA>().having( - (state) => state.refreshing, - 'error for third value refreshing', - true), - isA>() - .having((state) => state.refreshing, - 'error for fourth value not refreshing', false) - .having( - (state) => state.stateBeforeError, - 'no value before erreur', - isA>() - .having((state) => state.refreshing, 'no value', false), - ) - .having((state) => state.hasValue, 'second value before erreur', - false), - // refresh after arror - isA>().having( - (state) => state.refreshing, - 'error for fourth value refreshing', - true), - isA>() - .having((state) => state.refreshing, 'fifth value not refreshing', - false) - .having((state) => state.value, 'fifth value ', 5), - isA>() - .having( - (state) => state.refreshing, 'fifth value refreshing', true) - .having((state) => state.value, 'fifth value', 5), - isA>() - .having((state) => state.refreshing, - 'error for sixth value refreshing', false) - .having( - (state) => state.hasValue, 'error for sixth has value', true) - .having((state) => state.value, - 'error for sixth value refreshing', 5), - isA>().having((state) => state.refreshing, - 'error for sixth value refreshing', true), - isA>() - .having((state) => state.refreshing, - 'seventh value not refreshing', false) - .having((state) => state.value, 'seventh value', 7), - isA>() - .having( - (state) => state.refreshing, 'seventh value refreshing', true) - .having((state) => state.value, 'seventh value', 7), - isA>() - .having((state) => state.refreshing, - 'eighth value not refreshing', false) - .having((state) => state.value, 'eighth value', 8), - // after _myRefresh.clear() triggered - isA>(), - ])); - }); - - test('equalities and hash', () { - // Dont create object with [const] to avoid [identical] return true - const initState1 = InitState(), initState2 = InitState(); - - expect(initState1, initState2); - expect(initState1.hashCode, initState2.hashCode); - - const waitingState1 = PendingState(), - waitingState2 = PendingState(); - - expect(waitingState1, waitingState2); - expect(waitingState1.hashCode, waitingState2.hashCode); - - expect(waitingState1.mayRefreshing(), waitingState1); - expect(waitingState1.mayNotRefreshing(), waitingState2); - - const noValueState1 = NoValueState(), - noValueState2 = NoValueState(); - - expect(noValueState1, noValueState2); - expect(noValueState1.hashCode, noValueState2.hashCode); - - const valueState1 = ValueState(0), valueState2 = ValueState(0); - - expect(valueState1, valueState2); - expect(valueState1.hashCode, valueState2.hashCode); - - final errorState1 = - ErrorState(previousState: const InitState(), error: 'Error'), - errorState2 = - ErrorState(previousState: const InitState(), error: 'Error'); - - expect(errorState1, errorState2); - expect(errorState1.hashCode, errorState2.hashCode); - - final errorStateWithValue1 = - ErrorState(previousState: const ValueState(1), error: 'Error'), - errorStateWithValue2 = - ErrorState(previousState: const ValueState(1), error: 'Error'); - - expect(errorStateWithValue1, errorStateWithValue2); - expect(errorStateWithValue1.hashCode, errorStateWithValue2.hashCode); - }); - - test('waitReady', () async { - expect(counterCubit.state, isA>()); - - counterCubit.refresh(); - - final result = await counterCubit.waitReady(); - - expect(result, isTrue); - expect(counterCubit.state, isA>()); - }); - - test('waitReady', () async { - expect(counterCubit.state, isA>()); - - counterCubit.close(); - - final result = await counterCubit.waitReady(); - - expect(result, isFalse); - expect(counterCubit.state, isNot(isA>())); - }); - - test('visitor', () { - const visitor = _TestStateVisitor(); - - expect(const InitState().accept(visitor), 1); - expect(const PendingState().accept(visitor), 4); - expect(const NoValueState().accept(visitor), 2); - expect(const ValueState(0).accept(visitor), 3); - expect( - ErrorState(previousState: const InitState(), error: 'Error') - .accept(visitor), - 0); - }); -} - -class _TestStateVisitor extends StateVisitor { - const _TestStateVisitor(); - - @override - visitInitState(InitState state) => 1; - @override - visitPendingState(PendingState state) => 4; - - @override - visitValueState(ValueState state) => 3; - @override - visitNoValueState(NoValueState state) => 2; - - @override - visitErrorState(ErrorState state) => 0; -} diff --git a/packages/value_state/README.md b/packages/value_state/README.md index 8ce0edc..ef0cc5a 100644 --- a/packages/value_state/README.md +++ b/packages/value_state/README.md @@ -1,4 +1,4 @@ -A dart package that helps to implement basic states for [BLoC library](https://pub.dev/packages/bloc) to perform, load and fetch data. +A dart package that helps to implement basic states initial, success and error. [![pub package](https://img.shields.io/pub/v/value_state.svg)](https://pub.dev/packages/value_state) @@ -8,91 +8,115 @@ A dart package that helps to implement basic states for [BLoC library](https://p ## Features -* Provides all necessary states for data : init, waiting, value/no value and error states, -* Some helpers `performOnState` to emit intermediate states while an action is intended to update state : the same state is reemitted with attribute `refreshing` at `true`. +This package helps you manage the different states your data can have in your app (like loading, success, or error). It makes your code cleaner and easier to understand, especially when dealing with things like network requests, storage loading or complex operations. + +It provides a way to represent a value that can be in one of three states: + + * initial + * success + * failure + ## Usage +### Value + +Create a value: + ```dart -class CounterBehaviorSubject { - var _value = 0; - Future _getCounterValueFromRepository() async => _value++; - - Future refresh() => performOnState( - state: () => state, - emitter: _streamController.add, - action: (state, emitter) async { - final result = await _getCounterValueFromRepository(); - - if (result == 2) { - throw 'Error'; - } else if (result > 4) { - emitter(const NoValueState()); - } else { - emitter(ValueState(result)); - } - }); - - final BaseState _state = const InitState(); - BaseState get state => _state; - - final _streamController = StreamController>(); - late StreamSubscription> _streamSubscription; - - Stream> get stream => - Stream.value(state).followedBy(_streamController.stream); - - Future close() async { - await _streamSubscription.cancel(); - await _streamController.close(); - } -} - -main() async { - final counterCubit = CounterBehaviorSubject(); - - final timer = Timer.periodic(const Duration(milliseconds: 500), (_) async { - try { - await counterCubit.refresh(); - } catch (error) { - // Prevent stop execution for example - } - }); - - await for (final state in counterCubit.stream) { - if (state is ReadyState) { - print('State is refreshing: ${state.refreshing}'); - - if (state.hasError) { - print('Error'); - } - - if (state is WithValueState) { - print('Value : ${state.value}'); - } - - if (state is NoValueState) { - timer.cancel(); - print('No value'); - } - } else { - print('Waiting for value - $state'); - } - } -} +// The value is in the initial state. +final valueInitial = Value.initial(); + +// The value is in the success state with data. +final valueSuccess = Value.success(1); + +print('Data of value : ${valueSuccess.data}'); // Data of value : 1 + +// Map a Value to `failure` with actual data if any. +// There is no `Value.failure` constructor to prevent developers from forgetting to retain the data from a previous state of the Value. +final valueError = value1.toFailure(Exception('error')); + +print('Data of value : ${valueError.data}'); // Data of value : null +print('Error of value : "${valueError.error}"'); // Error of value : "Exception: error" + +// The new value from call `toFailure` on `valueSuccess` keep previous `data`. It provides a simple way to display both error and previous data (for example a refresh failure). +final valueErrorWithPreviousData = valueSuccess.toFailure(Exception('error')); + +print('Data of value : ${valueErrorWithPreviousData.data}'); // Data of value : 1 +print('Error of value : "${valueErrorWithPreviousData.error}"'); // Error of value : "Exception: error" + ``` -The whole code of this example is available in [example](example). +Get the state of the value: + +```dart +// Get the state of the value. +final state = valueInitial.state; // ValueState.initial + +// Check if the value is in the initial state. +final isInitial = valueInitial.isInitial; // true -## Models +// Check if the value is in the success state. +final isSuccess = valueSuccess.isSuccess; // false -### State diagram +// Check if the value is in the failure state. +final isFailure = valueError.isFailure; // false +``` + +Map the value to a different type: + +```dart +// Map the value to a different type. +final map = valueInitial.map( + initial: () => 'initial', + success: (data) => 'success: $data', + failure: (error) => 'failure: $error', + orElse: () => 'orElse', +); +``` + +When the value is in a specific state: + +```dart +// When the value is in a specific state. +final when = value.when( + initial: () => print('initial'), + success: (data) => print('success: $data'), + failure: (error) => print('failure: $error'), + orElse: () => print('orElse'), +); +``` + +Merge two values with different types: + +```dart +// Merge two values with different types. +final mergedValue = value1.merge(value2, mapData: (value) => value.length); +``` + +### Fetch + +Generate a stream of Value during a processing Future: + +```dart +// Generate a stream of Value during a processing Future. +final stream = Future.value(1).toValues(); +``` + +Handle states (isFetching, success, error...) while an action is processed: + +```dart +// Handle states (isFetching, success, error...) while an action is processed. +final fetch = value.fetch( + () async => 1, +); +``` -![State diagram](https://github.com/devobs/value_state/blob/main/packages/value_state/doc/state_diagram.png?raw=true) +## License -### Class diagram +MIT License -![Class diagram](https://github.com/devobs/value_state/blob/main/packages/value_state/doc/class_diagram.png?raw=true) +See the [LICENSE](https://www.google.com/url?sa=E&source=gmail&q=https://www.google.com/url?sa=E%26source=gmail%26q=LICENSE) file for details. ## Feedback diff --git a/packages/value_state/analysis_options.yaml b/packages/value_state/analysis_options.yaml index 0caccc8..17ccefe 100644 --- a/packages/value_state/analysis_options.yaml +++ b/packages/value_state/analysis_options.yaml @@ -1 +1,7 @@ -include: ../analysis_options.yaml \ No newline at end of file +include: package:lints/recommended.yaml + +linter: + rules: + - prefer_const_constructors + - prefer_const_declarations + - prefer_single_quotes diff --git a/packages/value_state/doc/class_diagram.mermaid b/packages/value_state/doc/class_diagram.mermaid deleted file mode 100644 index a257fc9..0000000 --- a/packages/value_state/doc/class_diagram.mermaid +++ /dev/null @@ -1,52 +0,0 @@ -classDiagram - class BaseState { - <> - } - class WaitingState { - <> - } - BaseState <|-- WaitingState - class InitState - WaitingState <|-- InitState - WaitingState <|-- PendingState - class ReadyState { - <> - refreshing: bool - hasError: bool - hasValue: bool - } - BaseState <|-- ReadyState - class NoValueState { - hasError = false - hasValue = false - } - class ValueState { - value: T - hasError = false - hasValue = true - } - class ErrorState { - <> - error: Object - stackTrace: StackTrace? - hasError = true - } - ReadyState <|-- NoValueState - ReadyState <|-- ValueState - ReadyState <|-- ErrorState - ErrorState "*" --> "1" BaseState : stateBeforeError - class ErrorWithValueState { - value: T - hasValue = true - } - class ErrorWithoutValueState { - hasValue = false - } - ErrorState <|-- ErrorWithValueState - ErrorState <|-- ErrorWithoutValueState - class WithValueState { - <> - value: T - } - WithValueState <|.. ValueState - WithValueState <|.. ErrorWithValueState \ No newline at end of file diff --git a/packages/value_state/doc/class_diagram.png b/packages/value_state/doc/class_diagram.png deleted file mode 100644 index 2d326f695ad87eaaec7b9452854454c22f1a026f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 136846 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 D6ls80>Y(!%)O@P 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 C);-Ap diff --git a/packages/value_state/doc/state_diagram.mermaid b/packages/value_state/doc/state_diagram.mermaid deleted file mode 100644 index 87e62a9..0000000 --- a/packages/value_state/doc/state_diagram.mermaid +++ /dev/null @@ -1,15 +0,0 @@ -stateDiagram-v2 - [*] --> WaitingState - state WaitingState { - InitState --> PendingState - } - WaitingState --> ReadyState - ReadyState --> PendingState - state ReadyState { - ValueState --> NoValueState - ValueState --> ErrorState - ErrorState --> NoValueState - ErrorState --> ValueState - NoValueState --> ValueState - NoValueState --> ErrorState - } \ No newline at end of file diff --git a/packages/value_state/doc/state_diagram.png b/packages/value_state/doc/state_diagram.png deleted file mode 100644 index da9e188a3d55c900d9263471cbff2e91f96348ce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 88453 zcmeFZXFyb0vn~uRBAGT2Br_-|8A*~6Mo|<{ksujSKr)hJgA!DdiUN|2NJhySX@X<~ z$w@LuXmXSJs-1Bf=brPPKi{u=&!5>Nz4r>WYE?a}>Z$eUs;Uwt83P#s0RiQui+^4x zAUH@!KtLo;LJWVo$p66{9)u3pl@tgvnwh2v2-pZN{dw+&tHJERp&Vw79f1XAc{a9n zu0NR1ovT!oKf%s+@_r6mwcJG-+Fu(7cpJFIZ%bX$z$ z^f~sibeYB2e(#o2hu%g{)AZsI2{Y5k7F*YuQcRz4aZAqK_V))55h934kZkfk1pEG{ zkYlr4j(>jvzkMn6K?+VD6qETc8dQ+S{+}%o_{Nfx5IPtb zwR8O5J+33@g!=!gP7?hq`fSUY=gYR{7Z%XefpiA0>?O<1Qo{`bfC*oRl{Iu_b;)$Jd8w`zP~}N>?6;2Fy3>i&hG1ZI)AynjTRaw;TGv-zx2Fb z?(L<>a*FI`Nz3zHXCM78a}i~ElI~M(J4e`BMG#4`_mm43D#^na8%5f$45wz6rMOzb zaBS&iJzcfU;+(k4Y99Tr8Hs}kg$nnenJ@z1_lF;bZHCed{UN>dg+qpFuUGX}q`ot0 z@qCeGdt;OQ?+PVjP*{6Qc z?S~>CWY&+j3@7B-$Pq#Wp5J{BrHFmIj-+R$9CV*aPwmVI_*W;QM1DRUSHBSxB3>S$ z7dE{@FK(mPRO7i=O&9nsLMO#^&NAwEaje_`ai|MT9%XxS*H_EH?W9#+kLfG51X<(W zoV%hg8^TcAqYGO@du5!~me2q7=>_L6dPV2HM^m z!clfZgR^G2k-9?7bW_^z9BmZlR(CjTQ7a+em-^VoDW;7WZU`6!|5j;-*;~O|uNP zzo=<4+A@PQh`-2uV6*-3zrMI4Pck*gCZ8yXSX}JyvnfuuS(aJ-rjE8qP1nrdb^P-; z(*Tp0hwsS*1RbYZ7g`mmJDP?ca)gpF+7uZ7pKT)%MTba`r%EcCr7%n^Zevf2B!U@4 zxvfeTYn*1(lz!JQ7zPUrV*#->Uu;`2lTrL*LePC>Dyvh+vLjuN`S9;XiapW8hrqXz zJl1vO(J*YA^ceJcI{jmtSd8I+KWMvl6>)2KNmXUrYR|EXU5QSFp}zD>U0v} zd?@43Ite0@F@^7OE`y|uR$1pBvJZgKw2({^?a~vjdJ#zVyQyoXfDK~$;l2=(?W(x% z&RU=B2ImzEAfp|eeLdbLN9u*;?5Vq9Rd~TP4s6J)E`bbb9 zPiDy(aZ^r(-^AZT2R5KQz*OExp86n_?D}`RKI{C|g_L=<1hWuRbR{1A zrm#PF*oe|H^9M%6|hYi*9JW>DWRxp8Z8Qw+R${17>0kDs176&p{aO{k%;8>b)%80N5d5ds3{D+ zRy8tf<=U?ZpX{P$5IGghA|u%-F&SHG)8PMmhl!dXK%F{^NCMw0B+?sW;lte~VU%Fn zu4L90cWCPRAD}v$*wt111X-_2Sg<0d zEi#@oq;tQ0ZPt&JRMT6b#Fu`PYBdcKL7Ts3s_G-p=qpgZeWu^BU1b<_mLi->jVi%= z^RCnzThvyc@e2)2Qp?OzXL>;+0>$4K{|PA}yAfyo?I%PeA`0Kr^9RY$fz;G$GOIV} zCGOqUwoK2h)N^R+c=&26{P)qnJ%U8MA8`;OM7Ys)J9X)!A5hEE*90Fo88;jZvi9CD zp)Vg{L29c-MG-`8;5yqGEqs4#i$Q>m$jXy47&ZncEfOJSZ%+~O+Y8+ASIsNqeZ==Rl)k$P=eeY;d4ii-|Z_4{OI=KwFGQFVd%WcT=dFsi|lj^iRe76s|C8LCQlMo z8hso@zHfLru(gVKzznqd`ygBA&i(eW8z}a~tH%?t@0y?=(Kj7}zkU4e8mOR>mHuJ2 zCo<6anuTfAzNVo~1F&uxu=(Zb5DN17zoV|Pb1Xc~P5)o>vd{4TuX*`joBrQ8(o=6M za8QPrg1&XG-`{88h{W$`ZVrO*5#z(?pt@MMjV zePfhR2Xj{EzreXNxHM(?NPrrXw!kVhO0m+&5J7z=bDmZ%X+T zE9ePomZc=Eh_6reeHq)2qFPq~L%$bzN_d3rs-q%^VK4B6d; z-)7uK20)UmD4xK#k=$|Zq3y||*~c%QRBmSfZI%~cmW=v@J0O;02>o>8SqmRcLb3B3 z6u*nF2tpJ^=V7%L41GhzNT%~A=iiLX1jGw(BkFeyqLDwOXyL5SEuf(pQRS)D}5_mB4&4kA{i(3v-5hnBq0 z^mQe~Tu;0iW06ZV!pVM=Me5958(&-Aqt3mv1s0;|%m}B$+OBnF#=|E!1J*P5o#%XS z!CD0A@AWs!2B5HNQ7imc9fH4|`g8)qN|S!p`2!M{6~#0W!*h{I znLgg0ZCm4eGUgY{Pf$4SoB35~5b&Z!eBbeW3Idb${C=Z7>VbaRzA6&r)Khs1qRYlc zdNc*o@#NSkcdmW7^NAOHw`OThyyrDJBx+U?uT4TQa0J$FO8fiTVkTEABAvuB5Z}?M zc-S_sqAlaWCIs~2dq{$bPbHk7Kr{#WB{mD+<{EPe+^p6VJ&y0Ge``IUjJWvpe3A>PLEZe_GQ` zX&&ed(^JE%Q?vqDhGTaGABrH}Y4`{c5X&*Xe= zkxMv|*Ee5lyt9g8aqbyURBhS%k(yb#)|Yu)c6S{;7Hha;F_hI~TCObG+THo^a5)vV zjH6n&bDs=4-Xru%he65u`xt(5t~kew>z(Aj;cwYx)SSZ zvcA$DrrVT{+8akxNo{j(uO#XEmw8QIw0`BgO|DHDwZ{;{{*U$X%>zBh{yA|lLw09% zp=MdVQY_FthtFClka|c=54BT$Nth=sOSxe7Q@L~R*xO6uf%?uHceRat&sWB|l=V)# zq4u`W#_cLvynEhX&xco5_*%YY_(7u;dz4DXG4dW`{=k~2w&Pb!o~1DH6QNVvyI3s> zudU{uy9w$^s+5*#AFsM%O7p`V^7?GF&(3Day=5>F6)0Qnw5-fBj&bT6+n5|;(A0N| zqiXTk-AJkgak9|f9FrQz3t#h79%x@1-5ex;u6dw_Qeor*`~Jww5}bM)Q6Ua;=yq(} zt~U?6(dKovD7*9BW(j-^ln^zOf(4$K%Nl;l;X)ayFDAAzS2V*yX)mgyR2wB!dy;2% zL})VB!T%78tK8~nYJUGcjcyN2Q^UZ32S#@|W+*mO13MWjEE&v-(s5mG>Z%M8m+cv^ zFN5&8Kf7WCl|bo=Ra=csG4qkN0c_MtqK5hKcw_?kOggqylaDXIU)gxjjgAB&0d{A~ zpUn;JuGy^jc9~i`%|l!W*(|#iweW=l>(3F!bTqHm!kp$phucvZ`^;&1k->L9O7{oY z@GAJ23l8lDO%CFt^ayuWBd7Y!uS%?bFEpc*`n&7645OuQ^{z!pt~O+~Deu&Wy{oC< zB{pMb*0o-JbHeAbk|)J_!DzFZOt(PUy4pkI_OUt_e;D3Lw-bePA4eWLMAmlclh9{+ z&3@cMLwE)`5o7!6Howqq%Ye}ZG?pY%@1CaHsnOb@!^D(2+1nd)`9mah`i>Wt3g3J` z6Hp}7AEG3qP!V4w{=oil?Z1KxiBn*7UJnsUM3p16ip~dzt=A}d6_d@ecrMH@1Q|BE zz7?0X@mTGk&SSjjS>oKGF)4sT)g15gbL!j}`ax9?-ua3CLN(1JLod9Al`lqc5Ec;cm*O-J3-*F&5_uRiLbLFE6wG zc|*_s)ebGgi<=KItGZSnG5#-2qm$8>s%aiV5WZY@ht{Pg&Z)DfeyDnA)Qu-?VRgwR z($L#b+amc+L&E6BJ7%Y(D;vCig6SGD*)6h4Q|(D=8^ygB=p<_uk5_6Afg#ckk1^PmVdcmOTq*30_AGT!*KTS_~J$ z3a%+@Ezcx-D1|@7n0k)Cn0)4OYtU^{ZQj3dV)MfJWvA;c;L~K5?}_0}h&03(makUd zZ#hSzJ-`T2E(7WZhXk(Mtkm;hs27N+&pMh>0V+T2}0w_I^PX5*IAZ+^Q; zRGh-3rXM-7>N4bY`Rv|ycqIg;duWc2 zyydml{)8P=x>0iUUW1E}vvVK?OUXHU@%i^lE%vpO5ZgYRtU__RkH!9IQ~|@{q}F?zHf)U)+D%RKi0k)SB>}H{JM~xklvcn4#7_GSeLn%F&NuQW!=gnF_b=8;G@h4cy{$0`dOIV zR?k|xrfqc`YBJtqv*r)`_E775X1y|-wzpl+lnh5l^DQ%X;!dnQde!Mem>P)jc9|Y~ z+AD=AIqfD=;K*cGL25i1M|}zfxi~qlu!b6aPm`@hb4EmsilBkV+&fJkV$?=MDSCUk zMSGxNsopX|OFWbz*P{*lLMvW(J}DKM<#?UqbeFzMd)D{+K>>$YqMiq*I$C9Yju?Kf z&d-DLUaU5IHV}AOslV=QhGVcMgpQfV^&>_xzZ`5tOX+aH<;vZyz1Q0k(bXD}pkhvu z#k_KxQ$x?}_3f)A#$Cv2I-+>eS4QvYg3CB!T)Ha8gWBC1Vx@|)DY~^Jk+G?3?9Y1A zchmz@`XcT6Hb8-f$r_=Wj}f8qMYS`7uA}3SISI;-)}?qnI{KB>dp%RoeuQ#d<<7_@ zUE5OKvm+0W$Gm+mup;Nq%SJRS^pYG8dT&FtGHT;UG+?exYy~Xn?QZio$1$zN*ui`S zij0wub)0giJ}nsd_$^SS-8^=f6~$>>BIX#?CJLj3F%V+{m5C5EbXqkTAEk z8EpCz(a7irdkt+pKKd|v*J>PTwi<0IkEnQt#KuzVsgTK zKnY9UrEMMuO%lvv?Qfr*%eglG3KAZH!!w2nQX5PUYAy@NNNo&#T?}B1Hy6d&kE)Vz z#k*eJ=(WsnJH?ybNFClHl-TcuO?V-0c{=Wx;umA|g#h}q&1V|ABuh!%i-f-5aMxwt zk5{^l1HCG{%ET-S2iBupDV#2|Tc;b>$Ulbw#yh&x$WVUj`C^p&ezB+ql4o5c*y^`hRx%o3EMH0Ta;$ zhKTiYmMqmTLq;g_iexZP706-3ZrDF5@J(nh&ql^LSwTcGwJB%GFD^iDO_uFcYiHP^ zv)B*%Gu366l^Qzy-(eVzBl5ib@PRGxMq4s-m3T$el;5YA5G)h=tyuHOa1)i<$* zvTl0CbJy;YxYja@`wckvoh_6OC_Tz4JuqFvAvGs*!Y%B!wEL1lRquA>(=bhcpN$W1 zZsPI!)FYo4xNN75Ivn;a)g#vDOV_pSq(@B3xndWR1iiOeE59mX;ym+fSpxmR0-nhk zxH$GzNsb&-wq>gAF^7pbM2mh+&*0=(<-;Rew$*DX5D+G}%hq$uB#2LwKcV-YhTe3; zg#aIG)>UhXq=*>TDrRpjL$A`Y$p{RyRq^!2>CL%(L!N%rUe(xW*%r%D=f1fo&U%a3 z0`B>zO7oW3KK^2+V*%8A`o)-bKOfj0-ma@1bm*DPa{7_xQv20G?=5Tf`O%kVI+zqD z)CUow;IQYlN)4l52TaNquK|p6o*e66SFI_y)%p~2u2Ib`9e6|k39>7Vw>p9gU%(a; z6R$#34>9Hqy63)KEV#~-@nvgV0CnA)Zb(B*g{pyHifyCxjt`AX%+92oAti^>eDpT$ z5TDCrTxK5QZBJIc{*sZUhFT{H-{qF_+yU-(EtWrElLblpga9}wn2R}4Jq z&p0l#8+n=;+(kc&U^i-rbC7b6T?c=Cc`#R7a>S-~k=ae3dC|+wzIHewbkXa9%fPC8 zste8938G+Y7W=CBaV(v)R)Pro_b^n6IS!2%&AcAK#L0vX{Ey@A5YCtg0!85fXEH~{ z=rS83g^dV>{O>?kJ-4vfy1B&b#Ze5&1LE|XyA~r=jOdJFn;W}K2dZ%%y62=T4jkQo zweE5Ez+-7YAJ{|JfGbfWwY%0m+A0WG^h8=v*g4F{+Pw&I-A9=9Ak5KN>%1NwZmTr4 zyB&8*zCY&KrHwH31`8GM=flTJ7)Dveb`Lv%ijl4#$j8JECnvF}YCebv6#==>Jg~nr z_WiRWcQnSOti8O?C+9UR%d`KqEFIa_Mp{3T-!SW#*wMAvx>bE&0C!=T>tWq6;6Ix2 zS?EMCp?brcaS1mP!qJra_4OGz|zuue#BTj{U_SA??eu}Lie>ng5aHcGh=@mokNc8tP)-vjnd1F>Alv)y?ndcy;t8l zj$9Vl;Ycgry*@r;Q#6e+esprzuy|2Jil}6JX{(ajThJu?X{fx;{CAJf2lM?|cg*LO z?g9dGKD)gz5ZKjWD?7%Pyr2@lNf_|S_^kduZ3%e9&xzmca{dQRryOGv>ElaWO`Y5Z~rAe???tlCfDQo2g=wh1eXTJg&>2AxS$NW~u|EMOH)M2MB-&-H0MpyXmwWl<- zs}`SuX|*&A$*gtnq&x4zk?Zsvi$@6N9e8HU3#~7?{de-eT|LS8Ag4cgG^ag-1k93K zoIhLKt@wKT{Y%pycH3&wLK55ClRPg_ zmc{p$Dh{BIXOud7E_s;c29?L5b}!Yfj|ka)XBd7R#F;xIAe+KGG_EBH^o(%l)U!NJ zVHjcQsYK>_>o|wzCgTuMZjTcMuNk{8>4Pc%FHfT;>_*m?d!79awZc;KC@HwqcG((5 zyTt1cSQS4@v%a{#F{%En!(4pO_3lRWFA%F4o_2!Bs`z+q_eXJOhELTsmd6o5hfyC+ zlGEpWi5BadWm)UPL9kcqwt#ROnO(P;hwt(z37i%O~#0CXNathzm3o&sL^n;cB$pFes+ZQ z4Zne|Yf zBn@}hYU)y|Aie+thhuxaR*tfG&iUez3mIjb!L7E7)se~y`R8djR9)A4YQt-zE;FK^ zOFj$}u+46=N#C4##V56`P~G&xUf8$9!k1ilqm3 zGT({a$lKD=6z?%5A@^J){_~9PMS3~4c%*n@L~2q}N~cT_!XvdOjMe)_F(}kotD@=0 z`c#quM(>ogM<3z9#MsXLcW<5{1v9w&&EMSVvo5)2@y6OnvjMqKO_xBb7?5Ki)J0PP z5Fnh$0nAvmvr|(hs?_l8`u8Ch*ZR+g_PBwU!78=QXY|T0J z&sIaQ1Zi6*cJ}#hT}k&Jvc{D(!%q~QE$9TDMY{7a7HKL&FMcngnjywRZ?v_vRpngt34XInQ+~kn?rg zEC+RC8U*Abw`p15%*`$CZedQG>tk~8lySovlzR`6d2OytTRVsr@<*pgO!cKEp7A9 zlGV#8z)D%IL!bvJc1TWWws7)G7Mg0y&(`ye z;h9-_ikglmkp_~Su@23o*tgEtYe>CCV-$NW@{as zrT_!+zp85ap#lPHhiyt0+w4HXen|=5IotE)XFn87U+Oo;YL)kjr)5=gQ!*zt2LV-x z_d;CLGyH6R@=@8BVT6s5Sx=5Wlv=GBa46qs+Ca}{WBK0tUJ}QVD9?Jx{DXgy=Fef! z*OoTZac5$S=L~$zd5=jD{{B!U)29zf4 zlIWOylqMiZvi&v3R#2}G*)Z_t??wC6g$UZ^JawOa#o&85br}U6)8h3g+mB_&b8~d_ zUWyqfAgkpID{+uJFZswnbffi5AV;MGWA-}9**+#=u5*b$Q&UBB>sT{&MG6Gndh@LD zaw*;PF2W3d9Ax{Y)3c<{6)M~&wsh9J`V5aTqGv&Rt%@KE>d&#N|0HkN&@eO#x|UYO zQ~o~W-ezkH&?2dA`I0R!Zl|<{RMhTm{?YIGIYtvvq>hhU9J>foNW3*^pJJ_8A>I5n z7Jj;F?K{c3^CyY6^^G~aaW=mPeTLJ0SttTVT<029;a&tboXP-Yqr5bED!!ufvMQ>} zblU}zZ=8FQpKvScP1*F%__*O(W@C77>kfp_+4R^MTuw{7^vrX>4g_|v?imJXo%%R= zJxbTD^{#`y$1~E9g6L{tN4x@;0*h|#Nb{X09Dz@jdE`fV0ohdoZRVCga<1zfE@<1_ zsjOF&rj5;wCMV3+6dJ`7_@3Z`N`qI)DWL_;L**;`Mh&~z^C1VHr<~dQDf3wr}@-0c@6Tx8V%a`oqt0KV| z#Zs2TlZ7RGpU-=X{2dlyQ#4r#S=>^!Gdx(}Kk~ddqly=22f!Z9mg|=(!F0S=qC>nF z=e(2La12REPlNWqv6I)rJq%>^(wf9uCD)-EGI8_^X*1iO(t`j322 zVAEB9pB_klOabsxG8%{ycRex-A>$$7Ib z7A!YwNzIM`dz)$xPK3ricMdsA;1bAZgBe{s^U)#(>Dm!%_mz#@E@|G-&j8E+DVgu0!>0+5WA?QDr`B%R5 zL8VThkIYN0O{v#rux~sjI(W^<31i+Jslk(n6TLw+n>S~YiE$$O03s@mv3_TKlj3NX zw#o242Rc1F-Sw|wo%0zRn&sq#NeN8JK z0Llv+lL`Kp`6Z;70E>?m7r5I(keHAEw8N~*e98BH;?vOkFG-U?6 z(@PB)^2>a=RvQitA9G7&%M=C6QgMeYN<+`ZgtslO1GpG^;PU)*iDWUOEDosmm?e-T zpkz<-maSte?~{8=xy=?b1R}E-9@Sr-0+@6YWZ5$)uvPr<7cd^Wjb^h@bWt-c!D($(k_$M|H{`j@bjV%=w^9ddRw-PN>NC~xPv@c3 zg>)P{?IiMGCKJWWoBQqFcmj@AQm{3zs}m>rcb1!LNG0A=3n(I=2W|m|0y4&O*7lND z!=wN(3~-Sg{~ptr(mpY$(-*=8wGBhz zG*92XNm}OC7l(v^@wX2H=-jS6J<^^<T$M)XgG1D$ z4vmYePz|RRNG>2<+Q9^-66IG-tNTeyJRF&&Wkp$jk)(U!2>m~TAn+~1VSCjKv;`b% z+AYQYAnLa%Hioc(Dt~#qK=Vkja41DmoLGwVt3#~Yf}T4w4*AjF?Q7>EXS*50Ao4x^ zR#}|*(Pa5*=ZtX!pVlQ76&ts-vdvAXjzh)xIq5UY^w6J}7hz&;*qO6cyPI1nlqYA? zzc^fVl$zhAY=g<<@YV)1dbHPB{ZQQMx z0-XZu7HZ%=&1$^9V2vIPlWVn+wvV;HW7L1~&6_t|5FXWd%ov|m?!C7}2Aii$HZ8aF zLOTESC6WSMrm)=7K7W9UbZg$@O5i{~B{>uxWeaZ23bQ=TSZO|j(B$sWn9<}*9mDpnL6U4rBM5yfIG78TnhX2;#MsJyDB;F{_Hw7?51NWn6JBPnvYW4A6i{L5 z<2(5D-4F!UJ|l;YcP~xdC0U=HpU+c=MBuiac*I_pbtBW21Y^nbIGaLT=^$jSQ+FCYo8oB@`4Q`Nhe;kG^ zT6h`-FSDAAs3@+|biCz729f7zNF@3KBVQLcK`_q_xYX>dR)rxdToR66W;1bV{>xp3 ziUt8zIw~nEbGP7H77v^A;ujNSg@JD+rshmNKg~`wo2=~+D?nrwDoKA3e=>51-8hq+ zo|%G!wBZw?M6*8z9=!MfDXu+#;G5)l{wT$k(Uiei-<=6jQybJGg~8T2LHVs#!H<+f zmAG6>2htl`yn_b+_}mi+QQaLSEUre^KaFHLO0RW47wogj);cp_kibDSE2^(3a10^# z+;9>vegr_+0ndF`k$M|8a7EPhSigJ}`FbCV+F zIym-s*W(f*laPWo+(*RePBYUPdTK#2-6|E=9>fD#LuJ;)jk~k18NUh1Bh7AH%3X5H zA9Pot7qhBqu3}Cv>oX3_MSEd)P8OQ3c63pj)DCtVcjtkPA%P-6?R$feGN1uAVodh?6Z($(jyd;HlA{Cn_;K)-CvXc>F0ALd_;PrCbT8`1Q)A!_?h!M7 zy^qW~{9_ZMAAgIuRU3wFva=cwInbyhf__hukPG2zvSdJfYO^>R8s{R*)+mFO9tMDa zDXS*9kbT1?e?W{BYCgS=3uZ*?0)Y|@=$8TPXOCtUI4cxH$6x4p0bu{joTJ1c7h?dF2m40SFkzhHdu4LpqM6@)=LoSR=c27voY}?|vnE@_?Zo<-wRb%&j+$spU{LH`g zDUx*S7SA~?1yiIbP63d}nGj2gSqw}Q zefOO=&2StJh<$m&*BiG5sh&#M*o=_w^{47P{&$Xn(|1e_RdfZHu9chqNom!o$N-0g#}`>yyYRoCp{<%IOQ2I(rT(t#fM(IsPte{H0d% zr4WHxrR_4*fakPCDh(p9|I}!|gDok*cLO&&Ej@eHvn#FBjJK>l)j1mjxMij)8Ry7@ z5UMqVP{r;#4Fcycz@hm#kPv{2$0;@K?5O85;6LnhAkP-7-XDh*K)DG)9&69m}oFq#7GkK%=Fha~wCHNtJ=cmKeb7?!hRd6D30};Guz2u~e zBY-Y!FRS^7$!@ZF&ghZ{Y)t5W=-|kEHwg5Z(ghrW7(0aG!dYYEVShSma)A=OwLAFr+H{GI zffOWhFBLELVm!@~-|ks3D^yrq>da?mH}Wc7z3^)j$tNBM^VD?ppvhD&xXCWZaq?vb z#QVsv3Maw8D?g9MFtHmgcT_#x=S=>t>h1a&!O{AEIXdybv7Y}2LsI|0FM8rJ1iw^) zp|*`(`3j}QtE&hIZ+6#RrnMNnnE-L#85$99{JB5|&IN1zjWj9{M!8}b48LWd-3p!YbwzRGtH(4Pm-WYK%vH3Uf%5PWUU*B;7RThnyZ zP!b(Vf38XBe(Y?F(|?NOr5TW>5vRr9Ijr-EOt4P_UM=>%poX_&;$W`^KNP;d3-+vo z@BH+Dj~HokhGo2t2b9PRKe~kBQfCl(Ye=6{#{4c@+YqYcQ%xK3a%qEvLHkBw(PwDp z2QAzDF(xgdvc>J^Z;g$OoV>F8btv{m*a;f%(U=A6lSG9{M(+c+@eqaecs<_#9#%DmA6BgB zN=P_juvPnfslK9j_5HXh9j?@feb;R}zlTSoXWh@|g@i;9-qFOvT-{Pk2`B#`O0)gi zmqC?gi;2UEInQ=4e1AY4KzM-I4=VS>8nW&Qh0+TIonI;oSAqO2#JkN1fMss;9kbV68Y8_OIxRv;$bR7}-(QAj!F?~`O83)m)$}P>C|8UOAjJ-xHfU-p*aUI^Hyo^YFd%L5hZ;WBDfvB}kVw>) z(&fYF2Fx{Kc-fjY1>A5wg=W?BnD8`2qsnQOw>ZTmrjy6wRhj{p(&yDD2rQlwBkzRUH1^+=AHb9HzO8&d6cG~WM;1bQcOox`b01t#2W^PqXot?O zL}reB);*{#^x`P#4X{RO59==BJHH3#EKrXi@#E}9LKZeOMnXTVB35x5q(x_c@z!~O zv%D_^Lk&Q_9@P=aK`@;gg%n~O&_nRCdON4=h^t=PLPHQlxDt;I@OlcR1HCjXU)LRy@(ar6I@P$1VBxmYep$ zsG;8Io1^f3NT%=Mf3zL>As^lx9NA1wfoDprN=J8v?}6~eEAi5W$u)NbiI#AMyNWbT z_;OzPze`1&!$G_;X>1w{p-R4~M9IeyaWIhq2E5FEl`x3{X)L4uW_+-c@A6HpYzr0` z{qncUi}+3V?J~$;;O@>h+#N_qI|IaZ$OvV~`WLR#%d5hWCHT_r;`6yva9o*M`vM_k z!S?YPy9Jhm&0v5&Y=E9MX&vSG@@1^dXul5wC)c!V z$LA}NxT+z_GvLP>9XBMIN|1smQPDROpj!U{hKr6{UmQJfh>GF>F$IZ9P4L}|dy*%w z{tv1%M~w0E*+@z15rO1-mYb!?ZM?iJK)=&oq&d$(Tp^9zVL)>6l-fm(uM-#>Epj3iU3wbC6&INPS*{L;ypIbE8gzjJsq4$qrEHvt( ztB=GE-ujmdxGK%oRZ1LV=6N}HT;H*`+`$a&tsF4du!2~8Aut;q^sM*%V?j9BuktmqgLOc0 zVDo@K&UQ3D#M{HS(9(MvM=wQ~D&=^l^5i>Y>w7kh;cq0qk70Y!2ua}YnUl|ezZ7zt zEpo#Bf!{?F8xm=q=%hAXFua)j-|x?`^-a>Hm+1TFTVxc4Bhm)wz` z^Kc@?#y^xgP^$>Aoq@s48CJL_J?<$Ub4;bfi1Y7dSmNkr>d#-*KFET0>-6LkQ*iCx zv%p_RDbI;(_f+^fXHFRIL@DTm=|Hfg9K5&vt?de48aHs(=tjMp%4v{r{U=AM4?tXk ztLu<^a0A0PEs(OIZ7F{DniE6U!Z$wC|8;(W056l2v(-Zsm~v83W%DU*N7clK(90j1 z1F`??k;kxCc{iD#EN%el#8d5`;XKZ^juOfvjo#(5K|V@Z|k;fidHZXYGuZ_$$`WW<4PA z-2|&MW1FFb*Siqh?O;WSO*LGUy1MI0&}FFupqF3pO>FmeyN80 zeaOWfP|qx3 zgnA56?Ycchr)~ZOe{@ClFs#kE3*s+7s@Z@yEYXk*=}vngl<}996z#YqL=?<;zsdyx z2NLsO=u~Ll9q*J{FG7zTtE+K1OB9_oPx&*wsEC*YMYq9SzxDSi%mt3ui(?5vU4daq+~cX5uv zM#DSHGFa}74BL4#E}}-&M}QcTUp$>KWDD@AWwv*B9ecqOURR^EVj_?Eb;JejV*5*W zc}QWM1TolBuV+e?p{u^{Nj1#(z7CxSPbjHp<6@|?&-!T5jpu3y0>#erny|9XNikl~m z6CcR$M-!@mCK6r3?j)IBh&F^ve2il6_LoYroToyl4Pgtb*x;HJ$~Dw*3kF4K`qvS) zh}IQ%A&THtZYAZ@orBZ>CE&GrGs$ zhDx<>4+9Eft#{X;_wY9D;NL3l%R>5GlPDv{yF654m4c(9Rn&J znYAb?h(Mh5ZC0p=IA`?KnWAiSr3=m2qIL-(kJ(!ux^(zp+4fSs%!xLM4x$CwKlxS-HB)W^2eH8#gkvJOub3NQ!1TH2vA>x;C-`tb9 zC2C}{y{e-Rro%}lSJMohai7&4wa5s$)Z$}b(?r7UM3lZ6a4Ez=Urj>I(a^RFokVLr zW(`p6fue-elJU>GO(^%^`V9fzoYq^zTyi~UDS$20bU)iY3NG-tU<@UC;TadgSXjk+ zZWpIGZT-3#l>yPi5Z+zsrrFcwa&AyOlQWja1G z1XI@gy3yo#Jzb#!incf@81ruCtIO!MRqGunsUTIBX_d|JBq^n6*nwbWg;s59f) z3sY~f{u-JamwMmpsU>D1>_z_$fKKPK`CrPu34R=ug%C>?4_E0tGMsF>bMKyXxgZ!yP6)a&^9EG~|$c5WH)1dT`No`wVr%o&F)&h3mQk#N_D^#j0V_28IPY5OQ8Uh+gMaqho++C1Y9VxM+UqV zZat{iyu?Z^vvRFzvrW*qg#0-RDU0*cGq;44jd4A2Gs}ocFL!_gH_ir-o_DX+cIGyD zd;S>I$?{f)eg=HBOJ~#!_f+BXV%tBowgTX`l#`fT58!M00T1IX6VrPvW{h?BNH5wA5MP4>B9KCWfkClau5vx>99;=GMz}) zXS2fsL&Ocrn3Tx~g`Kt-tinmF^zV<_K;N7^!NV)s;C)Tb(22nA%zByfbyXX7=T9S; z*oG8T9`mX-VmI2wxCmPbw4Nbi`c(`iH9Zp4LZQSYG`ck!DkTTYo_VJFW)RYDXLTzu z(!gEJ9&*t)9$;ltY;0qY_e8`Fg(m6jJ$x;wZ zI{e`yZmsgp84a>b(;+(7P1`-y$!;7$sK#yH(KdaV`J$F}h`a!@ijHR!lGT#}AV#Cz=aq|5>9k7H$8*hJP^YIl42G<#X`LY&L)%aUTjPR>n9 zlD{v)nsfkTmg=3=+(`;lY?#Z6>l!gvGA9! z%#M94_#~8JH#_@KYOkil?;X;5X^gt^N2&n*EjGF^s0Fmkd1lfWHClOzi@9w)8Aso( z2`NtYRHYCb)gDc0yq>_vxQ@F530DXOH@e%UI-8s-ZakCiD3u09g#z2(5Aozn3CIF{ z&Q}$PeZ@nsXI=%y(`srPzp&|ZLHJ4od|1BGbgSh&iT3QKnKy9Gt)5>+j~XQW;sTIF zg-9O1V^8FM6mcO=dY+z_h_~6x_PsC>H!0z$@+TV2sBF(`eYF9wba{fF>v_xp>U`;= zp*c`2lfC&_gt!y^aXVP=1D7%KABsaA?oZBG-G?Cm}>-kR&;PGEUZ+N{aP z0@ngG5KiLCGO6UY8?#13XI?yLpw|RC6-OOe;e04uInNn;ul|*Od>(MJ0)awyRn`nm zS4zA{gPK8@HoQM;ZyC~LY2xLF&6UaY0Y_0d55 zBoV?5AkxJDN8NizMcFLxqRcQz5CI2CDmh7#qy$NWpa>!v1Vo}_BudV3YWViI_qWget$WX(=em~bT_V%dZ+BPKQ&mq@XS|wbhM@N` zbPL5nH%KxBY|nsu@E%!n3NewjR(89PqVWAz>c}}(Efu1NtKVu**(6KdR{E8bA9p^= z)D0u_3(8K#fsQsl_35jCLolBk8LrF;%$Ki!Amv?Uw^}~>e|N*M{er!}SXvu_dcHD% zz}F)mJy`0-wdW@SQPj!N^(H)j?`prt4E}&@DDBF;UVJpP0LK;P8a!HA_J?lq-z+yw zTGGxS8Q6;KJ=i)@$f@{x5B^b3 zwsL=@{e=?af$=eeGKi<0t5)Y8u44|a|Hmr(fBJuX{PX#Hy|&U~bZM zE4RdSuiWZ%YZvEyI(pbp!VF5G%Tp4Y{Voxh_pV@Yed zD4kukSSHMXzjP;aENOvkA7=~Y0(ec$e(nUDFNEi=$9r#nobr*`nM}i#hX;U)q_Gtcdy()ky2cZ>Oz{K7!?h{H5stpi&|Y<_*~p%X3|OgosBgA|d3G+)wM^m?o^)1+1l<8x?JO&bm?`=v5T>pjZ7Zz7WI#iRj-ltL zIsKu9efdxmU>53j$+g)@m%*=(1~*-yHC4cIyN&q|M*R7-3$xGP3_uh1*ODL4y4Qy0 z%t242#sI1xCTm(?Oc^<%4}CY=@lAr}9O{xyFA1J_@6LWpF7!+&fkG<9`~7}}KmvsY z?bC>pb_@M#>qx~jp{BQ=(fVi6HrVTj1d-4#q@t;KzZ~sMTU*;~EsZ3`CW^aQPbbcd zbyaz_@3ec=77uxuYs5-<1wdP*0W)Dol`JE)77ycto+FR)^*OsHN)53e_B#6Hu--x= zKe(^_FsstZ$)DeOTshEHyYZ9Hb$cwcDU6w>B%4wfIXBk~MeR*D(|vso*Q_!sc8E2H zcJ%ElW$TqOH46RoN;QrjIg=$ZOSMPVoUKjaKc45Go%F+u5HlgxlE%hE$cYUSOlF+Z zmFpbRdm{<1UAvYl8oY|ZL;glTmqX4Gt=_vWfEF08P+oT>+GbB7DTl^`tUFM5Nl!fK zl-x_@Sb5dyeFHWBJT#lA`j)Oj{&eX>l5J-6qisehV=eyH&T~G4o=zXewC`FC>u4Ta zJ3+RvNOkn1_Hb}+^X67t8QU*l12X}JjMs6kn9>X5OiozLd*rn}HaPW^(x|=i`{2Fh zN_)HbgZL{OjLuyOoGm#8()xND9V9&`74|g^h_gfwG-B>$&eDZOiZbmD_V#*34#quE z#ZZO7g$v5F33db*i2|R7s?xDxQLPr;v(CCmWekVnTUdbNaA`IRkyUyAU^%9X#93d8 zeBfOR#nzI-qlkJcX(v~{F1xo^xiFg2XXZ@qceoAanYoF}Yvo6Zj*hr=h;_9{J(lz# zo~`ATD@TsHbgW&DzoUkn`{^U)nLC~6Cu-eV-<|e#IiYMn2bB|8ZHLfJKiDV`jSPKv z{i0os09wm`L6&gCj-4i_Vbr0RFVS)EkTVO_6X{>$*;_mI!DxtC?&9lua>lc4p|AZg zxCsj7yV5k7N-7ie^mBd-d#uW(K5qA3ul_Xg@+i$k?cOso zo-41kN8R;5?y#pE^^M!*jP57esv+&feT|pgtfbcRjmtO9gn^U)zWBPpY#L2c!u`h6 zW-AiQkMu--rdMq}&?5d|@#*D@kQ;hS-wQ@%+ncY}`0oJdb4^c6n#$BXCU#$DtL{rl$ z5P zzuTarj>lS?enPZs)#CO_M7n8JM$45s{)uR;&$qYE8tkTdnMtSw!3oFT!(LJayKpB? zAY31TqPw!b)ow54wZsiwOnkGr)`8U9q-UHr+uLOvxlt{>wW3y6++L{x^1w!V;I~gx zI6Lo|lp3UZG+|!x^EqlC3dYl2X4q@qRxGgIXy!7ZKq}WgW~;=jmq$`Fee+;ofnEHm z@b6uW!m3`(`R6X8wbX_mPEa|Ic=-O|hLY*fRo|l@ERArp1PVxhEr~}z7K(u(dA2onVO5f`I5%#V#NrMqQ3^pkSkc&nG95sQ{4a!#+u=b-Xo&PJ5k?#${|Z7u8KApf$1 ztkl%&C)JIOFTCZ;hDG|LEb^dFpiYa^q4!jzN6{B_hVC6B#f~3?wwdN0gMJ0IKB>_3 zQ$wS*IWov`SnIqza*nUub1+Z(iiUemvCl!b+Ng4sn_6kym4RA+8nX{`uRRw_t)c0M z(}>rhZpdzGzEJh@$CdUyu#h1MKD)ZQgDd8B*XeLRX*1;7-I=kzJ zqxMJdGNr?y+hT>Cq*StYhpnqP$eMLq(W59PEqT~z7Hg^G+i*~Y3;gM*V58r;v0J#f zxQJX)=VuOcL8_==8lpy@xb){!%kU=htV-`rH&c2x2n^V zoNn7w1dMKzXEy3SD(k%Y^ID$SkHwD?OZ{DjT$$**u~t66BfI$8l^BN$7N|${%@g^9 z`9D6@SG+3+;h*o&>GX<5l4No2sq$aI5@!<~y7z7^W8_!v_4cFLCr|`ZdOcIGM07NlH%~6Uxg1dYseK@H|pQ6_YU=R44o7MBw539a-c+0{w2( z89yep*6n1M_WMFUk8CsIs{(bm_vh>a*zQ>q9 zm(|n*uN4I0dDG2-V18*n@RyurgQPvVf=HC8+&UzMRiM|c&5Yu7bBm6v1E?VuwTl(6 zp?Q&TUSp=C)StSXq7lz?(GBOs3SUZY^vEw?j;u($;yA86WRsl8M#)kAJI}3pv5bRG z-1(m2fz+m$L7$-w{?1aB$0}&7mZ!w-SC*B2k|gy!dJ9}JCM@cbY%DDY5fkE?7Kh?3 zklw&Ip`|ysrnU5tjX{x(Ocb%0ZnT!fbhGcE0ux$b^-}7s0Af@m)@8YcW`o*s8K<2O^+=gU3fQM!5+gVv=f`B!^>3@LTg zpA(v_!>p8Q7^DBZ%RJ=3C*0f|=<5xIW@CQ5MNKSuyR$Yw`tZoaC*|ox7G7m51e}sE zSgP4-%wGP@&f*#ST>5YgKZiD-*R)jFVA4b^2g|e-niXa zkr|$RF;i&zQ(akXeA{-|D%v#P$&duYM2=HbO)R&y02)lo{%_Er?kTFZ`9InU8paM! z_39_PnBAEVF!DCHN^ZWewPJ3)0@XLo{xoFaVCqsLL%#P~4_9v7D{FRVJ0+;9jz?-5$uyAD2D7&IY$*>2q1-r#j~;64TMsFjPWe+b2Yr>{*_%q0C8sao$g zZYg8mYnc-(`}x+jqDfmR@gfZG*2|TK)X~S^CCI2vaZ1zO$t-91;py6&{GzR9yWTwC zYilua@YM$F^Nw@dWgCgX?1Psi9}W**zN#lXH-02d%RrZIeB2MS8aMaV%E?!Y+Xk{J00I8=ydzRRqOVIrRq$c^=zq=)VyJa#cx~6M}1hq zjeh0wrUAOLoFP%n)W}$c&|>AR$Y>dZl-i#sT($;e7-RwdZv0pk-Iy2${ugC~(2uj^9 zs_Pknt@$$!rYh?4kA*vTzy78}F@>E4oozQud|nuytr**OUIbf; z;fQuBGa^kZjSTgQJuC`UveIb5W>Mr4+6Ng-L@lbv_z|?iRi2r)aH=X5G+rl0x=~<| z1L1_8U1BiI1gR~v0@&9xV}zukC{m)JTNDDyOnk&ya_hmE8bOnnB+6^D$hRuV{S?YC zu|aj_hedKJ#v$M5*~6Lf5XPY29KWFs?!3lB7ivH5(Ts$x)ZO=cTvhvL(2dL>rCo;z zDb61D7Ih;%P3fiE;cUbvPW%%zoC63DMBo}xJP(JDD8^ihy}DFZd&&2k zCKcW%W(#J7FtK1&E3Tu?1hBGUAL??(lUql!t*eVNyTd&JufjawII}0!{13!Tb zqozo#$PhA)o9MAz9SgEd1#=Yo=`9$9&eT8^{o{|~A*0YBC~>rvR5(ZFk`Q;lJe(sP zk?bu3;yb=CW^b+#^>ZLD;~$=Ta_uk;fi8TwmV8?7uStJK-(&w3p4JzsKy7-#;X$)p z-C5Af_Sa>VQsERRzOgPNLwEAtO_gPWKsh@sSzNW?!-aRt-}dj)H1X@3T%|U59%I_R zfdAKKVKJYG9<8jnuhzTEe3P}w<&rUW>v|lTG&UUwQ`7v(`^z8a2;g*shJxE~wTW*7t=e{2YnM3sk@Rul)=llJso(x;o~y21`LCb`uSIq)y6rD*o91 zR=lTGu4~-ZK8IAHC^SXR#atVvU5Lgm>YGRs50w!A{Ys6*L*7%h;aIv3=Ha`{;1P1* zJi^Yyb>@E^CO=+fjLxwqA$k-nH2#iqMocbI$~$T886i@I(xCLV1gHGg$YR66QTpv} zX_WeD3^I=8T0%M;wg?XOy+GJLjv&#f#&GO=i}ce(UsRb+qO}Me#&Lq0s}tPF%apQu zckU@Qq6y#c60z&@_mR_&B`KQl;m9x)HPM1>g?WV8!oKZ&pHZ!wp0?hTP$Ops4Qdz; zdMTy{CsB{pcOR2}=f@LW>S8g!a|y&tZ?jRCi?GlZK;d2dwaG<&_E$o+OzAqG-#r>qw3HYU93mT9qF(2c6E=YOO?~E zBB{9scNlok0;4-^2eTj=nY8~N!Vsw!R40%Va==48?D?+&4A_}$5Yv1)JLkyJ0>0vW z1JA>-3M~(Pz6PUO?>e0n6arOatB4#(X7x0XL>hv}eL9P0H!dy?$$pxcmWa|{NFH++ zzt7kYPP3|!Y}GDq`}?PICv*~T&lWIcVQX*^K*sG$!^UcZOZyu-BxHc2TuT@d<<{TjRU zZKwL^lxa+JLE+#*k60Av+YBa9L(Zy#uhgVE8(NtvdC$fuO1A{Xn~HOe0}|4*oJ2J zCBYw`^?qo=+J(jyWZ*JUJnVvnETzHosks`jC#SM1Ru^0$n`Dcjod z@rJ}RA(9cWrLpS9Hzh(*ybg0Ng7?@gE_OFD$)kvyE;U^OMLP^(uD=J2!p8O5iJs7p zzIgRgaT*j2#8$d*FC7H%fbiA9C9zlfU#4caEmdFv6p*2??z z3|IK~_bJWw6YxBTin;fI&EhlGmH~O1_>3%0n(VUHk>O7C0q^O>#wAmYc2Pwa5i_kj zHvpF@5>9sb9-f43X#MwRXHy7q&OdZT8P!OJyOPBXZw0dB_oNb`!^Fd^Kwa?Ky!^Na zUlL0m%K=tj!u#Kwn1j|j(s);vT9Bu1mspncsb=}_WfNsV%ycAk?kyB&e6^^<-nLq< zA(bp3QRl_d znPyXWQ~qRV3~Q*oxaUX6FrnN^Mz1H-m8jl`L0Stry6*bIa;W=Pg8_%7CZH#TE%^e# z=dUfv1w(nT*8+JoD*N#;?ECR_!Ftr$aO^-YgzZf|z&lDbbDt{-gl60=50nJ#dbot# z78J}XXw3ItLc5cWxxSVn6x~Xl;7}S`p*P0?(7yd zz1}y4s*dh9Hlu?N)pyB_)`JwZ^fA!8Z&xjag<>?LY*0c+`Ob-fq z?_OHo@LKXP6B)R(Cd6>J_Mj5j6ox`KKPPsVFZ;la73GT4RO#aoZEt??BasL0j|oZe zc8gf3Dv+`fVnsZ$3==8?<2VpUw@dXe%VLbf@w!)x9yChDBnQhbv{TmbF!<%FVtn6@ zqaPoua>>vS+Qi$R2!%6jI!YzkDub@+7x#MJ;9aa*zFS@XcXTa0+*%eXz5i#?`7`mJ z$L;f1TwkAeZVD`d<|h&bzeUDs4kDghj8dHtW-*A1tA-F;?F@8fiOBRdKdAQ#{H+o`?kzyeHmN-!u`1-5M4( z3m`kFc{Y(>^;UJJeb{Ksu+YNKp=yzPL=Ottq-6uhYkPR$5~Yn+xaz=5%?CIshBy6n zE#dNos0Qksnl|a@sGh|Sa4L|T?P#@>-8s#tZ0d2wq02>TuC?1F8vBiU&g;^NE;-|pt@`xug7_o-bMlP_+!m-wSrG0J zz+aa92p1Hnmkl{T%f9cqwZtZDU1DTbpK?r)I;9ZKf#~ z2wtV2;mK2sKF^0BW0U#lNB&?lU%BMz3u*k-0t%a>zVVe-;1+Whgap-RCdFOA1uWHL zSMTIhR_MS@OvdQ8s>!ksSS%hiXp=fVIpUJDTm}HG$o!TNjcWGkDSY9vjvZ!PZVb}L=wgMD+U8(j`nZ=^qh~x zfAeU2%(iH$^4#mlQ!I$R&Ecl1M-u_(7$SjGRi7v%!Z|H}K5+ly`1r}tvz!4Jni&{0DKg9+l(ATvD)3Wt`2 z#@`VqW_+wu2lv@6baWzIe|I-5wRcMpoq3MwVoQ-Jjl4=hY>*O|T>qw#9ifY&cW!#Y z@CE{>DiApJGS_z^fcwshr?S#C+(-0nGgeg@O}T!&pHM97xvnNr{(a6)Z0lVaN!x2w z?rVE)LHEH$jG@m?WFlWHHwJeXikbPw3G)?gX(E%zH5Kt%M@Bv&+kLXXVAIl* zpbM?I{lKj7LGI%y>{|q*bMw3EL9gZd>ML;Beb#K5-BzsVvhnX;dq<_F-`O%$v5-Um z2o1u?S6}){E;yWWOODX|s7xlYV7WXG9cA@^twLO}*8YAZsA;i$nP<4taVFC6cIX4E z%tX~!?^kcphQ2$13yxl_0-H)KI`Iy}uubj3i56(fb8aW%DJmr7MG!R@Nuw}SCB;c_ zrg&*>+4#V^moyOu2ct+E1FusETuW&}cXk;0|C+oX-1Hw)WU#j&^j(~V0ri;TIpiTd zoZb$p@HWjJ=_KNYn!E*Bvd;Jc<9xAW=d%fEr zeC4`BDdqU{eG{@zc0Fh4=Q22<7g8^ zG?kjvJCkowwNy!%Zqs2}oz21aD@$9>YN20SL!HW0bX2mUSI@><(`pRj=3KZqTIRwv zQK^f@4qe~p?K3)EN44nOSHIC*p}BX%=}n)|O?C@olgKnDFSq2Gr1t+69Nq{Sl(`$9<*euu_ZWKKQE%8deGc*8)85NcB zZNZqoRjKuHV6*U6e2vAN1u(@vr(Ac*nn`eig(4HxqbBS&{D88+KEE)>g#BS7oqh{0 ztMGuR(s2V~qu-$AM$)WXKQyK`g^p1UI6JCyxcoeizpm_8fe)TsiLvVazDUR`1<~#a!Bu zRVG{z@C6^dPMPIn;Q!(g_g|2m%YY?sKk>SqFhOWa?swDhe&e-+52j{UfO1<-?B5teLl@D&fQ>VdPR195s zkp)r0>nL6d^P#62$~|jJp@7XR1|t& zoWK9&bSmMpi$vkDN2+@RC~NQy?)J^Ne7Uby2h0+KvXRP?=DUC{oTp=zJRUEgb@X!=i_~U>5LrA2&;^m)&?@rsWKO|!QcH)nm1RL3u@drXB-+XfGbLJd9EKK)_2r6W|h+5 zIEA9qeX`;CZoJ^!d13>a(*#UmB%n@-C3yZmMniCndizAQYeMz-Ki^}lq1x{7{rF|) zx_Ry1(#(04NndE-$W{TjY$ZmktJUF<(-n8K?s<(VD@MT#+1<@y<+qy2)0f;kWqrd= zvtC?_+blrs4=-Kv|tZmwFaZf8zz5io?0BIFz7y6{(T% zu>8FHkXhcJg@1V$&1R!^IaD=yg^<&{GZQe>44jf1fmvXCR@4f33aWKHx15GW^U}(v zD|sdl0U0Fry6kQf48!%0Y`}PTUU;{aTo0FL5eDU_xH8s4MQ-8%pXlwz!S1RpPqUh5 zj7c2x`Bk#Uip*&5?PS5*3~rX;N56Y)nU~c} zOZ#fPej`tBg@&`fd<|BuePrTPP=Z>oZ+7|#cTCt=e!r$BR8X;_ zBK`}@U`Fpdx=+8o+R|Ya%y;D>zp2NRDIBCE;I-L6Pt+T5m59i4DKyby^;-5e_$juo z2YgwXwu#FgtLlcU;dl^(KDp{}v5wPSf(&B~b{n1VJ4J`P@v5UAp^n)r z9D8ak&AAy)6c;D<&wE(K#(-A=-4NhYf8zNkM!D`+?ge55F4V+6pp1zSera%@Qdt%G zaeMqbV3v#)M(&OZm#=T4eM0EH!bN!_G#B~%)IgpH;0Pj_01Ggny*s5g7#!ni48O4H@F6zs`6A6Uz|7T zi~eW&JvG`3dKqV~`AqfoH*a4F2o;%QS)1|Y(RnLr)c4k`@QuwOUx}8jf4XW3gOnam z#Oq3S!-k=g{Ob4TL@!It?(18nsb2GTO*q)4pZv1>7egUQV)jIyTcd^5QVD1<31yt` z34BPEqG30QmS3M(6FAoQWvMC-GXdaaxXk1iL4)EZIOJ-FD=(iV>b@9A{uW{L4VtK) z^Ek_jU`L>E?6h#L^7LYhetPOe%^I9BoP zd08||*lX1O@62S&uk;e0gBlRING|$mZCrNNKmX(RLHCWm0i%~evNaohrtivUlAbkt z%$P1y9nHuvMm!-ubTr&(4(4tbn;@z<*zCG|+o58LY!#{lyq5J*KV@bMggf*_ zDY@GKudo}!#mEUohmp@6b^E9SDl^JfS;+I!2E!j1JLk%=xlrF3s5=+VmGbnSv{-atB^C_u||jFU_e;C;j}PL({y~5BGheNB7_An+BAU zF#W%;0}JBG{nRrkbw~yRFAP+{UC&hmCGj|8A~rJdU3XinsaR{CIFPW)h< zwYlue+#WDASU3~z<*zSP1Q&Bddq>kmJMI>zim5;$QsC9OS8R{#RO{Z01p7}tSMeBd zaJ9OgZ3tLPb26!Qw%>#9w_^n-_%er$y07bR{NA)2PM!C4jf^kr&H1s^&Rx$Q?J!e= zYvtU;S8l>O@zq>c_cd2CkO}WyqQxrlWBz-nl#RBu}AuBXrb3ZYS` zcE#+R#?*C>HJAR15EQ9e$f^j4-@xlryWK=!#aP#~;;+DSj_Xb_{@)=exEMsdo-8wg*uBVHk{F+^P^`MMbdc$BcCMMd;sky zHbbLlrY_{ZL<4a-*BPlJy1>Ks*7&W^*(siZr2Km(U_UG&#*+8b+}C6Zn&sM;8cP4E zJJJVh9z{Fl2!^kO>)d+AY0%<|v)ZB$;Mt>t3(gX(lOc5hDu!bpU0Ri8QX!5ZzZ&wH z15jFgk*xV=!21F0iXO)B8;)(H2PBX@r6tm_^EV)lIqQj(OSuNW$i~DN{s*J(*Wcv-4xYnDEbiO*epGycYSouslRnF4F(RC> zyuyrFOB`hhB_lRScbK`C3wsm+xiATt_52T@03bBje)uT#JaJ6HqiOF;%5~K?Vxtps z$Z3gMg|qbNLgkYDhfTIV+A9FyCq2yK|f(`1ti1s@2^m_|g?N zi}0ev`u7l!*RcDMZ<uvGbstN zLClRApA-n}XNryD}UT)`&djo`60J6Go&e%^X*FBmNUQUC=hE)9y10ya&!PgBL z3Wa7jvah&zdiS3L_#8mgLdTCLSg~fC5v3=nRF#q%$4?{pnr zZsyqtRWu$NA{9eFlM5i*uol@-{qh8H%ogd4*CY7anR4YcNUv}Wil65JsFwbBwgHk> zaLkKk%yBseo@5WkQ)q!@y7sN@FzinI-Ec)nn_Mq*5z~WrQTcb;_O3Kj}P1E1K`!@2TTbSrf z*s&bd*(MJ%=b(CQj=o=+b_*fp-9x!jr37J239@n!9!z1EVZdCgki$M)mcZ$l+%SS8 zH?*CSf(SGPaZ@If4C<{$0;>*ZYj~(vNp!mu6uu?5PTR08abxJa1f!XA)*^o13Pl|! zM?Fd)>1k7rcN`A5>{(5u-NoUpK`7Z$&9?3}3zJmTl5Nu4lFWIj<( z7c>4^>;!K}GyhRZEq`0Jnk!Es&(3~PLEppm>*6&gF9?xk@WunLha*%8BcjFb`(qOk4BYpYXDtTOK9#Apy zAmnbRUHuHpL5%;_<2F1SwJ0wT!?v+yHGbzn3oyJWLHe=5w8OC~tvmxI!PsVu6wjZ` z`JT zWG&WbyHb6JIPH0I;eHe%o0GPEyYyCW6}R(pbBu32*e`PmT^uO2e?1n}%XQV~T!1|O zJAh_ylaN;^C}CW6CR<=3D%ZJwAPfvbkzNfHqELQ@J4`X{t*qHxz)kRN{zm_#nO}!r!A|&yCXJkK9J~c)w~!ijb^jxvcWWdeF;nFr;PD(k?R0w zLms)x)x_y1*;EU$pQD&3t?H3}hcdErY=#^mM3%kguVPac*+AqX($xkz;CZc^LvI0= z?mcU0o-CV%!v$L+# zvrV=yGle7$TF4i6-ywlfxGbt)<$ghmp$O$hB^F)H-KBuyr_ ze2-L7*xUKUFO$}ucgf#ED=^lYG0i!H0reMchX%&1wk%4Sfa4PX@V|t`G9JS)m291^ zOyhT>y-tiRWzgCvHIMIfRu0~VH+`nG?S|lB@*~e=4p{EE^+0+~cqUx3Z2|61%Kb?{ zd=>=WL0G;o zo!x~XtQgAIX$_F|o>1gFCV;S5NV{`S=Ib@@3iv#j`p>L9Rl0F7+t56WGo0% z?xwn5zP?)Bq_AXk?eZ!~u!J6s^2(sq1!O;*5(z~)P%!l}hy69RPrntuGef=hAs0hC zB3D`^dpO^it9?kEQZdUv=GA-Zyo6D0y+ln!b*k1WmN%#_%Q!aNZ{?6) z-hK@BvR*hXAQ4{LZQ9?AfDQJ7h}-`b@J3T8(MZIxP#`i%8g6lDx$b0&6_;gPp61dj zuTUj6xE5!%_y`2}ELC(-3gN*Z-y@ac(ky1T~8bq_td&!Wa* zba`Z>c=DYbX~d-YN!BB`A6Yz~=f1bQ5!VXX+wVE(l@o|^BZe3BoDyH;3_El%cqX&K z_P%np9fW6!%D|VPL|8+&^!Jyaqe4!xpOR&AJ3cN85kV(_!J(b@a?hNKirbC5+?|>r zx$4x10qP&A0$HLO(cw`!?g3`huESqyMx+C2inKz(D z<;H}JfX#JA^b->Wq$0shvHPqghDJ=vi;I*Y2;&tGJ#;jqtnVA&L-TC*vz&Ku($2=6 z79b=JGQe!SoMkmxz=|kht+Q04%HGjly-G`DAcwO{sjF3MM6$?zljQJ13IDcKs?46SOg#)y2T5*p%xR8p$(2ZLy({;PQZS(4Y$PXTt91 zoC?RYB_05U>? z*%s4rq-T;%j4bd%1hk2c6Avs9FMcRa+B{cV7SSwX;S!OQT$NpH9b8x;4p+e8+@TL* z;0{r9{-`+51@@|BwLa?~=;D$h-VRL&Y*$Qg_@{@Ljhct4hv3g0or;_e>an4Wo^32x z&TvWrW>}D?b=|pi@H)Z?Y4WnbbXBA$iUHjRLK$nj6Y{xl)WRdQDDSdC%j;|fY;w(9 z$Eh-+A2#xVb8yC6L;gvw8TqpIp=z;xqxcjyWO$##?jM-x6H(zVKW%37^;~2~BVWEzLIdrK^cFSy z`#x4;ZVKGM|GHkbCZ&gS!Q%J81zWr(_+J2Ej?!Yo+*`3%6;RgMaxwcAVS|Tp2~@O6 zhFx~RT#j1Ve?dbWGde_l%My1aTFF!Xfs(Z1vA)4fJWoD%oY6xB*hI;nav~wqj4Vdh z3-eq*b?lQagHH;s@X$*#uq#3Tg|V~}@p6vN#?$2@9YV|#N_WJ0uUyk3P_&7ji8}_C z1^_GpF;<-8gBibGtb?KV_8*JRhuS8P`50TL_iNjr@hAFL+5NbEIoS!)wN4~jfZWiM6q0RC zlE*~58XVE?KNuB(6j&06&wU7N{LbJJ_$E zfp>TDGw|*dnw(#J1zjRqvETXcm#9niA>qDzAaBqhOxaIzp8PAD5wqxu&J?ZRcC7Uq z1vU%nB6-Fqpp{O%k*BkV^PG1r)V&>jq-uBf#jN3d_yG)@aIim~WYYK3Iqv+Rn9;OJ zo!Et_@=xM^E5fBU3T!Ehbg90w@S9Iwr6xpzH0ix<`2BjXg?cb|q3yI3&&6YR_f9b* zBF3RHT>aasW61+In@g&No(Ih?NJGN}QX69!-WfI0SXQhSzJ015D7OW zzkdo*-Q*kAm{&IEl~5O*0-xMk6dCG3h!S$Ph`05HV{N-R-4bwvbf@!8z_T%&knA~% zG$aUaBGka?>r2wVuM<7TyM&*(yk#vWde)zBFch6@o?PoPC*15z%w`c)tTcK6pKN`G zi*F9ZOZ#vY{_Q1LjK~n|3prh^HzB$8;sI;;kmO}iQPFPF2lEuamXr21PP19~xEPm& zLl`A$B_Wgsy5wH6pZt>7i^I z5PH^9bF2tYot@kt6I8j zlAC(mSSUi0piTo{-gW2Z^sad%fsy zJ#gtL-+Jr7m%mk}gxuHo|zPJ(l~CQ4DFSY#OixqNXq{v_$%}KR}t2% zjs@t_x~vcyc^pQ=5TSqvD~~*p#7j**8RlK}>hqRY)v9)msKa0xr{)l?gXYE0>%h~( zpXQ~7sPzoH-abW+kH9~^6s2b_T!1p9-(R11GQ?o~<+3>V z*&^NQQ2Tx%Uw?ab&m0sO8}Fo+m%@Z&>HlR3{^6HeFY5dnJ(0^3>#9KcQVP{Sa7XiE z_Qq2+*@ZUpWO^?bou9&N7H-Z8nuD+umI43IfJ;oNCZZfMm?$V8P_uKBr?95tzE`8F z(Vk1Xl>A@y>(cP*Ku;-z(zwo%N$}tQHeoRtng}%Oxt~`p`(ruV9ZH1x56zgnpy&x| zqQz6AKjCEke(LKCcih& zPXL*pn2_gEgBRz!2E4kFFAyUBAe?C-pz*p0{NK**FSyVHDK5-Z=s*@CwITIS%6cn#FcFgguc~5Q zej&RU(*?N(15;}vSM{6y)aNOip2o@k&u)SP;frX_JXt0UWG;%g3+E{mk2{|9JI$|2 zna3=su0_Ux&vh;#l8^mcEMM%MVe-G6?lD-I`jylYE8NPkfB8ZG4`00a57)2gF<#4e z)h``vOE_&zvTh%r=WvFAsGmj3nBwZdsPphz&JMOe@xV4Cnoi4scl$W)EQ<6v@^wnR zIN&Oiz}e5yc2;bP+1oY0Oo(nNF?`;9e4FAkT38>rxGye;zXGpIJ&He%QyrkOz^@Z= zN=OmBm)R7`u*z7ore@`)|8rsv@i2y;G@l{ryUnG3fl6 zz@)_mz(TEzfBC(L{wd+|xQ3b!!?xOm~dN>}QVG?j?*C_nREcp1oYU&xZ6mjm6sqbr;~_2uzT~VpH!28wD5BroAdIW=LkNT7CRKeI9pUky1}Er*H~9C6tk@3^^DR*|_(#-!b84 z81;~Di9;>fRi1NJ+Y(**k^rom32OoY1<)qaU*?0bRS6R@tVaX}7sASwAZ$$9O=>-I z^IK;u3n($azfw8H6?Ps`hUEMz(iWALYwbKmc$LRs+QiJL5|;*MSG59~%5c&p82B53 z6pj&GDCpCp5eqKg6sqKO(qi4Q62wMgGsNM`X4dNXt2QTDmXe;nfdJar&YR*d3KLAOSS#XLbn|tY1vdNEuiBsj&Uv>OVmGoxw~{ z)y9gierrN(&gZ>9%0YtM0bj#9X`qi=MrSbmG!%(=q|o5P2O{lZ)Ylh{GO3}a`)yF3Qvjcc-R@E%21iffX#Nr zQwUc;2{hJ+>s<$sq)z+@Tj;Pzf)~g_p$t`#AlThKIcKAmX+*5*)y)S6s6$Y>$@tNX zIc)RA&1%G3E}s;{e_Nd)j$!a(`HDkZ{F+9>Al-LpA3ndge;xYnDKJ|cYlu4!n@j0l zP+k0MAfs>7wP)nL>Q(Dj6Yi0v#ERP)zxL$0<2ZkpVGlBJxAlU>IoK=&*OKD3VRf&u z|8v71k-+k(akOrw4aB-d&LHdx)*FpYR~lJAi$l7RK)G(+1N?1nI>NIq3>W{naKi7* z@#b@>S7kHrnB7$4=$=<9<*SRV;o2pjO~8*C*)QP|ihhzF4=!ruejo!QNW>#vTP^~4 z$1^E}q~G}65VG2&NUmd@t`p;?D_2z15T*WQkob0c*PZb*#stI`BrKvWR1HLHJfr#~EH8{TL6Kb;Wx9CH7Qv$qbba@*R6 zWw}rg5kWu^5hRtCZcroy38hn1TDm(GR9Xe3QBY7)xl+XI>~r=x@A=+q z|K30LwXVf>&1XJyjydM2`yRw^7rhxyb4pR&&3r`=Y<=ooOq7~w3>9H}1DN81TlWGX zTg$2)o1>2yBdjYI$AUd>=sYAu`G67#@$gB~?6Po)%e;XzwbxoZCn6{(pZ}A1(o&dI zjMV<&+CVK&;k873r+_Xo6jyan{}wDx|2rHf0;twvMRABK?MyabG^`Y%fOn-JV6Dt~ zU3#v(u{w~09akLR3xdxPB#)`cCMko(XC}*;g&!w{Kcm)hT6lep1=glSz^I88?&Bw; zH!4>}rLa^9UwS+_(c5FUR5-5<^qbV)Epo-8I_*b+Y}nB5hnF+g5`wkEKqkM_S(U)X ztga<;{t?Ud#qvd;$ME}fkrfUf_;do3#!@1A{Qbe${Co0pSW|~~nrJ|$wQgyH8gL>3 zNvw&jT?Ny$y5U{-c_fy3<~k*;7@G0T-3pi$1wre|`{zV~8q5RD6`ilGb+pB zqf~aeZ7ZWaKHoayd2mTpZ^$l7j9fQ9%4nNh{*A*kyZ&t8a$?{vU!G{C`l@xz$;*9f=N0SAk)$6{j|m+^YF`%Ro3kh}N&pUr5oKlw z-If?UULH;=&1MUhxYwdEc@uM;9Ru4imKXmW7yu7^7}Bo73Fbia-MvDsgEmz3^nl@#^cKK#3iHS3{lT74C=q>g^MRHX4rB>4)8G^TyR#Gr{WpBiMeS?1B%l~j3fi%Y5V!8^q z6$ci-?oDbPNgYC8k2@!;^Vbcfv@ZVS^ zQi9AgCiV=#Ft^wzdBBKo(Q<}yElh`6hmV~Y`@;qMPt|g;Ho^+(9Egi|(XLirvLd+R z<^>FIL_djmccgtmFSLL5~&ENG?HV3;~~+PhxxiCt-$X+*8#ed_B4PxQ$yV#{WT$jsY~ev+<8 zkuxlufT0i<-`mhEm7Lwym8EwF^6SS|B@%O8&B)n8=vP)tF{ zd``3i618w7XI`XW`JhcO05!;AaN~P6$jQy$M`ZOq~38Y#1*k;hFPP}$|3yqZxHEVBB=1l}Ogf_N3qmFl+y7#~b4iHIt zjxj_-S})FEJqh-}kAH}X@;SK=eZHu7u>$)B@u9-TdMeK))+RN{1tjnfw?t$y0*AT8 zZ3goxc-j%msGQhQevp?7cggt>chjgi6Xukc_EGsnAOs!!{R76A%O;kY|#5MxDZ zno}6K+D6^A3Ezn*^CJb~bcH0D ziFofL3jXDijGX8BitSo;y8Dt9VL7vl6?>Tkt%DqJ$ezh0>caBAo0Oi%1}UOFU*I_f z^}ZDe^k@PIRxn~*cb;=*J08GnckjL0?)K}~C0e<4I3vAmcmO?&;bWS}v8}|Ou7?`f zIa${H5@JY5`=n;oN5VuIR#txIH(=8(OcNDv_3!IJ1-xl?KQxu51>i`D$`o{Vz9~Uw zt6nP4InwgxU-&rwW>PZ;#k~Hxe5D4YR54Ij%QQE4KCi>j({a5l6p&!z2u@Kv>P?yu zEHVuNYu+uQf*aI2I*Noj(I7i(=(bYiC@#j_0N3k}>H)?kIE1?`rwKA&Is(XL0LWx- ztwA^EF8#ajf<5Tpa1EFkv6w-@bKU3g;eLJLr9y+~^8jsB(f?hQubYeus zKID^Q`}E7pQ{5>~7hI`zaEz(rzu%h1yo-u7kgd`M34S*wm4cjCT+hIZXRxneTvejb zf#aNszKU&V*uGmyfuW&O#e|`vZp7(0>Zzg&>~U)BU++tb;C|6?UM_g%e_7>oNPVh$ zONFQF#MdNRKJyf;;`#KixH46M-(_zRkOF>Mb@S-)8o9SiL;2ffN5G&z5vGxDS=JpI z6WRgmHX)1a>THKaTjjn@q$h6#}hj{CcetkfetT(>?010ayHcg|h zi?GHwTJd3SR3ZuI(C5J{4-=unz?I>94AuaSe*sN=-fr>8A{FeRb5kNM4MrS5`gm6= z7E21kHP4hJrY&~m^mYd`*SNq{?K2iHT(fv5#YUC@mV2@H~`?nL zd!MzO=hTN$nUzs_7I5(NV_P4M)+Ou@98davMQlK;5%5#% zY&a6;(vFa;*U~`I|B!VH8H4j8XQr`JrDVnK`gGmKY{x`)i_lwy!b$nD>tbYh(7|^3 zacHby&qiL3S-ISzXd2O?~ro2S*Z9KB&! z3KnwRy0C$-4u!VUY~k-@`9wB|bzQ~%3m$CsO1wi61bK1>V5SlIazR+I<2njVpBkURWu%JQ zli3d&g~sl^TX*RE-tUfAW28epSe#>$46~pQzQmn&YJ~`w&gm6`^m-OYq#Oy018!bb zFJH8Y4y9OX{kAW$1+Xoe?vME<)fLT~wbF~;q1I`Lu$E{57UR}$W%Z>YRL9gQsafJF z+t51PWcnKK_U^Q>aLCr8bhvv#2D#IWESp|^LUVXJqoUCUundHdniX{72ZaWi*>-nX z16V(qv1Fw(Z8(DtqkPXe&}O+TT4gy@WlAaZ5q%o4w^3h9lG%Sqtfm{13v9#kd| zo!&FM7R%@xFYwJckZ>3}FtinPDKoq|$Lku%zr=_z7@%Ce5VA*U7SsLq@auwZbkC{< z7IL?NZmYpGUyIItw;`_N!;y{?>H5l8`2Bn{LHg=E5$NyUR&-eO2NEOSP)Rl)KhCzh zt$px7H)Y#qVEYeTbe`VO)&M=^IX-%$O$@J!ee*~A14}M3T(RmO2#W|oN3rrSbkOY5 zw7^AwhXNtrc?~h0-WpvQu=dU^2`(}VvNYjmNj=^EM_9%Y|$n>d`qOB~4tPmW4MlC1| zxsSC(As!)Z58K$l=4c?eYhMZK!)IJCi=%uZB2k7B>jfBMjH`E1K2iLZ@2B=faefuh zj3R6hKYhp+u)krP4zyL)-i-s=*a@m(0I2Eq&$hXEUM zmlb8;96TECRYJ!)PQ_ir{#B$o}Cdm8&`*wElq1f%O4&I-9hEa zi=xCM1OvM6{Qr35G%bI6XPyHwJq2QVyp^9XMic1HIh@-w%)-BrnG*gT z7`OAsjZ2}!eZGQS1CxC)pg@uq7I?HUxpgYGv`K~{orfKJ!kc`g>bQ;C8?(}Wo+ay) zhUKwL3OvW5^?FL^r!P4r0O3An$JhZvVOzV$diw%Tyf5@UCY8t{^Aa`1)g(Sj*YY zF5A7%w;uUJ-PBYP7q;5>dY>)xUFK61+8$(=RqQToc_-J*T%41zwop3ulO+_`F@D_A zb{rn={T@`CH?W1x5o$xz*ZQ⁣pIK$o{f>VXAgJL8cYy`eu*Zd(*tWuf`)1L1iM$ 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

+`yL~`ghG!@)0`w_XhDw;cxs>mZ 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 F{{hXpke~nn diff --git a/packages/flutter_value_state/example/.gitignore b/packages/value_state/example/.gitignore similarity index 100% rename from packages/flutter_value_state/example/.gitignore rename to packages/value_state/example/.gitignore diff --git a/packages/flutter_value_state/example/.metadata b/packages/value_state/example/.metadata similarity index 100% rename from packages/flutter_value_state/example/.metadata rename to packages/value_state/example/.metadata diff --git a/packages/flutter_value_state/example/README.md b/packages/value_state/example/README.md similarity index 64% rename from packages/flutter_value_state/example/README.md rename to packages/value_state/example/README.md index 5891114..ca32683 100644 --- a/packages/flutter_value_state/example/README.md +++ b/packages/value_state/example/README.md @@ -1,6 +1,6 @@ # flutter_state_value_examples -A basic example of flutter value state usage. +A basic example of value state usage with flutter and blocs. ## Getting Started diff --git a/packages/value_state/example/analysis_options.yaml b/packages/value_state/example/analysis_options.yaml new file mode 100644 index 0000000..57644e4 --- /dev/null +++ b/packages/value_state/example/analysis_options.yaml @@ -0,0 +1,15 @@ +include: package:flutter_lints/flutter.yaml + +analyzer: + plugins: + - custom_lint + +custom_lint: + rules: + - missing_provider_scope: false + +linter: + rules: + - prefer_const_constructors + - prefer_const_declarations + - prefer_single_quotes diff --git a/packages/value_state/example/lib/logic/counter_cubit.dart b/packages/value_state/example/lib/logic/counter_cubit.dart new file mode 100644 index 0000000..4bc31cb --- /dev/null +++ b/packages/value_state/example/lib/logic/counter_cubit.dart @@ -0,0 +1,13 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:value_state/value_state.dart'; + +import 'repository.dart'; + +class CounterCubit extends Cubit> { + CounterCubit() : super(const Value.initial()); + + final _myRepository = MyRepository(); + + Future increment() => + state.fetchFrom(_myRepository.getValue).forEach(emit); +} diff --git a/packages/value_state/example/lib/logic/counter_notifier.dart b/packages/value_state/example/lib/logic/counter_notifier.dart new file mode 100644 index 0000000..cd4cbd8 --- /dev/null +++ b/packages/value_state/example/lib/logic/counter_notifier.dart @@ -0,0 +1,31 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:value_state/value_state.dart'; + +import 'repository.dart'; + +part 'counter_notifier.g.dart'; + +@riverpod +MyRepository myRepository(Ref ref) => MyRepository(); + +@riverpod +Future counter(Ref ref) => ref.watch(myRepositoryProvider).getValue(); + +/// Simple extension to map [AsyncValue] to [Value] that you can include in +/// your project. +extension AsyncValueX on AsyncValue { + Value mapToValue() => map( + data: (data) => Value.success(data.value, isFetching: isLoading), + error: (error) => switch (error) { + AsyncError(:final value?) => Value.success(value) + .toFailure(error, stackTrace: stackTrace, isFetching: isLoading), + _ => Value.initial().toFailure( + error, + stackTrace: stackTrace, + isFetching: isLoading, + ), + }, + loading: (loading) => Value.initial(isFetching: isLoading), + ); +} diff --git a/packages/value_state/example/lib/logic/counter_notifier.g.dart b/packages/value_state/example/lib/logic/counter_notifier.g.dart new file mode 100644 index 0000000..ed7ebf1 --- /dev/null +++ b/packages/value_state/example/lib/logic/counter_notifier.g.dart @@ -0,0 +1,42 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'counter_notifier.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$myRepositoryHash() => r'f60f56f53e1042b2719118d50e02a2134f095fd5'; + +/// See also [myRepository]. +@ProviderFor(myRepository) +final myRepositoryProvider = AutoDisposeProvider.internal( + myRepository, + name: r'myRepositoryProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$myRepositoryHash, + dependencies: null, + allTransitiveDependencies: null, +); + +@Deprecated('Will be removed in 3.0. Use Ref instead') +// ignore: unused_element +typedef MyRepositoryRef = AutoDisposeProviderRef; +String _$counterHash() => r'f915d09ac88f54b0ee6665ded639a2958c7461b4'; + +/// See also [counter]. +@ProviderFor(counter) +final counterProvider = AutoDisposeFutureProvider.internal( + counter, + name: r'counterProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$counterHash, + dependencies: null, + allTransitiveDependencies: null, +); + +@Deprecated('Will be removed in 3.0. Use Ref instead') +// ignore: unused_element +typedef CounterRef = AutoDisposeFutureProviderRef; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/value_state/example/lib/logic/counter_value_notifier.dart b/packages/value_state/example/lib/logic/counter_value_notifier.dart new file mode 100644 index 0000000..fd2f4cc --- /dev/null +++ b/packages/value_state/example/lib/logic/counter_value_notifier.dart @@ -0,0 +1,21 @@ +import 'package:flutter/widgets.dart'; +import 'package:value_state/value_state.dart'; + +import 'repository.dart'; + +class CounterValueNotifier extends ValueNotifier> { + CounterValueNotifier() : super(const Value.initial()); + + final _myRepository = MyRepository(); + + Future increment() => + value.fetchFrom(_myRepository.getValue).forEach(setNotifierValue); +} + +/// Add this extension on your Flutter project to make it easier to use. +extension ValueNotifierExtensions on ValueNotifier { + @protected + void setNotifierValue(Value newValue) { + value = newValue; + } +} diff --git a/packages/value_state/example/lib/logic/repository.dart b/packages/value_state/example/lib/logic/repository.dart new file mode 100644 index 0000000..d3cd9cb --- /dev/null +++ b/packages/value_state/example/lib/logic/repository.dart @@ -0,0 +1,15 @@ +class MyRepository { + var _value = 0; + + Future getValue() async { + // Emulate a network request delay + await Future.delayed(const Duration(milliseconds: 500)); + + final value = _value++; + + if (value == 2) { + throw 'Error'; + } + return value; + } +} diff --git a/packages/value_state/example/lib/main.dart b/packages/value_state/example/lib/main.dart new file mode 100644 index 0000000..ac2071d --- /dev/null +++ b/packages/value_state/example/lib/main.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; +import 'package:value_state/value_state.dart'; + +import 'widgets/action_button.dart'; +import 'widgets/app_root.dart'; +import 'widgets/counter_notifier.dart'; +import 'widgets/default_error.dart'; +import 'widgets/formatted_column.dart'; +import 'widgets/loader.dart'; + +// coverage:ignore-start +void main() { + runApp(const MyApp()); +} +// coverage:ignore-end + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + // This widget is the root of your application. + @override + Widget build(BuildContext context) => + CounterNotifier(child: const AppRoot(child: MyHomePage())); +} + +class MyHomePage extends StatelessWidget { + const MyHomePage({super.key}); + + @override + Widget build(BuildContext context) => + // This example, show how to handle different states with refetching + // problematic. In this case, when an error is raised after a value has + // been successfully fetched, we can see the error and the last value + // fetched both displayed. + ValueListenableBuilder( + valueListenable: CounterNotifier.of(context), + builder: (context, state, _) { + if (state.isInitial) return const Loader(); + + return FormattedColumn(children: [ + RefreshLoader(isLoading: state.isRefetching), + if (state case Value(:final error?)) DefaultError(error: error), + if (state case Value(:final data?)) Text('Counter value : $data'), + ActionButton( + onPressed: state.isRefetching + ? null + : CounterNotifier.of(context).increment, + ), + ]); + }, + ); +} diff --git a/packages/value_state/example/lib/main_cubit.dart b/packages/value_state/example/lib/main_cubit.dart new file mode 100644 index 0000000..d7e17f5 --- /dev/null +++ b/packages/value_state/example/lib/main_cubit.dart @@ -0,0 +1,56 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:value_state/value_state.dart'; + +import 'logic/counter_cubit.dart'; +import 'widgets/action_button.dart'; +import 'widgets/app_root.dart'; +import 'widgets/default_error.dart'; +import 'widgets/formatted_column.dart'; +import 'widgets/loader.dart'; + +// coverage:ignore-start +void main() { + runApp(const MyCubitApp()); +} +// coverage:ignore-end + +class MyCubitApp extends StatelessWidget { + const MyCubitApp({super.key}); + + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (_) => CounterCubit()..increment(), + child: const AppRoot(child: MyHomePage()), + ); + } +} + +class MyHomePage extends StatelessWidget { + const MyHomePage({super.key}); + + @override + Widget build(BuildContext context) => + // This example, show how to handle different states with refetching + // problematic. In this case, when an error is raised after a value has + // been successfully fetched, we can see the error and the last value + // fetched both displayed. + BlocBuilder>( + builder: (context, state) { + if (state.isInitial) return const Loader(); + + return FormattedColumn(children: [ + RefreshLoader(isLoading: state.isRefetching), + if (state case Value(:final error?)) DefaultError(error: error), + if (state case Value(:final data?)) Text('Counter value : $data'), + ActionButton( + onPressed: state.isRefetching + ? null + : context.read().increment, + ), + ]); + }, + ); +} diff --git a/packages/value_state/example/lib/main_riverpod.dart b/packages/value_state/example/lib/main_riverpod.dart new file mode 100644 index 0000000..318b893 --- /dev/null +++ b/packages/value_state/example/lib/main_riverpod.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:value_state/value_state.dart'; + +import 'logic/counter_notifier.dart'; +import 'widgets/action_button.dart'; +import 'widgets/app_root.dart'; +import 'widgets/default_error.dart'; +import 'widgets/formatted_column.dart'; +import 'widgets/loader.dart'; + +// coverage:ignore-start +void main() { + runApp( + const ProviderScope(child: MyRiverpodApp()), + ); +} +// coverage:ignore-end + +class MyRiverpodApp extends StatelessWidget { + const MyRiverpodApp({super.key}); + + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return const AppRoot(child: MyHomePage()); + } +} + +class MyHomePage extends StatelessWidget { + const MyHomePage({super.key}); + + @override + Widget build(BuildContext context) => + // This example, show how to handle different states with refetching + // problematic. In this case, when an error is raised after a value has + // been successfully fetched, we can see the error and the last value + // fetched both displayed. + // All fetch/refresh logic is handled by riverpod, this example show how + // to standardize the UI with the use of Value. + Consumer( + builder: (context, ref, _) { + final state = ref.watch(counterProvider).mapToValue(); + + if (state.isInitial) return const Loader(); + + return FormattedColumn(children: [ + RefreshLoader(isLoading: state.isRefetching), + if (state case Value(:final error?)) DefaultError(error: error), + if (state case Value(:final data?)) Text('Counter value : $data'), + ActionButton( + onPressed: state.isRefetching + ? null + : () => ref.invalidate(counterProvider), + ), + ]); + }, + ); +} diff --git a/packages/value_state/example/lib/widgets/action_button.dart b/packages/value_state/example/lib/widgets/action_button.dart new file mode 100644 index 0000000..d1da08c --- /dev/null +++ b/packages/value_state/example/lib/widgets/action_button.dart @@ -0,0 +1,18 @@ +import 'package:flutter/material.dart'; + +class ActionButton extends StatelessWidget { + const ActionButton({ + super.key, + required this.onPressed, + }); + + final VoidCallback? onPressed; + + @override + Widget build(BuildContext context) => Center( + child: ElevatedButton( + onPressed: onPressed, + child: const Text('Increment'), + ), + ); +} diff --git a/packages/value_state/example/lib/widgets/app_root.dart b/packages/value_state/example/lib/widgets/app_root.dart new file mode 100644 index 0000000..7f45b43 --- /dev/null +++ b/packages/value_state/example/lib/widgets/app_root.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; + +class AppRoot extends StatelessWidget { + const AppRoot({super.key, required this.child}); + + final Widget child; + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Value State Demo', + home: Scaffold( + appBar: AppBar(title: const Text('Flutter Demo Home Page')), + body: DefaultTextStyle( + style: const TextStyle(fontSize: 24), + textAlign: TextAlign.center, + child: child, + ), + ), + ); + } +} diff --git a/packages/value_state/example/lib/widgets/counter_notifier.dart b/packages/value_state/example/lib/widgets/counter_notifier.dart new file mode 100644 index 0000000..a7eef33 --- /dev/null +++ b/packages/value_state/example/lib/widgets/counter_notifier.dart @@ -0,0 +1,11 @@ +import 'package:flutter/widgets.dart'; + +import '../logic/counter_value_notifier.dart'; + +class CounterNotifier extends InheritedNotifier { + CounterNotifier({super.key, required super.child}) + : super(notifier: CounterValueNotifier()..increment()); + + static CounterValueNotifier of(BuildContext context) => + context.dependOnInheritedWidgetOfExactType()!.notifier!; +} diff --git a/packages/value_state/example/lib/widgets/default_error.dart b/packages/value_state/example/lib/widgets/default_error.dart new file mode 100644 index 0000000..e9d9536 --- /dev/null +++ b/packages/value_state/example/lib/widgets/default_error.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; + +class DefaultError extends StatelessWidget { + const DefaultError({super.key, required this.error}); + + final Object error; + + @override + Widget build(BuildContext context) { + return Center( + child: Text( + 'Expected error.', + style: TextStyle(color: Theme.of(context).colorScheme.error), + ), + ); + } +} diff --git a/packages/value_state/example/lib/widgets/formatted_column.dart b/packages/value_state/example/lib/widgets/formatted_column.dart new file mode 100644 index 0000000..eb0e15e --- /dev/null +++ b/packages/value_state/example/lib/widgets/formatted_column.dart @@ -0,0 +1,18 @@ +import 'package:flutter/material.dart'; + +class FormattedColumn extends StatelessWidget { + const FormattedColumn({super.key, required this.children}); + + final List children; + + @override + Widget build(BuildContext context) => FractionallySizedBox( + alignment: Alignment.topCenter, + heightFactor: 0.75, + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: children, + ), + ); +} diff --git a/packages/value_state/example/lib/widgets/loader.dart b/packages/value_state/example/lib/widgets/loader.dart new file mode 100644 index 0000000..34f38c8 --- /dev/null +++ b/packages/value_state/example/lib/widgets/loader.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; + +class Loader extends StatelessWidget { + const Loader({super.key}); + + @override + Widget build(BuildContext context) => + const Center(child: CircularProgressIndicator()); +} + +class RefreshLoader extends StatelessWidget { + const RefreshLoader({super.key, required this.isLoading}); + + final bool isLoading; + + @override + Widget build(BuildContext context) { + return Visibility( + visible: isLoading, + child: const Align( + heightFactor: 0, + alignment: Alignment.topCenter, + child: LinearProgressIndicator(), + ), + ); + } +} diff --git a/packages/value_state/example/main.dart b/packages/value_state/example/main.dart deleted file mode 100644 index 6e00581..0000000 --- a/packages/value_state/example/main.dart +++ /dev/null @@ -1,71 +0,0 @@ -import 'dart:async'; - -import 'package:stream_transform/stream_transform.dart'; -import 'package:value_state/value_state.dart'; - -class CounterBehaviorSubject { - var _value = 0; - Future _getCounterValueFromRepository() async => _value++; - - Future refresh() => performOnState( - state: () => state, - emitter: _streamController.add, - action: (state, emitter) async { - final result = await _getCounterValueFromRepository(); - - if (result == 2) { - throw 'Error'; - } else if (result > 4) { - emitter(const NoValueState()); - } else { - emitter(ValueState(result)); - } - }); - - final BaseState _state = const InitState(); - BaseState get state => _state; - - final _streamController = StreamController>(); - late StreamSubscription> _streamSubscription; - - Stream> get stream => - Stream.value(state).followedBy(_streamController.stream); - - Future close() async { - await _streamSubscription.cancel(); - await _streamController.close(); - } -} - -main() async { - final counterCubit = CounterBehaviorSubject(); - - final timer = Timer.periodic(const Duration(milliseconds: 500), (_) async { - try { - await counterCubit.refresh(); - } catch (error) { - // Prevent stop execution for example - } - }); - - await for (final state in counterCubit.stream) { - if (state is ReadyState) { - print('State is refreshing: ${state.refreshing}'); - - if (state.hasError) { - print('Error'); - } - - if (state is WithValueState) { - print('Value : ${state.value}'); - } - - if (state is NoValueState) { - timer.cancel(); - print('No value'); - } - } else { - print('Waiting for value - $state'); - } - } -} diff --git a/packages/value_state/example/pubspec.yaml b/packages/value_state/example/pubspec.yaml new file mode 100644 index 0000000..b1ead85 --- /dev/null +++ b/packages/value_state/example/pubspec.yaml @@ -0,0 +1,35 @@ +name: value_state_example +description: A sample with a basic example + +publish_to: 'none' + +version: 1.0.0+1 + +environment: + sdk: ">=3.3.0 <4.0.0" + flutter: ">=3.3.0" + +dependencies: + flutter: + sdk: flutter + + flutter_bloc: ^8.1.6 + flutter_riverpod: ^2.6.1 + riverpod_annotation: ^2.6.1 + + value_state: ^1.5.1 + +dev_dependencies: + flutter_test: + sdk: flutter + + flutter_lints: ^4.0.0 + riverpod_generator: ^2.6.3 + build_runner: ^2.4.13 + custom_lint: ^0.7.0 + riverpod_lint: ^2.6.3 + + meta: ^1.7.0 + +flutter: + uses-material-design: true diff --git a/packages/value_cubit/example/test/widget_test.dart b/packages/value_state/example/test/widget_test.dart similarity index 53% rename from packages/value_cubit/example/test/widget_test.dart rename to packages/value_state/example/test/widget_test.dart index b826e3c..9c8f155 100644 --- a/packages/value_cubit/example/test/widget_test.dart +++ b/packages/value_state/example/test/widget_test.dart @@ -8,26 +8,50 @@ import 'dart:async'; import 'package:flutter/material.dart'; -import 'package:flutter_basic/main.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:meta/meta.dart'; +import 'package:value_state_example/main.dart'; +import 'package:value_state_example/main_cubit.dart'; +import 'package:value_state_example/main_riverpod.dart'; void main() { + testScenario( + 'Counter increments standard test', + const MyApp(), + ); + testScenario( + 'Counter increments cubit test', + const MyCubitApp(), + ); + testScenario( + 'Counter increments riverpod test', + const ProviderScope(child: MyRiverpodApp()), + ); +} + +@isTest +void testScenario(String name, Widget widget) { + const incrementTextButton = 'Increment'; + const expectedError = 'Expected error.'; + Finder findCounter(int count) => find.text('Counter value : $count'); + testWidgets( - 'Counter increments test', + name, (WidgetTester tester) async { // Build our app and trigger a frame. runZonedGuarded( () async { - await tester.pumpWidget(const MyApp()); + await tester.pumpWidget(widget); await tester.pumpAndSettle(); // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); + expect(findCounter(0), findsOneWidget); + expect(findCounter(1), findsNothing); expect(find.byType(LinearProgressIndicator), findsNothing); // Tap the refresh icon. - await tester.tap(find.byIcon(Icons.refresh)); + await tester.tap(find.text(incrementTextButton)); await tester.pump(); expect(find.byType(LinearProgressIndicator), findsOneWidget); @@ -35,12 +59,12 @@ void main() { await tester.pumpAndSettle(); // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); - expect(find.text('Expected error.'), findsNothing); + expect(findCounter(0), findsNothing); + expect(findCounter(1), findsOneWidget); + expect(find.text(expectedError), findsNothing); // Tap the refresh icon. - await tester.tap(find.byIcon(Icons.refresh)); + await tester.tap(find.text(incrementTextButton)); await tester.pump(); expect(find.byType(LinearProgressIndicator), findsOneWidget); @@ -48,11 +72,11 @@ void main() { await tester.pumpAndSettle(); // Verify that our counter has incremented. - expect(find.text('Expected error.'), findsOneWidget); - expect(find.text('1'), findsOneWidget); + expect(find.text(expectedError), findsOneWidget); + expect(findCounter(1), findsOneWidget); // Tap the refresh icon. - await tester.tap(find.byIcon(Icons.refresh)); + await tester.tap(find.text(incrementTextButton)); await tester.pump(); expect(find.byType(LinearProgressIndicator), findsOneWidget); @@ -60,10 +84,10 @@ void main() { await tester.pumpAndSettle(); // Verify that our counter has incremented. - expect(find.text('Expected error.'), findsNothing); - expect(find.text('3'), findsOneWidget); + expect(find.text(expectedError), findsNothing); + expect(findCounter(3), findsOneWidget); - await tester.tap(find.byIcon(Icons.refresh)); + await tester.tap(find.text(incrementTextButton)); await tester.pump(); expect(find.byType(LinearProgressIndicator), findsOneWidget); @@ -71,10 +95,10 @@ void main() { await tester.pumpAndSettle(); // Verify that our counter has incremented. - expect(find.text('Expected error.'), findsNothing); - expect(find.text('4'), findsOneWidget); + expect(find.text(expectedError), findsNothing); + expect(findCounter(4), findsOneWidget); - await tester.tap(find.byIcon(Icons.refresh)); + await tester.tap(find.text(incrementTextButton)); await tester.pump(); expect(find.byType(LinearProgressIndicator), findsOneWidget); @@ -82,8 +106,8 @@ void main() { await tester.pumpAndSettle(); // Verify that our counter has incremented. - expect(find.text('Expected error.'), findsNothing); - expect(find.text('5'), findsNothing); + expect(find.text(expectedError), findsNothing); + expect(findCounter(5), findsOneWidget); }, (error, stack) { if (error != 'Error') { diff --git a/packages/flutter_value_state/example/web/favicon.png b/packages/value_state/example/web/favicon.png similarity index 100% rename from packages/flutter_value_state/example/web/favicon.png rename to packages/value_state/example/web/favicon.png diff --git a/packages/flutter_value_state/example/web/icons/Icon-192.png b/packages/value_state/example/web/icons/Icon-192.png similarity index 100% rename from packages/flutter_value_state/example/web/icons/Icon-192.png rename to packages/value_state/example/web/icons/Icon-192.png diff --git a/packages/flutter_value_state/example/web/icons/Icon-512.png b/packages/value_state/example/web/icons/Icon-512.png similarity index 100% rename from packages/flutter_value_state/example/web/icons/Icon-512.png rename to packages/value_state/example/web/icons/Icon-512.png diff --git a/packages/flutter_value_state/example/web/icons/Icon-maskable-192.png b/packages/value_state/example/web/icons/Icon-maskable-192.png similarity index 100% rename from packages/flutter_value_state/example/web/icons/Icon-maskable-192.png rename to packages/value_state/example/web/icons/Icon-maskable-192.png diff --git a/packages/flutter_value_state/example/web/icons/Icon-maskable-512.png b/packages/value_state/example/web/icons/Icon-maskable-512.png similarity index 100% rename from packages/flutter_value_state/example/web/icons/Icon-maskable-512.png rename to packages/value_state/example/web/icons/Icon-maskable-512.png diff --git a/packages/flutter_value_state/example/web/index.html b/packages/value_state/example/web/index.html similarity index 100% rename from packages/flutter_value_state/example/web/index.html rename to packages/value_state/example/web/index.html diff --git a/packages/flutter_value_state/example/web/manifest.json b/packages/value_state/example/web/manifest.json similarity index 100% rename from packages/flutter_value_state/example/web/manifest.json rename to packages/value_state/example/web/manifest.json diff --git a/packages/value_state/lib/src/extensions.dart b/packages/value_state/lib/src/extensions.dart index 3720f11..94cefee 100644 --- a/packages/value_state/lib/src/extensions.dart +++ b/packages/value_state/lib/src/extensions.dart @@ -1,76 +1,61 @@ -import 'perform.dart'; -import 'states.dart'; - -extension ObjectWithValueExtensions on BaseState { - /// Shortcut on [BaseState] to easily handle [WithValueState] state. It can be used in different case : - /// * To return a value - /// ```dart - /// print('Phone number : ${personState.withValue((person) => person.phone) ?? 'unknown'}'); - /// ``` - /// * To perform some action - /// ```dart - /// personState.withValue((person) => print('Phone number : ${person.phone}')); - /// ``` - /// - /// If [onlyValueState] is true, then [withValue] is trigerred only on [ValueState] state. - R? withValue(R Function(T value) onValue, {bool onlyValueState = false}) { - final state = this; - - if (state is WithValueState) { - if (!onlyValueState || !state.hasError) { - return onValue(state.value); - } - } - - return null; +import 'value.dart'; + +extension ValueExtensions on Value { + /// Provides a concise way to map a value depending on the [Value.state] of + /// this value. + /// * [initial] is called if this value is [Value.isInitial], + /// * [success] is called if this value is [Value.isSuccess], [Value.data] is + /// then available, + /// * [data] is called if this value has [Value.data] available (can occur + /// in both [ValueState.success] and [ValueState.failure] states), + /// * [failure] is called if this value is [Value.isFailure], and + /// [Value.error] is then available as parameter, + /// * [orElse] is called if none of the above match or not specified. This + /// parameter is required. + R map({ + R Function()? initial, + R Function(T data)? success, + R Function(T data)? data, + R Function(Object error)? failure, + required R Function() orElse, + }) { + final dataAvailable = data; // to make code more readable. + + return switch (this) { + Value(isInitial: true) when initial != null => initial(), + Value(:final dataOnSuccess?) when success != null => + success(dataOnSuccess), + Value(:final data?) when dataAvailable != null => dataAvailable(data), + Value(:final error?) when failure != null => failure(error), + _ => orElse(), + }; } - /// Shorcut to [withValue] with its parameter `onlyValueState` set to `true`. It is equivalent to handle only - /// [ValueState] state. - R? whenValue(R Function(T value) onValue) => - withValue(onValue, onlyValueState: true); - - /// Shorcut to [withValue] which return the value if avaible. [onlyValueState] is the same as [withValue]. - T? toValue({bool onlyValueState = false}) => - withValue((value) => value, onlyValueState: onlyValueState); -} - -extension OrExtensions on R? { - /// Helpers to execute/return non null result on a null object. + /// Provides a concise way to execute an action or map a value depending on + /// the [Value.state] of this value. + /// * [initial] is called if this value is [Value.isInitial], + /// * [success] is called if this value is [Value.isSuccess], [Value.data] is + /// then available as parameter, + /// * [data] is called if this value has [Value.data] available (can occur + /// in both [ValueState.success] and [ValueState.failure] states), + /// * [failure] is called if this value is [Value.isFailure], and + /// [Value.error] is then available as parameter, + /// * [orElse] is called if none of the above match or not specified. /// - /// Example : - /// ```dart - /// personState.whenValue((person) { - /// print('Phone number : ${person.phone}'); - /// }).orElse(() { - /// print('Phone number unknown'); - /// }); - /// ``` - R orElse(R Function() elseAction) => this ?? elseAction(); -} - -extension ToReadyStateExtensions on T? { - /// Shorcut to transform to a [ReadyState] with following rules : - /// * if `this`is non null, it returns a [ValueState] - /// * else it returns a [NoValueState] - ReadyState toState({bool refreshing = false}) { - final state = this; - return state == null - ? NoValueState(refreshing: refreshing) - : ValueState(state, refreshing: refreshing); - } -} - -extension FutureValueStateExtension on Future { - /// Map a [Future] to [ReadyState] : [NoValueState] or [ValueState]. - Future> toFutureState({bool refreshing = false}) async { - final result = await this; - - if (result == null) return NoValueState(refreshing: refreshing); - return ValueState(result, refreshing: refreshing); - } - - /// Generate a stream of [BaseState] during a processing [Future]. - Stream> toStates() => - InitState().perform((_) => toFutureState()); + /// If none of those parameters are specified or does not match, then this + /// method returns `null`. + R? when({ + R Function()? initial, + R Function(T data)? success, + R Function(T data)? data, + R Function(Object error)? failure, + R Function()? orElse, + }) => + map( + initial: initial, + success: success, + data: data, + failure: failure, + orElse: orElse ?? () => null, + ); } diff --git a/packages/value_state/lib/src/fetch.dart b/packages/value_state/lib/src/fetch.dart new file mode 100644 index 0000000..5481c85 --- /dev/null +++ b/packages/value_state/lib/src/fetch.dart @@ -0,0 +1,54 @@ +import 'dart:async'; + +import 'fetch_on_value.dart'; +import 'value.dart'; + +extension ValueFetch on Value { + /// Fetch a value from a [computation] function and return a stream of values. + Stream> fetchFrom(Future Function() computation) => + fetchFromStream(() => computation().asStream().map(Value.success)); + + /// Handle values (isFetching, success, error...) before and after the + /// [streamComputation] is processed : + /// * Before the [streamComputation] is processed, the value is emitted with + /// [Value.isFetching] set to true. + /// * After the [streamComputation] is processed, the last value is emitted + /// with [Value.isFetching] set to false. + /// * If an exception is raised, an error is emitted based on the + /// [keepLastOnError] setting : + /// * If [keepLastOnError] is true, the most recent value emitted is used + /// to construct the error. + /// * If [keepLastOnError] is false, the value present before the stream + /// processing begins is used instead. + Stream> fetchFromStream( + Stream> Function() streamComputation, { + bool keepLastOnError = false, + }) { + final controller = StreamController>(); + var lastValue = this; + + fetchOnValue( + value: () => lastValue, + emitter: (value) { + lastValue = value; + controller.add(value); + }, + action: (value, emit) => streamComputation().forEach(emit), + lastValueOnError: keepLastOnError, + ).onError((error, stackTrace) { + if (error != null) { + _errorsController.add(AsyncError(error, stackTrace)); + } + }).whenComplete(() { + controller.close(); + }); + + return controller.stream; + } + + /// Stream of errors emitted by [fetchFromStream] and [fetchFrom]. + static Stream get errors => _errorsController.stream; + + static final StreamController _errorsController = + StreamController.broadcast(); +} diff --git a/packages/value_state/lib/src/fetch_on_value.dart b/packages/value_state/lib/src/fetch_on_value.dart new file mode 100644 index 0000000..7ddc573 --- /dev/null +++ b/packages/value_state/lib/src/fetch_on_value.dart @@ -0,0 +1,43 @@ +import 'dart:async'; + +import 'package:value_state/value_state.dart'; + +typedef FetchOnValueEmitter = FutureOr Function( + Value value); +typedef FetchOnValueAction = FutureOr Function( + Value value, + FetchOnValueEmitter emitter, +); + +Future fetchOnValue({ + required Value Function() value, + required FetchOnValueEmitter emitter, + required FetchOnValueAction action, + required bool lastValueOnError, +}) async { + final valueBeforeFetch = value(); + + try { + final currentValue = valueBeforeFetch; + final valueFetching = currentValue.copyWithFetching(true); + + if (currentValue != valueFetching) await emitter(valueFetching); + + return await action(value(), emitter); + } catch (error, stackTrace) { + final currentValue = lastValueOnError ? value() : valueBeforeFetch; + + await emitter(currentValue.toFailure( + error, + stackTrace: stackTrace, + isFetching: false, + )); + + rethrow; + } finally { + final currentValue = value(); + final valueFetchingEnd = currentValue.copyWithFetching(false); + + if (currentValue != valueFetchingEnd) await emitter(valueFetchingEnd); + } +} diff --git a/packages/value_state/lib/src/helpers.dart b/packages/value_state/lib/src/helpers.dart deleted file mode 100644 index 1c2ffc2..0000000 --- a/packages/value_state/lib/src/helpers.dart +++ /dev/null @@ -1,131 +0,0 @@ -import 'states.dart'; - -typedef WaitingMapperType = BaseState? Function(); -typedef RefreshingyMapperType = BaseState? Function(bool refreshing); -typedef ErrorMapperType = BaseState? Function( - ErrorState errorState); - -/// Helper to map from a state to other state. Useful to map "default" states -/// from original stream. -/// The [map] argument contains a function that map the origin event from the -/// stream to the value. If `null` is returned, then a [NoValueState] is -/// emitted. Else a [ValueState] is emitted with the value returned inside. -/// [fromState] is the origin state to map. -/// If the optional parameter [currentState] is not `null` (default -/// value), then the cubit emit the current state refreshing if original -/// stream emit a refreshing state. Else, the refreshing is mapped from -/// original stream. -/// [mapInit], [mapPending], [mapNoValue] and [mapError] override the default -/// behavior of the mapper. -BaseState mapState( - T? Function(F from) map, - BaseState fromState, { - BaseState? currentState, - WaitingMapperType? mapInit, - WaitingMapperType? mapPending, - RefreshingyMapperType? mapNoValue, - ErrorMapperType? mapError, -}) { - return fromState.accept>(_MappedStateVisitor( - map, - currentState: currentState, - mapInit: mapInit, - mapPending: mapPending, - mapNoValue: mapNoValue, - mapError: mapError, - )); -} - -/// A visitor to map a state to other state. -class _MappedStateVisitor implements StateVisitor, F> { - /// Function that map the origin event from the stream to the value - /// If `null` is returned, then a [NoValueState] is emitted. Else a - /// [ValueState] is emitted with the value returned inside. - final T? Function(F from) mapValue; - - final BaseState? currentState; - - final WaitingMapperType? mapInit; - final WaitingMapperType? mapPending; - final RefreshingyMapperType? mapNoValue; - final ErrorMapperType? mapError; - - _MappedStateVisitor( - this.mapValue, { - required this.currentState, - required this.mapInit, - required this.mapPending, - required this.mapNoValue, - required this.mapError, - }); - - @override - BaseState visitInitState(InitState state) => - _applyMap((_) => mapInit?.call(), state) ?? InitState(); - - @override - BaseState visitPendingState(PendingState state) => - _applyMap((_) => mapPending?.call(), state) ?? PendingState(); - - @override - BaseState visitValueState(ValueState state) { - final currentStateRefreshing = _returnCurrentStateRefreshing(state); - if (currentStateRefreshing != null) { - return currentStateRefreshing; - } - - final mapped = mapValue(state.value); - - if (mapped == null) { - return NoValueState(refreshing: state.refreshing); - } - - return ValueState(mapped, refreshing: state.refreshing); - } - - @override - BaseState visitNoValueState(NoValueState state) => - _applyMap(mapNoValue, state) ?? - NoValueState(refreshing: state.refreshing); - - @override - BaseState visitErrorState(ErrorState errorState) { - if (mapError != null) { - final result = mapError!(errorState); - - if (result != null) return result; - } - - return _returnCurrentStateRefreshing(errorState) ?? - ErrorState( - error: errorState.error, - stackTrace: errorState.stackTrace, - refreshing: errorState.refreshing, - previousState: currentState ?? - errorState.stateBeforeError.accept( - _MappedStateVisitor(mapValue, - currentState: currentState, - mapInit: mapInit, - mapPending: mapPending, - mapNoValue: mapNoValue, - mapError: mapError), - ), - ); - } - - BaseState? _returnCurrentStateRefreshing(BaseState state) => - state is ReadyState && state.refreshing && currentState != null - ? - // Prefer using the current state refreshing before display the new - // value - currentState!.mayRefreshing() - : null; - - BaseState? _applyMap( - RefreshingyMapperType? mapper, BaseState state) { - if (mapper != null) { - return mapper(state is ReadyState && state.refreshing); - } - return _returnCurrentStateRefreshing(state); - } -} diff --git a/packages/value_state/lib/src/perform.dart b/packages/value_state/lib/src/perform.dart deleted file mode 100644 index f1bd7a4..0000000 --- a/packages/value_state/lib/src/perform.dart +++ /dev/null @@ -1,93 +0,0 @@ -import 'dart:async'; - -import 'states.dart'; - -typedef PerfomOnStateEmitter = FutureOr Function(BaseState state); -typedef PerfomOnStateAction = FutureOr Function( - BaseState state, - PerfomOnStateEmitter emitter, -); - -/// Handle states (waiting, refreshing, error...) while an [action] is -/// processed. -/// [state] must return the state updated. -/// If [errorAsState] is `true` and [action] raise an exception then an -/// [ErrorState] is emitted. if `false`, nothing is emitted. The exception -/// is always rethrown by [performOnState] to be handled by the caller. -Future performOnState( - {required BaseState Function() state, - required PerfomOnStateEmitter emitter, - required PerfomOnStateAction action, - bool errorAsState = true}) async { - try { - final currentState = state(); - final stateRefreshing = currentState.mayRefreshing(); - - if (currentState != stateRefreshing) await emitter(stateRefreshing); - - return await action(state(), emitter); - } catch (error, stackTrace) { - if (errorAsState) { - await emitter(ErrorState( - previousState: state().mayNotRefreshing(), - error: error, - stackTrace: stackTrace)); - } - rethrow; - } finally { - final currentState = state(); - final stateRefreshingEnd = currentState.mayNotRefreshing(); - - if (currentState != stateRefreshingEnd) await emitter(stateRefreshingEnd); - } -} - -extension ValueStatePerformExtensions on BaseState { - Stream> perform( - Future> Function(BaseState state) action) { - final controller = StreamController>(); - var lastState = this; - - performOnState( - state: () => lastState, - emitter: (state) { - lastState = state; - controller.add(state); - }, - action: (state, emit) async { - return emit(await action(state)); - }, - ).onError((error, stackTrace) { - // Will be raised in stream as [ErrorState] - }).whenComplete(() { - controller.close(); - }); - - return controller.stream; - } - - Stream> performStream( - Stream> Function(BaseState state) action) { - final controller = StreamController>(); - var lastState = this; - - performOnState( - state: () => lastState, - emitter: (state) { - lastState = state; - controller.add(state); - }, - action: (state, emit) async { - final stream = action(this); - - await stream.forEach(emit); - }, - ).onError((error, stackTrace) { - // Will be raised in stream as [ErrorState] - }).whenComplete(() { - controller.close(); - }); - - return controller.stream; - } -} diff --git a/packages/value_state/lib/src/states.dart b/packages/value_state/lib/src/states.dart deleted file mode 100644 index 0a6b1c2..0000000 --- a/packages/value_state/lib/src/states.dart +++ /dev/null @@ -1,336 +0,0 @@ -/// Base class for handling value states. -abstract class BaseState { - const BaseState(); - - /// The action is processing to get a new value or refresh it. - bool get fetching; - - /// Copy the actual object and according to the state can enable refreshing - BaseState mayRefreshing(); - - /// Copy the actual object and according to the state can disable refreshing - BaseState mayNotRefreshing(); - - /// Visitor pattern to safely enhance class capabilities - R accept(StateVisitor visitor); - - Map get _diagnosticableAttributes => {'fetching': fetching}; - - @override - String toString() { - return '$runtimeType${_prettyPrint(_diagnosticableAttributes)}'; - } - - static String _prettyPrint(Map attributes) => - '(${attributes.entries.map((entry) => '${entry.key}: ${entry.value}').join(', ')})'; -} - -/// State for waiting value and there was no [ReadyState] before. -/// Useful to handle waiting page before first value is displayed or when -/// a user is disconnected. -abstract class WaitingState extends BaseState { - const WaitingState(); - - @override - WaitingState mayRefreshing(); - - @override - WaitingState mayNotRefreshing(); - - @override - bool get fetching => true; -} - -/// Initial state before any processing. If all has been intialized and -/// the action to get the value is started, then emit a [WaitingState] -class InitState extends WaitingState { - const InitState(); - - @override - WaitingState mayRefreshing() => PendingState(); - - @override - WaitingState mayNotRefreshing() => PendingState(); - - @override - R accept(StateVisitor visitor) => visitor.visitInitState(this); - - @override - bool operator ==(other) => - identical(this, other) || runtimeType == other.runtimeType; - - @override - int get hashCode => runtimeType.hashCode; -} - -/// Initial state before any processing. If all has been intialized and -/// the action to get the value is started, then emit a [WaitingState] -class PendingState extends WaitingState { - const PendingState(); - - @override - WaitingState mayRefreshing() => this; - - @override - WaitingState mayNotRefreshing() => this; - - @override - R accept(StateVisitor visitor) => visitor.visitPendingState(this); - - @override - bool operator ==(other) => - identical(this, other) || runtimeType == other.runtimeType; - @override - int get hashCode => runtimeType.hashCode; -} - -/// Abstract class for all states that a value, no value or error has been -/// received. -abstract class ReadyState extends BaseState { - const ReadyState(); - - /// This property indicate an action is processing from a [ReadyState] to get a new state. - /// [ValueState], [NoValueState] or [ErrorState] will be emitted. - bool get refreshing; - - @override - bool get fetching => refreshing; - - /// Current state carry a value ? - bool get hasValue; - - /// Current state is an error ? - bool get hasError; - - @override - ReadyState mayRefreshing(); - - @override - ReadyState mayNotRefreshing(); -} - -/// State with no value (support null safety). -class NoValueState extends ReadyState { - const NoValueState({this.refreshing = false}); - - @override - final hasValue = false; - - @override - final hasError = false; - - @override - final bool refreshing; - - @override - ReadyState mayRefreshing() => NoValueState(refreshing: true); - - @override - ReadyState mayNotRefreshing() => NoValueState(refreshing: false); - - @override - R accept(StateVisitor visitor) => visitor.visitNoValueState(this); - - @override - bool operator ==(other) => - identical(this, other) || - runtimeType == other.runtimeType && - other is NoValueState && - refreshing == other.refreshing; - @override - int get hashCode => refreshing.hashCode; -} - -/// Abstraction of a state with a value [ValueState] or [ErrorState] -abstract class WithValueState extends ReadyState { - /// Value associated with state - T get value; -} - -/// State that provide the value. -class ValueState extends ReadyState implements WithValueState { - const ValueState(this.value, {this.refreshing = false}); - - @override - final T value; - - @override - final hasValue = true; - - @override - final hasError = false; - - @override - final bool refreshing; - - @override - ReadyState mayRefreshing() => ValueState(value, refreshing: true); - - @override - ReadyState mayNotRefreshing() => ValueState(value, refreshing: false); - - @override - R accept(StateVisitor visitor) => visitor.visitValueState(this); - - @override - bool operator ==(other) => - identical(this, other) || - runtimeType == other.runtimeType && - other is ValueState && - refreshing == other.refreshing && - value == other.value; - @override - int get hashCode => Object.hash(refreshing, value); - - @override - Map get _diagnosticableAttributes => { - ...super._diagnosticableAttributes, - 'value': value, - }; -} - -/// State for error (may be linked with a [ValueState] or not) -abstract class ErrorState extends ReadyState { - factory ErrorState({ - required BaseState previousState, - required Object error, - StackTrace? stackTrace, - bool refreshing = false, - }) { - final stateBeforeError = _consumePreviousErrors(previousState); - - if (stateBeforeError is ValueState) { - return ErrorWithPreviousValue._( - stateBeforeError: stateBeforeError, - error: error, - stackTrace: stackTrace, - refreshing: refreshing); - } - - return ErrorWithoutPreviousValue._( - stateBeforeError: stateBeforeError, - error: error, - stackTrace: stackTrace, - refreshing: refreshing); - } - - const ErrorState._({ - required this.error, - required this.stackTrace, - required this.stateBeforeError, - required this.refreshing, - }); - - /// Previous state that is not [ErrorState]. If several errors are - /// triggered, they are also ignored. - final BaseState stateBeforeError; - - /// The error object. - final Object error; - - @override - final hasError = true; - - /// The error stack trace. - final StackTrace? stackTrace; - - /// Current error has previous value - @override - bool get hasValue => stateBeforeError is ValueState; - - static BaseState _consumePreviousErrors(BaseState state) => - state is ErrorState - ? _consumePreviousErrors(state.stateBeforeError) - : state.mayNotRefreshing(); - - @override - final bool refreshing; - - @override - ReadyState mayRefreshing() => ErrorState( - previousState: stateBeforeError, - refreshing: true, - error: error, - stackTrace: stackTrace, - ); - - @override - ReadyState mayNotRefreshing() => ErrorState( - previousState: stateBeforeError, - refreshing: false, - error: error, - stackTrace: stackTrace, - ); - - @override - R accept(StateVisitor visitor) => visitor.visitErrorState(this); - - @override - bool operator ==(other) => - identical(this, other) || - runtimeType == other.runtimeType && - other is ErrorState && - refreshing == other.refreshing && - error == other.error && - stackTrace == other.stackTrace && - stateBeforeError == other.stateBeforeError; - @override - int get hashCode => - Object.hash(refreshing, error, stackTrace, stateBeforeError); - - @override - Map get _diagnosticableAttributes => { - ...super._diagnosticableAttributes, - 'error': error, - if (stackTrace != null) 'stackTrace': stackTrace, - 'stateBeforeError': stateBeforeError, - }; -} - -/// An error with a [ValueState] as previous state -class ErrorWithoutPreviousValue extends ErrorState { - const ErrorWithoutPreviousValue._({ - required Object error, - required StackTrace? stackTrace, - required BaseState stateBeforeError, - required bool refreshing, - }) : super._( - error: error, - stackTrace: stackTrace, - stateBeforeError: stateBeforeError, - refreshing: refreshing); -} - -/// An error with a [ValueState] as previous state -class ErrorWithPreviousValue extends ErrorState - implements WithValueState { - const ErrorWithPreviousValue._({ - required Object error, - required StackTrace? stackTrace, - required ValueState stateBeforeError, - required bool refreshing, - }) : super._( - error: error, - stackTrace: stackTrace, - stateBeforeError: stateBeforeError, - refreshing: refreshing); - - @override - ValueState get stateBeforeError => super.stateBeforeError as ValueState; - - @override - T get value => stateBeforeError.value; -} - -/// Visitor base class to enhance states capabilities -abstract class StateVisitor { - const StateVisitor(); - - R visitInitState(InitState state); - R visitPendingState(PendingState state); - - R visitValueState(ValueState state); - R visitNoValueState(NoValueState state); - - R visitErrorState(ErrorState state); -} diff --git a/packages/value_state/lib/src/value.dart b/packages/value_state/lib/src/value.dart new file mode 100644 index 0000000..8cb27d2 --- /dev/null +++ b/packages/value_state/lib/src/value.dart @@ -0,0 +1,209 @@ +import 'package:meta/meta.dart'; + +/// A class that represents a value that can be in one of three states: +/// * [ValueState.initial] - the initial state of the value. +/// * [ValueState.success] - the state when the value is successfully fetched. +/// * [ValueState.failure] - the state when the value has failed to fetch. +enum ValueState { + initial, + success, + failure, +} + +/// A convenient class to handle different states of a value. +/// The three states are enumerated in [ValueState]. +/// +/// [T] cannot be `null` or `void` : +/// * if you need a nullable data, use an `Optional` class pattern as type. +/// * if you want to follow excution flow with `void`, create or use a `Unit` +/// class. +final class Value { + /// Create a value in the initial state. + const Value.initial({bool isFetching = false}) + : this._( + data: null, + failure: null, + isFetching: isFetching, + ); + + /// Create a value in the success state with [data]. + const Value.success(T data, {bool isFetching = false}) + : this._( + data: data, + failure: null, + isFetching: isFetching, + ); + + /// Map a [Value] to `failure` with actual [data] if any. + /// + /// There is no [Value.failure] constructor to prevent developers from + /// forgetting to retain the [data] from a previous [Value]. + Value toFailure( + Object error, { + StackTrace? stackTrace, + bool isFetching = false, + }) => + Value._( + data: data, + failure: _Failure(error, stackTrace: stackTrace), + isFetching: isFetching, + ); + + /// Create a [Value] in the [ValueState.failure] state. + /// This is only for tests purpose. + @visibleForTesting + Value.failure( + Object error, { + StackTrace? stackTrace, + bool isFetching = false, + }) : this._( + data: null, + failure: _Failure(error, stackTrace: stackTrace), + isFetching: isFetching, + ); + + const Value._({ + required this.isFetching, + required this.data, + required _Failure? failure, + }) : _failure = failure; + + /// A new value state will be available. It can start from + /// [ValueState.initial] or a previous [ValueState.success] or + /// [ValueState.failure]. + final bool isFetching; + + /// Get data if available, otherwise return `null`. + /// [Value] can have [data] in this [state] : + /// * [ValueState.success] - when the value is successfully fetched, + /// * [ValueState.failure] - when the value has failed to `fetch` and the + /// previous [Value] has [data]. + final T? data; + + final _Failure? _failure; + + /// Get error if available, otherwise return `null`. + Object? get error => _failure?.error; + + /// Get stackTrace if available, otherwise return `null`. + StackTrace? get stackTrace => _failure?.stackTrace; + + /// Get state of the value. + ValueState get state => switch (this) { + Value(hasError: true) => ValueState.failure, + Value(hasData: true) => ValueState.success, + _ => ValueState.initial, + }; + + /// Check if the value is in the initial state. + bool get isInitial => state == ValueState.initial; + + /// Check if the value is in the success state. + /// If the generic type T is nullable, [isSuccess] will return true if the + /// data is `null`. + bool get isSuccess => state == ValueState.success; + + /// Check if the value is in the failure state. + bool get isFailure => state == ValueState.failure; + + /// Get data if the value is in the [ValueState.success] state, otherwise + /// return `null`. + T? get dataOnSuccess => isSuccess ? data : null; + + /// Get data if the value is in the [ValueState.failure] state, otherwise + /// return `null`. A [Value] in the [ValueState.failure] state can have [data] + /// if previous value before `fetch` has [data]. + T? get previousDataOnFailure => isFailure ? data : null; + + /// Check if the value has data. It is a bit different of [isSuccess] because + /// [ValueState.failure] can have data (from previous state). + bool get hasData => data != null; + + /// Check if the value has error (available only in + /// [ValueState.failure]). + bool get hasError => _failure != null; + + /// Check if the value has stack trace (available only in + /// [ValueState.failure]). + bool get hasStackTrace => _failure?.stackTrace != null; + + /// Check if the value is fecthing again : the current state is fetching with + /// a previous fetched state ([ValueState.success] or [ValueState.failure]). + bool get isRefetching => !isInitial && isFetching; + + /// Copy the actual object with fetching as [isFetching]. + Value copyWithFetching(bool isFetching) => this.isFetching != isFetching + ? Value._( + data: data, + failure: _failure, + isFetching: isFetching, + ) + : this; + + /// Merge two values with different type. It is intendend to facilitate + /// data mapping from a value to another without handling [Value.isFetching] + /// and [Value.error]/[Value.stackTrace] attributes. + Value merge( + Value from, { + Value Function(F from)? mapData, + }) => + mapData != null && from.data != null + ? mapData(from.data!).copyWithFetching(from.isFetching) + : Value._( + data: this.data, + failure: from._failure, + isFetching: from.isFetching, + ); + + @override + bool operator ==(Object other) => + identical(this, other) || + runtimeType == other.runtimeType && + other is Value && + isFetching == other.isFetching && + data == other.data && + _failure == other._failure; + + @override + int get hashCode => Object.hash(data, _failure, isFetching); + + @override + String toString() { + final prettyPrint = _attributes.entries + .where((entry) => entry.value?.toString().isNotEmpty ?? false) + .map((entry) => '${entry.key}: ${entry.value}') + .join(', '); + + return '$runtimeType($prettyPrint)'; + } + + Map get _attributes => { + 'state': state, + 'isFetching': isFetching, + if (!isInitial) 'data': data, + ...?_failure?._attributes, + }; +} + +final class _Failure { + const _Failure(this.error, {this.stackTrace}); + + final Object error; + final StackTrace? stackTrace; + + @override + bool operator ==(Object other) => + identical(this, other) || + runtimeType == other.runtimeType && + other is _Failure && + error == other.error && + stackTrace == other.stackTrace; + + @override + int get hashCode => Object.hash(error, stackTrace); + + Map get _attributes => { + 'error': error, + 'stackTrace': stackTrace, + }; +} diff --git a/packages/value_state/lib/value_state.dart b/packages/value_state/lib/value_state.dart index d62aa34..f73faca 100644 --- a/packages/value_state/lib/value_state.dart +++ b/packages/value_state/lib/value_state.dart @@ -1,6 +1,3 @@ -library value_state; - export 'src/extensions.dart'; -export 'src/helpers.dart'; -export 'src/perform.dart'; -export 'src/states.dart'; +export 'src/fetch.dart'; +export 'src/value.dart'; diff --git a/packages/value_state/pubspec.yaml b/packages/value_state/pubspec.yaml index ba10a5f..29b8878 100644 --- a/packages/value_state/pubspec.yaml +++ b/packages/value_state/pubspec.yaml @@ -5,13 +5,12 @@ homepage: https://github.com/devobs version: 1.5.1 environment: - sdk: ">=2.17.1 <3.0.0" + sdk: ^3.4.0 dependencies: meta: ^1.7.0 stream_transform: ^2.0.0 - synchronized: ^3.0.0 dev_dependencies: - lints: ^2.0.0 + lints: ^5.0.0 test: ^1.20.1 diff --git a/packages/value_state/test/extensions_test.dart b/packages/value_state/test/extensions_test.dart index 578aee2..2cedef3 100644 --- a/packages/value_state/test/extensions_test.dart +++ b/packages/value_state/test/extensions_test.dart @@ -2,168 +2,58 @@ import 'package:test/test.dart'; import 'package:value_state/value_state.dart'; void main() { + const myStrInitial = 'Initial'; const myStr = 'My String'; - const myStrOrElse = 'My String orElse'; + const myError = 'Test Error'; + const myOrElse = 'Or Else'; - group('toState()', () { - test('on a non null String', () { - expect(myStr.toState(), const ValueState(myStr)); - expect(myStr.toState(refreshing: true), - const ValueState(myStr, refreshing: true)); + group('when', () { + test('on initial', () { + expect(const Value.initial().when(initial: () => myStrInitial), + myStrInitial); }); - test('on null', () { - const String? nullStr = null; - expect(nullStr.toState(), const NoValueState()); - expect(nullStr.toState(refreshing: true), - const NoValueState(refreshing: true)); + test('on success', () { + expect(const Value.success(myStr).when(success: (data) => data), myStr); }); - }); - - String? modifier(String value) => '$value modified'; - - group('withValue', () { - test('on a $ValueState', () { - final result = myStr.toState().withValue(modifier); - - expect(result, modifier(myStr)); - }); - - test('on a $ValueState with onlyValueState to true', () { - final result = myStr.toState().withValue(modifier, onlyValueState: true); - - expect(result, modifier(myStr)); - }); - - test('on a $InitState', () { - final result = const InitState().withValue(modifier); - - expect(result, isNull); - }); - - test('on a $InitState with onlyValueState to true', () { - final result = - const InitState().withValue(modifier, onlyValueState: true); - - expect(result, isNull); - }); - - test('on a $ErrorState', () { - final result = - ErrorState(error: 'Error', previousState: myStr.toState()) - .withValue(modifier); - expect(result, modifier(myStr)); + test('on data', () { + expect(const Value.success(myStr).when(data: (data) => data), myStr); }); - test('on a $ErrorState with onlyValueState to true', () { - final result = - ErrorState(error: 'Error', previousState: myStr.toState()) - .withValue(modifier, onlyValueState: true); - - expect(result, isNull); + test('on data with error', () { + expect( + const Value.success(myStr).toFailure(myError).when( + data: (data) => data, + failure: (error) => myError, + ), + myStr); }); - test('orElse on a $ValueState', () { - final result = - myStr.toState().withValue(modifier).orElse(() => myStrOrElse); - - expect(result, modifier(myStr)); + test('on failure', () { + expect( + const Value.initial().toFailure(myError).when( + data: (data) => data, + failure: (error) => error, + ), + myError); }); - test('orElse on a $InitState', () { - final result = const InitState() - .withValue(modifier) - .orElse(() => myStrOrElse); - - expect(result, myStrOrElse); + test('on orElse', () { + expect( + const Value.initial().when( + data: (data) => myStr, + orElse: () => myOrElse, + ), + myOrElse); }); - test('expression on a $ValueState', () { - String? result; - myStr.toState().withValue((value) { - result = modifier(value); - }); - - expect(result, modifier(myStr)); - }); - }); - - group('whenValue', () { - test('on a $ValueState', () { - final result = myStr.toState().whenValue(modifier); - - expect(result, modifier(myStr)); - }); - - test('on a $ErrorState', () { - final result = - ErrorState(error: 'Error', previousState: myStr.toState()) - .whenValue(modifier); - - expect(result, isNull); + test('on fallback', () { + expect( + const Value.initial().when( + data: (data) => myStr, + ), + isNull); }); }); - - group('toValue', () { - test('on a $ValueState', () { - final result = myStr.toState().toValue(); - - expect(result, myStr); - }); - - test('on a $ValueState with onlyValueState to true', () { - final result = myStr.toState().toValue(onlyValueState: true); - - expect(result, myStr); - }); - - test('on a $InitState', () { - final result = const InitState().toValue(); - - expect(result, isNull); - }); - - test('on a $InitState with onlyValueState to true', () { - final result = const InitState().toValue(onlyValueState: true); - - expect(result, isNull); - }); - - test('on a $ErrorState', () { - final result = - ErrorState(error: 'Error', previousState: myStr.toState()) - .toValue(); - - expect(result, myStr); - }); - - test('on a $ErrorState with onlyValueState to true', () { - final result = - ErrorState(error: 'Error', previousState: myStr.toState()) - .toValue(onlyValueState: true); - - expect(result, isNull); - }); - }); - - test('toFutureState', () { - const value = 'Result'; - - expect(Future.value(value).toFutureState(), completion(value.toState())); - expect(Future.value(null).toFutureState(), - completion(const NoValueState())); - }); - - test('toStates', () { - const value = 'Result'; - - expect( - Future.value(value).toStates(), - emitsInOrder([ - const PendingState(), - const ValueState(value), - emitsDone, - ])); - }); } diff --git a/packages/value_state/test/fetch_test.dart b/packages/value_state/test/fetch_test.dart new file mode 100644 index 0000000..4a94ec7 --- /dev/null +++ b/packages/value_state/test/fetch_test.dart @@ -0,0 +1,63 @@ +// ignore_for_file: prefer_const_constructors + +import 'package:test/test.dart'; +import 'package:value_state/value_state.dart'; + +import 'stream.dart'; + +void main() { + late CounterStream counterStream; + + setUp(() { + counterStream = CounterStream(); + }); + + tearDown(() async { + await counterStream.close(); + }); + + test('test fetchOnValue with stream', () { + TypeMatcher> isFailure({required isFetching}) => + isA>() + .having((value) => value.isFailure, 'is failure', true) + .having( + (value) => value.isFetching, + 'is fetching $isFetching', + isFetching, + ) + .having( + (value) => value.error, + 'failure content', + isA(), + ); + + expect( + counterStream.stream, + emitsInOrder([ + const Value.initial(), + const Value.initial(isFetching: true), + Value.success(0), + Value.success(0, isFetching: true), + Value.success(1), + Value.success(1, isFetching: true), + Value.success(2), + Value.success(2, isFetching: true), + isFailure(isFetching: false), + isFailure(isFetching: true), + isFailure(isFetching: false), + isFailure(isFetching: true), + Value.success(5), + Value.success(5, isFetching: true), + isFailure(isFetching: false) + .having((value) => value.data, 'data has old value', 5), + isFailure(isFetching: true) + .having((value) => value.data, 'data has old value', 5), + Value.success(7), + Value.success(7, isFetching: true), + Value.success(8), + const Value.initial(isFetching: true), + ])); + + streamStandardActions(counterStream); + }); +} diff --git a/packages/value_state/test/helpers_cubit_test.dart b/packages/value_state/test/helpers_cubit_test.dart deleted file mode 100644 index 860caaf..0000000 --- a/packages/value_state/test/helpers_cubit_test.dart +++ /dev/null @@ -1,229 +0,0 @@ -import 'dart:async'; - -import 'package:test/test.dart'; -import 'package:value_state/value_state.dart'; - -import 'stream.dart'; - -class CounterStreamListener { - CounterStreamListener({required this.counterStream, required this.variant}); - - final CounterStream counterStream; - final bool variant; - - Stream> get stream { - var ignoreMapError = false; - - return counterStream.stream.map((state) { - if (!ignoreMapError && state is WithValueState && state.value > 4) { - ignoreMapError = true; - } - - final result = mapState( - (from) => variant && from == 1 ? null : from + 1, state, - currentState: variant ? _state : null, - mapError: variant && !ignoreMapError - ? (errorState) { - return NoValueState(refreshing: errorState.refreshing); - } - : null, - mapNoValue: variant - ? (refreshing) { - return ValueState(-1, refreshing: refreshing); - } - : null); - - _state = result; - - return result; - }); - } - - BaseState? _state; -} - -void main() { - late CounterStream counterStream; - late CounterStreamListener counterStreamListener; - - setUp(() { - counterStream = CounterStream(); - }); - - tearDown(() async { - await counterStream.close(); - }); - - test('with values incremented', () { - counterStreamListener = - CounterStreamListener(counterStream: counterStream, variant: false); - - expect( - counterStreamListener.stream, - emitsInOrder([ - const InitState(), - const PendingState(), - isA>() - .having((state) => state.refreshing, 'first value not refreshing', - false) - .having((state) => state.value, 'first value', 1), - isA>() - .having((state) => state.refreshing, - 'second value not refreshing', true) - .having((state) => state.value, 'second value', 1), - isA>() - .having((state) => state.refreshing, - 'second value not refreshing', false) - .having((state) => state.value, 'second value', 2), - // refresh with no value after value - isA>() - .having( - (state) => state.refreshing, 'second value refreshing', true) - .having((state) => state.value, 'second value', 2), - isA>() - .having((state) => state.refreshing, 'no value', false), - isA>() - .having((state) => state.refreshing, 'no value refreshing', true), - isA>() - .having((state) => state.refreshing, - 'error for third value not refreshing', false) - .having( - (state) => state.stateBeforeError, - 'no value before erreur', - isA>() - .having((state) => state.refreshing, 'no value', false), - ) - .having((state) => state.hasValue, 'second value before erreur', - false), - // refresh with error after error - isA>().having( - (state) => state.refreshing, - 'error for third value refreshing', - true), - isA>() - .having((state) => state.refreshing, - 'error for fourth value not refreshing', false) - .having( - (state) => state.stateBeforeError, - 'no value before erreur', - isA>() - .having((state) => state.refreshing, 'no value', false), - ) - .having((state) => state.hasValue, 'second value before erreur', - false), - // refresh after arror - isA>().having( - (state) => state.refreshing, - 'error for fourth value refreshing', - true), - isA>() - .having((state) => state.refreshing, 'fifth value not refreshing', - false) - .having((state) => state.value, 'fifth value ', 6), - isA>() - .having( - (state) => state.refreshing, 'fifth value refreshing', true) - .having((state) => state.value, 'fifth value', 6), - isA>() - .having((state) => state.refreshing, - 'error for sixth value refreshing', false) - .having( - (state) => state.hasValue, 'error for sixth has value', true) - .having((state) => state.value, - 'error for sixth value refreshing', 6), - isA>().having((state) => state.refreshing, - 'error for sixth value refreshing', true), - isA>() - .having((state) => state.refreshing, - 'seventh value not refreshing', false) - .having((state) => state.value, 'seventh value', 8), - isA>() - .having( - (state) => state.refreshing, 'seventh value refreshing', true) - .having((state) => state.value, 'seventh value', 8), - isA>() - .having((state) => state.refreshing, - 'eighth value not refreshing', false) - .having((state) => state.value, 'eighth value', 9), - // after _myRefresh.clear() triggered - isA>(), - ])); - - streamStandardActions(counterStream); - }); - - test('with values incremented and variant', () { - counterStreamListener = - CounterStreamListener(counterStream: counterStream, variant: true); - - expect( - counterStreamListener.stream, - emitsInOrder([ - const InitState(), - const PendingState(), - isA>() - .having((state) => state.refreshing, 'first value not refreshing', - false) - .having((state) => state.value, 'first value', 1), - isA>() - .having((state) => state.refreshing, - 'second value not refreshing', true) - .having((state) => state.value, 'second value', 1), - isA>().having((state) => state.refreshing, - 'error for fourth value not refreshing', false), - isA>().having((state) => state.refreshing, - 'error for fourth value refreshing', true), - isA>() - .having((state) => state.refreshing, - 'second value not refreshing', false) - .having((state) => state.value, 'second value', -1), - isA>() - .having( - (state) => state.refreshing, 'second value refreshing', true) - .having((state) => state.value, 'second value', -1), - // refresh with error after error - isA>().having((state) => state.refreshing, - 'error for fourth value not refreshing', false), - isA>().having((state) => state.refreshing, - 'error for fourth value refreshing', true), - isA>().having((state) => state.refreshing, - 'error for fourth value not refreshing', false), - isA>().having((state) => state.refreshing, - 'error for fourth value refreshing', true), - // refresh after arror - isA>() - .having((state) => state.refreshing, 'fifth value not refreshing', - false) - .having((state) => state.value, 'fifth value ', 6), - isA>() - .having( - (state) => state.refreshing, 'fifth value refreshing', true) - .having((state) => state.value, 'fifth value', 6), - isA>() - .having((state) => state.refreshing, - 'error for sixth value refreshing', false) - .having( - (state) => state.hasValue, 'error for sixth has value', true) - .having((state) => state.value, - 'error for sixth value refreshing', 6), - isA>().having((state) => state.refreshing, - 'error for sixth value refreshing', true), - isA>() - .having((state) => state.refreshing, - 'seventh value not refreshing', false) - .having((state) => state.value, 'seventh value', 8), - isA>() - .having( - (state) => state.refreshing, 'seventh value refreshing', true) - .having((state) => state.value, 'seventh value', 8), - isA>() - .having((state) => state.refreshing, - 'eighth value not refreshing', false) - .having((state) => state.value, 'eighth value', 9), - // after _myRefresh.clear() triggered - isA>(), - ])); - - streamStandardActions(counterStream); - }); -} diff --git a/packages/value_state/test/helpers_test.dart b/packages/value_state/test/helpers_test.dart index c2ef75b..4762f69 100644 --- a/packages/value_state/test/helpers_test.dart +++ b/packages/value_state/test/helpers_test.dart @@ -1,31 +1,97 @@ +import 'dart:async'; + import 'package:test/test.dart'; import 'package:value_state/value_state.dart'; void main() { - test('perform on $ValueState', () { - final stream = - const ValueState(1).perform((state) async => const ValueState(2)); + test('fetch on ${Value}', () { + final stream = const Value.success(1).fetchFrom(() async => 2); expect( stream, emitsInOrder([ - const ValueState(1, refreshing: true), - const ValueState(2, refreshing: false), + const Value.success(1, isFetching: true), + const Value.success(2, isFetching: false), + emitsDone, ])); }); - test('performStream on $ValueState', () { - final stream = const ValueState(1).performStream((state) async* { - yield const ValueState(2); - yield const ValueState(3); - }); + group('fetchStream', () { + test('success', () { + final stream = const Value.success(1).fetchFromStream( + () => Stream.fromIterable(const [Value.success(2), Value.success(3)]), + ); - expect( + expect( stream, emitsInOrder([ - const ValueState(1, refreshing: true), - const ValueState(2, refreshing: false), - const ValueState(3, refreshing: false), - ])); + const Value.success(1, isFetching: true), + const Value.success(2, isFetching: false), + const Value.success(3, isFetching: false), + emitsDone, + ]), + ); + + expect(ValueFetch.errors, emitsInOrder([])); + }); + + group('failure', () { + const myExceptionStr = 'My exception'; + Never throwMyException() => fail(myExceptionStr); + final isMyException = isA().having( + (tf) => tf.message, + 'message', + myExceptionStr, + ); + + test('with keepLastOnError set to false', () { + final stream = const Value.success(1).fetchFromStream(() async* { + yield const Value.success(2); + yield const Value.success(3); + throwMyException(); + }); + + expect( + stream, + emitsInOrder([ + const Value.success(1, isFetching: true), + const Value.success(2, isFetching: false), + const Value.success(3, isFetching: false), + isA() + .having((v) => v.error, 'error', isMyException) + .having((v) => v.data, 'data', 1), + emitsDone, + ]), + ); + + expect(ValueFetch.errors, emits(isA())); + }); + + test('with keepLastOnError set to true', () { + final stream = const Value.success(1).fetchFromStream( + () async* { + yield const Value.success(2); + yield const Value.success(3); + throwMyException(); + }, + keepLastOnError: true, + ); + + expect( + stream, + emitsInOrder([ + const Value.success(1, isFetching: true), + const Value.success(2, isFetching: false), + const Value.success(3, isFetching: false), + isA() + .having((v) => v.error, 'error', isMyException) + .having((v) => v.data, 'data', 3), + emitsDone, + ]), + ); + + expect(ValueFetch.errors, emits(isA())); + }); + }); }); } diff --git a/packages/value_state/test/stream.dart b/packages/value_state/test/stream.dart index 953494d..0753d01 100644 --- a/packages/value_state/test/stream.dart +++ b/packages/value_state/test/stream.dart @@ -2,16 +2,14 @@ import 'dart:async'; import 'package:stream_transform/stream_transform.dart'; import 'package:test/expect.dart'; +import 'package:value_state/src/fetch_on_value.dart'; import 'package:value_state/value_state.dart'; class CounterStream { var _value = 0; - Future _getMyValueFromRepository() async { + Future _getMyValueFromRepository() async { final value = _value++; switch (value) { - case 2: - return null; - case 3: case 4: case 6: @@ -21,33 +19,35 @@ class CounterStream { } } - BaseState state = const InitState(); + Value state = const Value.initial(); - final _resultStreamController = StreamController>(); - Stream> get stream => Stream.value(state) + final _resultStreamController = StreamController>(); + Stream> get stream => Stream.value(state) .followedBy(_resultStreamController.stream) .handleError((_) {}); var errorsRaisedCount = 0; Future incrementValue() async { - await performOnState( - state: () => state, - emitter: (state) { - this.state = state; - _resultStreamController.add(this.state); - }, - action: (state, emitter) async { - final result = await _getMyValueFromRepository(); - - emitter(result == null ? const NoValueState() : ValueState(result)); - }).onError((error, stackTrace) { + await fetchOnValue( + value: () => state, + emitter: (state) { + this.state = state; + _resultStreamController.add(this.state); + }, + action: (state, emitter) async { + final result = await _getMyValueFromRepository(); + + emitter(Value.success(result)); + }, + lastValueOnError: false, + ).onError((error, stackTrace) { errorsRaisedCount++; }); } void clear() { - _resultStreamController.add(const PendingState()); + _resultStreamController.add(const Value.initial(isFetching: true)); } Future close() async { diff --git a/packages/value_state/test/value_state_test.dart b/packages/value_state/test/value_state_test.dart index 99fe38e..be12250 100644 --- a/packages/value_state/test/value_state_test.dart +++ b/packages/value_state/test/value_state_test.dart @@ -1,223 +1,394 @@ +// ignore_for_file: prefer_const_constructors + import 'package:test/test.dart'; import 'package:value_state/value_state.dart'; -import 'stream.dart'; - void main() { - group('test with stream', () { - late CounterStream counterStream; + const value = 0; + final valueStr = value.toString(); + const error = 'Error'; + final stackTrace = StackTrace.fromString('My StackTrace'); - setUp(() { - counterStream = CounterStream(); + group('test getters', () { + test('initial state', () { + final state = Value.initial(); + + expect(state.isInitial, isTrue); + expect(state.isFetching, isFalse); + expect(state.isRefetching, isFalse); + expect(state.isSuccess, isFalse); + expect(state.isFailure, isFalse); + expect(state.dataOnSuccess, isNull); + expect(state.previousDataOnFailure, isNull); + expect(state.hasData, isFalse); + expect(state.hasError, isFalse); + expect(state.hasStackTrace, isFalse); + expect(state.data, isNull); + expect(state.error, isNull); + expect(state.stackTrace, isNull); }); - tearDown(() async { - await counterStream.close(); + test('initial state fetching', () { + final state = Value.initial(isFetching: true); + + expect(state.isInitial, isTrue); + expect(state.isFetching, isTrue); + expect(state.isRefetching, isFalse); + expect(state.isSuccess, isFalse); + expect(state.isFailure, isFalse); + expect(state.dataOnSuccess, isNull); + expect(state.previousDataOnFailure, isNull); + expect(state.hasData, isFalse); + expect(state.hasError, isFalse); + expect(state.hasStackTrace, isFalse); + expect(state.data, isNull); + expect(state.error, isNull); + expect(state.stackTrace, isNull); }); - test('with values incremented', () { - expect( - counterStream.stream, - emitsInOrder([ - isA>() - .having((state) => state.fetching, 'init fetching', true), - isA>() - .having((state) => state.fetching, 'init fetching', true), - isA>() - .having((state) => state.refreshing, - 'first value not refreshing', false) - .having((state) => state.fetching, 'first value not fetching', - false) - .having((state) => state.value, 'first value', 0), - isA>() - .having((state) => state.refreshing, - 'second value not refreshing', true) - .having( - (state) => state.fetching, 'second value fetching', true) - .having((state) => state.value, 'second value', 0), - isA>() - .having((state) => state.refreshing, - 'second value not refreshing', false) - .having((state) => state.value, 'second value', 1), - // refresh with no value after value - isA>() - .having((state) => state.refreshing, 'second value refreshing', - true) - .having((state) => state.value, 'second value', 1), - isA>() - .having((state) => state.refreshing, 'no value', false), - isA>().having( - (state) => state.refreshing, 'no value refreshing', true), - isA>() - .having((state) => state.refreshing, - 'error for third value not refreshing', false) - .having( - (state) => state.stateBeforeError, - 'no value before erreur', - isA>() - .having((state) => state.refreshing, 'no value', false), - ) - .having((state) => state.hasValue, 'second value before erreur', - false), - // refresh with error after error - isA>().having( - (state) => state.refreshing, - 'error for third value refreshing', - true), - isA>() - .having((state) => state.refreshing, - 'error for fourth value not refreshing', false) - .having( - (state) => state.stateBeforeError, - 'no value before erreur', - isA>() - .having((state) => state.refreshing, 'no value', false), - ) - .having((state) => state.hasValue, 'second value before erreur', - false), - // refresh after arror - isA>().having( - (state) => state.refreshing, - 'error for fourth value refreshing', - true), - isA>() - .having((state) => state.refreshing, - 'fifth value not refreshing', false) - .having((state) => state.value, 'fifth value ', 5), - isA>() - .having( - (state) => state.refreshing, 'fifth value refreshing', true) - .having((state) => state.value, 'fifth value', 5), - isA>() - .having((state) => state.refreshing, - 'error for sixth value refreshing', false) - .having((state) => state.hasValue, 'error for sixth has value', - true) - .having((state) => state.value, - 'error for sixth value refreshing', 5), - isA>().having( - (state) => state.refreshing, - 'error for sixth value refreshing', - true), - isA>() - .having((state) => state.refreshing, - 'seventh value not refreshing', false) - .having((state) => state.value, 'seventh value', 7), - isA>() - .having((state) => state.refreshing, 'seventh value refreshing', - true) - .having((state) => state.value, 'seventh value', 7), - isA>() - .having((state) => state.refreshing, - 'eighth value not refreshing', false) - .having((state) => state.value, 'eighth value', 8), - // after _myRefresh.clear() triggered - isA>(), - ])); - - streamStandardActions(counterStream); + test('success state', () { + final state = Value.success(value); + + expect(state.isInitial, isFalse); + expect(state.isFetching, isFalse); + expect(state.isRefetching, isFalse); + expect(state.isSuccess, isTrue); + expect(state.isFailure, isFalse); + expect(state.hasData, isTrue); + expect(state.hasError, isFalse); + expect(state.hasStackTrace, isFalse); + expect(state.data, value); + expect(state.error, isNull); + expect(state.stackTrace, isNull); }); - }); - test('equalities and hash', () { - // Dont create object with [const] to avoid [identical] return true - const initState1 = InitState(), initState2 = InitState(); + test('success state', () { + final state = Value.success(value); - expect(initState1, initState2); - expect(initState1.hashCode, initState2.hashCode); + expect(state.isInitial, isFalse); + expect(state.isFetching, isFalse); + expect(state.isRefetching, isFalse); + expect(state.isSuccess, isTrue); + expect(state.isFailure, isFalse); + expect(state.dataOnSuccess, value); + expect(state.previousDataOnFailure, isNull); + expect(state.hasData, isTrue); + expect(state.hasError, isFalse); + expect(state.hasStackTrace, isFalse); + expect(state.data, value); + expect(state.error, null); + expect(state.stackTrace, null); + }); - const waitingState1 = PendingState(), - waitingState2 = PendingState(); + test('success state fetching', () { + final state = Value.success(value, isFetching: true); - expect(waitingState1, waitingState2); - expect(waitingState1.hashCode, waitingState2.hashCode); + expect(state.isInitial, isFalse); + expect(state.isFetching, isTrue); + expect(state.isRefetching, isTrue); + expect(state.isFetching, isTrue); + expect(state.isFailure, isFalse); + expect(state.dataOnSuccess, value); + expect(state.previousDataOnFailure, isNull); + expect(state.hasData, isTrue); + expect(state.hasError, isFalse); + expect(state.hasStackTrace, isFalse); + expect(state.data, value); + expect(state.error, isNull); + expect(state.stackTrace, isNull); + }); - expect(waitingState1.mayRefreshing(), waitingState1); - expect(waitingState1.mayNotRefreshing(), waitingState2); + test('failure state', () { + final state = Value.failure(error, stackTrace: stackTrace); - const noValueState1 = NoValueState(), - noValueState2 = NoValueState(); + expect(state.isInitial, isFalse); + expect(state.isFetching, isFalse); + expect(state.isRefetching, isFalse); + expect(state.isSuccess, isFalse); + expect(state.isFailure, isTrue); + expect(state.hasData, isFalse); + expect(state.hasError, isTrue); + expect(state.hasStackTrace, isTrue); + expect(state.data, isNull); + expect(state.error, error); + expect(state.stackTrace, stackTrace); + }); - expect(noValueState1, noValueState2); - expect(noValueState1.hashCode, noValueState2.hashCode); + test('failure state fetching', () { + final state = Value.failure( + error, + stackTrace: stackTrace, + isFetching: true, + ); - const valueState1 = ValueState(0), valueState2 = ValueState(0); + expect(state.isInitial, isFalse); + expect(state.isFetching, isTrue); + expect(state.isRefetching, isTrue); + expect(state.isSuccess, isFalse); + expect(state.isFailure, isTrue); + expect(state.dataOnSuccess, isNull); + expect(state.previousDataOnFailure, isNull); + expect(state.hasData, isFalse); + expect(state.hasError, isTrue); + expect(state.hasStackTrace, isTrue); + expect(state.data, isNull); + expect(state.error, error); + expect(state.stackTrace, stackTrace); + }); - expect(valueState1, valueState2); - expect(valueState1.hashCode, valueState2.hashCode); + test('success to failure state', () { + final state = Value.success(value).toFailure(error); - final errorState1 = - ErrorState(previousState: const InitState(), error: 'Error'), - errorState2 = - ErrorState(previousState: const InitState(), error: 'Error'); + expect(state.isInitial, isFalse); + expect(state.isFetching, isFalse); + expect(state.isRefetching, isFalse); + expect(state.isSuccess, isFalse); + expect(state.isFailure, isTrue); + expect(state.dataOnSuccess, isNull); + expect(state.previousDataOnFailure, value); + expect(state.hasData, isTrue); + expect(state.hasError, isTrue); + expect(state.hasStackTrace, isFalse); + expect(state.data, value); + expect(state.error, error); + expect(state.stackTrace, isNull); + }); + }); - expect(errorState1, errorState2); - expect(errorState1.hashCode, errorState2.hashCode); + group('merge', () { + test('merge success in initial', () { + final state = Value.initial().merge( + Value.success(valueStr), + mapData: (from) => Value.success(int.parse(from)), + ); - final errorStateWithValue1 = - ErrorState(previousState: const ValueState(1), error: 'Error'), - errorStateWithValue2 = - ErrorState(previousState: const ValueState(1), error: 'Error'); + expect(state.isInitial, isFalse); + expect(state.isFetching, isFalse); + expect(state.isRefetching, isFalse); + expect(state.isSuccess, isTrue); + expect(state.isFailure, isFalse); + expect(state.hasData, isTrue); + expect(state.hasError, isFalse); + expect(state.hasStackTrace, isFalse); + expect(state.data, value); + expect(state.error, isNull); + expect(state.stackTrace, isNull); + }); + + test('merge success in success', () { + final state = Value.success(value).merge( + Value.success(valueStr), + mapData: (from) => Value.success(int.parse(from)), + ); + + expect(state.isInitial, isFalse); + expect(state.isFetching, isFalse); + expect(state.isRefetching, isFalse); + expect(state.isSuccess, isTrue); + expect(state.isFailure, isFalse); + expect(state.hasData, isTrue); + expect(state.hasError, isFalse); + expect(state.hasStackTrace, isFalse); + expect(state.data, value); + expect(state.error, isNull); + expect(state.stackTrace, isNull); + }); + + test('merge success in failure', () { + final state = Value.failure(error).merge( + Value.success(valueStr, isFetching: true), + mapData: (from) => Value.success(int.parse(from)), + ); + + expect(state.isInitial, isFalse); + expect(state.isFetching, isTrue); + expect(state.isRefetching, isTrue); + expect(state.isSuccess, isTrue); + expect(state.isFailure, isFalse); + expect(state.hasData, isTrue); + expect(state.hasError, isFalse); + expect(state.hasStackTrace, isFalse); + expect(state.data, value); + expect(state.error, isNull); + expect(state.stackTrace, isNull); + }); + + test('merge failure in initial', () { + final state = Value.initial().merge( + Value.failure(error), + mapData: (from) => Value.success(int.parse(from)), + ); + + expect(state.isInitial, isFalse); + expect(state.isFetching, isFalse); + expect(state.isRefetching, isFalse); + expect(state.isSuccess, isFalse); + expect(state.isFailure, isTrue); + expect(state.hasData, isFalse); + expect(state.hasError, isTrue); + expect(state.hasStackTrace, isFalse); + expect(state.data, isNull); + expect(state.error, error); + expect(state.stackTrace, isNull); + }); + + test('merge failure in success', () { + final state = Value.success(value).merge( + Value.failure(error), + mapData: (from) => Value.success(int.parse(from)), + ); + + expect(state.isInitial, isFalse); + expect(state.isFetching, isFalse); + expect(state.isRefetching, isFalse); + expect(state.isSuccess, isFalse); + expect(state.isFailure, isTrue); + expect(state.hasData, isTrue); + expect(state.hasError, isTrue); + expect(state.hasStackTrace, isFalse); + expect(state.data, value); + expect(state.error, error); + expect(state.stackTrace, isNull); + }); - expect(errorStateWithValue1, errorStateWithValue2); - expect(errorStateWithValue1.hashCode, errorStateWithValue2.hashCode); + test('merge initial in failure', () { + final state = Value.failure(error).merge( + Value.initial(), + mapData: (from) => Value.success(int.parse(from)), + ); + + expect(state.isInitial, isTrue); + expect(state.isFetching, isFalse); + expect(state.isRefetching, isFalse); + expect(state.isSuccess, isFalse); + expect(state.isFailure, isFalse); + expect(state.hasData, isFalse); + expect(state.hasError, isFalse); + expect(state.hasStackTrace, isFalse); + expect(state.data, isNull); + expect(state.error, isNull); + expect(state.stackTrace, isNull); + }); }); - test('visitor', () { - const visitor = _TestStateVisitor(); + group('Value mappers', () { + const myStr = 'My String'; + const myError = 'Test Error'; + const initial = Value.initial(); + final success = Value.success(myStr); - expect(const InitState().accept(visitor), 1); - expect(const PendingState().accept(visitor), 4); - expect(const NoValueState().accept(visitor), 2); - expect(const ValueState(0).accept(visitor), 3); - expect( - ErrorState(previousState: const InitState(), error: 'Error') - .accept(visitor), - 0); + final failure = Value.failure(myError, stackTrace: stackTrace); + final failureWithData = success.toFailure(myError, stackTrace: stackTrace); + + test('toFailure', () { + expect( + initial.toFailure( + myError, + stackTrace: stackTrace, + ), + Value.failure(myError, stackTrace: stackTrace), + ); + expect( + initial.toFailure( + myError, + stackTrace: stackTrace, + isFetching: true, + ), + Value.failure( + myError, + stackTrace: stackTrace, + isFetching: true, + ), + ); + + expect( + failure.toFailure(myError, stackTrace: stackTrace), + Value.failure(myError, stackTrace: stackTrace), + ); + expect( + failure.toFailure(myError, stackTrace: stackTrace, isFetching: true), + Value.failure( + myError, + stackTrace: stackTrace, + isFetching: true, + ), + ); + + expect( + failureWithData, + isA>() + .having((value) => value.data, 'has data', failureWithData.data!) + .having((value) => value.isFetching, 'is not fetching', false) + .having((value) => value.isFailure, 'is failure', true) + .having( + (value) => value.error, + 'failure content', + myError, + ), + ); + expect( + success.toFailure(myError, isFetching: true), + isA>() + .having((value) => value.isFetching, 'is fetching', true) + .having((value) => value.isFailure, 'is failure', true) + .having( + (value) => value.error, + 'failure content', + myError, + ), + ); + }); }); - test('toString', () { - expect(const InitState().toString(), - 'InitState(fetching: true)'); + test('test equalities and hash', () { + // Dont create object with [const] to avoid [identical] return true + final init1 = Value.initial(), init2 = Value.initial(); - const pendingState = PendingState(); - expect(pendingState.toString(), 'PendingState(fetching: true)'); + expect(init1, init2); + expect(init1.hashCode, init2.hashCode); - expect(const NoValueState().toString(), - 'NoValueState(fetching: false)'); + final fetching1 = Value.initial(isFetching: true), + fetching2 = Value.initial(isFetching: true); - const valueState = ValueState('My value'); + expect(fetching1, fetching2); + expect(fetching1.hashCode, fetching2.hashCode); - expect(valueState.toString(), - 'ValueState(fetching: false, value: ${valueState.value})'); - expect( - ErrorState(previousState: valueState, error: ArgumentError()) - .toString(), - 'ErrorWithPreviousValue(fetching: false, error: Invalid argument(s), stateBeforeError: $valueState)'); - expect( - ErrorState( - previousState: pendingState, - error: ArgumentError(), - stackTrace: StackTrace.fromString('My StackTrace')) - .toString(), - 'ErrorWithoutPreviousValue(fetching: false, error: Invalid argument(s), stackTrace: My StackTrace, ' - 'stateBeforeError: $pendingState)'); + final success1 = Value.success(value), success2 = Value.success(value); + + expect(success1, success2); + expect(success1.hashCode, success2.hashCode); + + final failure1 = Value.failure(error), + failure2 = Value.failure(error); + + expect(failure1, failure2); + expect(failure1.hashCode, failure2.hashCode); + + final failureWithData1 = success1.toFailure(error), + failureWithData2 = success2.toFailure(error); + + expect(failureWithData1, failureWithData2); + expect(failureWithData1.hashCode, failureWithData2.hashCode); }); -} -class _TestStateVisitor extends StateVisitor { - const _TestStateVisitor(); + test('test toString', () { + expect(const Value.initial().toString(), + 'Value(state: ValueState.initial, isFetching: false)'); - @override - visitInitState(InitState state) => 1; - @override - visitPendingState(PendingState state) => 4; + final value = Value.success('My value'); - @override - visitValueState(ValueState state) => 3; - @override - visitNoValueState(NoValueState state) => 2; + expect( + value.toString(), + 'Value(state: ValueState.success, isFetching: false, ' + 'data: My value)'); + expect( + value.toFailure(ArgumentError()).toString(), + 'Value(state: ValueState.failure, isFetching: false, ' + 'data: My value, error: Invalid argument(s))'); - @override - visitErrorState(ErrorState state) => 0; + expect( + Value.failure(ArgumentError()).toString(), + 'Value(state: ValueState.failure, isFetching: false, ' + 'error: Invalid argument(s))'); + }); } diff --git a/pubspec.lock b/pubspec.lock index 3a4303b..fe4823c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -13,10 +13,10 @@ packages: dependency: transitive description: name: args - sha256: c372bb384f273f0c2a8aaaa226dad84dc27c8519a691b888725dec59518ad53a + sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6 url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.6.0" async: dependency: transitive description: @@ -25,14 +25,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.11.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" - url: "https://pub.dev" - source: hosted - version: "2.1.1" charcode: dependency: transitive description: @@ -41,6 +33,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff + url: "https://pub.dev" + source: hosted + version: "2.0.3" cli_launcher: dependency: transitive description: @@ -53,18 +53,26 @@ packages: dependency: transitive description: name: cli_util - sha256: b8db3080e59b2503ca9e7922c3df2072cf13992354d5e944074ffa836fba43b7 + sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c url: "https://pub.dev" source: hosted - version: "0.4.0" + version: "0.4.2" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" + source: hosted + version: "1.1.2" collection: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.19.1" conventional_commit: dependency: transitive description: @@ -77,10 +85,10 @@ packages: dependency: transitive description: name: file - sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 url: "https://pub.dev" source: hosted - version: "6.1.4" + version: "7.0.1" glob: dependency: transitive description: @@ -101,10 +109,10 @@ packages: dependency: transitive description: name: http - sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2" + sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f url: "https://pub.dev" source: hosted - version: "0.13.6" + version: "1.3.0" http_parser: dependency: transitive description: @@ -113,6 +121,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" + intl: + dependency: transitive + description: + name: intl + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf + url: "https://pub.dev" + source: hosted + version: "0.19.0" io: dependency: transitive description: @@ -129,30 +145,22 @@ packages: url: "https://pub.dev" source: hosted version: "4.8.1" - matcher: - dependency: transitive - description: - name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" - url: "https://pub.dev" - source: hosted - version: "0.12.16" melos: dependency: "direct dev" description: name: melos - sha256: ccbb6ecd8bb3f08ae8f9ce22920d816bff325a98940c845eda0257cd395503ac + sha256: "3f3ab3f902843d1e5a1b1a4dd39a4aca8ba1056f2d32fd8995210fa2843f646f" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "6.3.2" meta: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.16.0" mustache_template: dependency: transitive description: @@ -173,10 +181,10 @@ packages: dependency: transitive description: name: platform - sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.6" pool: dependency: transitive description: @@ -189,10 +197,10 @@ packages: dependency: transitive description: name: process - sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" + sha256: "107d8be718f120bbba9dcd1e95e3bd325b1b4a4f07db64154635ba03f2567a0d" url: "https://pub.dev" source: hosted - version: "4.2.4" + version: "5.0.3" prompts: dependency: transitive description: @@ -213,26 +221,18 @@ packages: dependency: transitive description: name: pub_updater - sha256: "42890302ab2672adf567dc2b20e55b4ecc29d7e19c63b6b98143ab68dd717d3a" + sha256: "54e8dc865349059ebe7f163d6acce7c89eb958b8047e6d6e80ce93b13d7c9e60" url: "https://pub.dev" source: hosted - version: "0.2.4" - pubspec: - dependency: transitive - description: - name: pubspec - sha256: f534a50a2b4d48dc3bc0ec147c8bd7c304280fff23b153f3f11803c4d49d927e - url: "https://pub.dev" - source: hosted - version: "2.3.0" - quiver: + version: "0.4.0" + pubspec_parse: dependency: transitive description: - name: quiver - sha256: b1c1ac5ce6688d77f65f3375a9abb9319b3cb32486bdc7a1e0fdf004d7ba4e47 + name: pubspec_parse + sha256: "81876843eb50dc2e1e5b151792c9a985c5ed2536914115ed04e9c8528f6647b0" url: "https://pub.dev" source: hosted - version: "3.2.1" + version: "1.4.0" source_span: dependency: transitive description: @@ -249,14 +249,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.11.0" - stream_channel: - dependency: transitive - description: - name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" - url: "https://pub.dev" - source: hosted - version: "2.1.1" string_scanner: dependency: transitive description: @@ -273,14 +265,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.1" - test_api: - dependency: transitive - description: - name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" - url: "https://pub.dev" - source: hosted - version: "0.6.0" typed_data: dependency: transitive description: @@ -289,14 +273,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.2" - uri: + web: dependency: transitive description: - name: uri - sha256: "889eea21e953187c6099802b7b4cf5219ba8f3518f604a1033064d45b1b8268a" + name: web + sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.1.0" yaml: dependency: transitive description: @@ -314,4 +298,4 @@ packages: source: hosted version: "2.1.1" sdks: - dart: ">=2.19.0 <4.0.0" + dart: ">=3.4.0 <4.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index c60adc4..b6d7f9c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: value_state_root environment: - sdk: '>=2.17.1 <3.0.0' + sdk: ^3.4.0 dev_dependencies: - melos: ^3.1.0 + melos: ^6.3.2 From 297801d902fb1823e4c3ee8e0a46f9844b1323ca Mon Sep 17 00:00:00 2001 From: zemanux Date: Fri, 7 Feb 2025 11:44:17 +0100 Subject: [PATCH 2/6] chore(release): publish packages - value_state@2.0.0-beta.0 --- CHANGELOG.md | 21 +++++++++++++++++++++ packages/value_state/CHANGELOG.md | 6 ++++++ packages/value_state/example/pubspec.yaml | 2 +- packages/value_state/pubspec.yaml | 2 +- 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc10ff2..1a1e91f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,27 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## 2025-02-07 + +### Changes + +--- + +Packages with breaking changes: + + - [`value_state` - `v2.0.0-beta.0`](#value_state---v200-beta0) + +Packages with other changes: + + - There are no other changes in this release. + +--- + +#### `value_state` - `v2.0.0-beta.0` + + - **BREAKING** **REFACTOR**: class ValueState is replaced by Value. + + ## 2023-06-01 ### Changes diff --git a/packages/value_state/CHANGELOG.md b/packages/value_state/CHANGELOG.md index 8981332..e182be0 100644 --- a/packages/value_state/CHANGELOG.md +++ b/packages/value_state/CHANGELOG.md @@ -1,3 +1,9 @@ +## 2.0.0-beta.0 + +> Note: This release has breaking changes. + + - **BREAKING** **REFACTOR**: class ValueState is replaced by Value. + ## 1.5.1 - **FIX**: close stream in perform. diff --git a/packages/value_state/example/pubspec.yaml b/packages/value_state/example/pubspec.yaml index b1ead85..96ad0bf 100644 --- a/packages/value_state/example/pubspec.yaml +++ b/packages/value_state/example/pubspec.yaml @@ -17,7 +17,7 @@ dependencies: flutter_riverpod: ^2.6.1 riverpod_annotation: ^2.6.1 - value_state: ^1.5.1 + value_state: ^2.0.0-beta.0 dev_dependencies: flutter_test: diff --git a/packages/value_state/pubspec.yaml b/packages/value_state/pubspec.yaml index 29b8878..0d7bfcd 100644 --- a/packages/value_state/pubspec.yaml +++ b/packages/value_state/pubspec.yaml @@ -2,7 +2,7 @@ name: value_state description: A dart package that helps implements basic states for BLoC library repository: https://github.com/devobs/value_state homepage: https://github.com/devobs -version: 1.5.1 +version: 2.0.0-beta.0 environment: sdk: ^3.4.0 From d6e911d02455345dd78ece1605a7f909310e4360 Mon Sep 17 00:00:00 2001 From: zemanux Date: Thu, 13 Feb 2025 17:40:13 +0100 Subject: [PATCH 3/6] docs: simplify README.md --- packages/value_state/README.md | 113 +++++++++++++-------------------- 1 file changed, 44 insertions(+), 69 deletions(-) diff --git a/packages/value_state/README.md b/packages/value_state/README.md index ef0cc5a..7b0d31d 100644 --- a/packages/value_state/README.md +++ b/packages/value_state/README.md @@ -1,73 +1,49 @@ -A dart package that helps to implement basic states initial, success and error. +## value_state +[![pub package](https://img.shields.io/pub/v/value_state.svg)](https://pub.dev/packages/value_state) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -[![pub package](https://img.shields.io/pub/v/value_state.svg)](https://pub.dev/packages/value_state) -[![Test](https://github.com/devobs/value_state/actions/workflows/test.yml/badge.svg)](https://github.com/devobs/value_state/actions/workflows/test.yml) -[![codecov](https://codecov.io/gh/devobs/value_state/branch/main/graph/badge.svg)](https://app.codecov.io/gh/devobs/value_state/tree/main/packages/value_state) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +[![Test](https://github.com/devobs/value_state/actions/workflows/test.yml/badge.svg)](https://github.com/devobs/value_state/actions/workflows/test.yml) [![codecov](https://codecov.io/gh/devobs/value_state/branch/main/graph/badge.svg)](https://app.codecov.io/gh/devobs/value_state/tree/main/packages/value_state) -## Features +A dart package that helps to implement basic states such as initial, success and error. + +### 🔥 Features This package helps you manage the different states your data can have in your app (like loading, success, or error). It makes your code cleaner and easier to understand, especially when dealing with things like network requests, storage loading or complex operations. It provides a way to represent a value that can be in one of three states: - * initial * success * failure -## Usage - -### Value - -Create a value: +### 🚀 Quick start ```dart -// The value is in the initial state. final valueInitial = Value.initial(); +final state = valueInitial.state; // ValueState.initial +final isInitial = valueInitial.isInitial; // true -// The value is in the success state with data. final valueSuccess = Value.success(1); - +final isSuccess = valueSuccess.isSuccess; // false +final isFailure = valueError.isFailure; // false print('Data of value : ${valueSuccess.data}'); // Data of value : 1 - -// Map a Value to `failure` with actual data if any. -// There is no `Value.failure` constructor to prevent developers from forgetting to retain the data from a previous state of the Value. -final valueError = value1.toFailure(Exception('error')); - -print('Data of value : ${valueError.data}'); // Data of value : null -print('Error of value : "${valueError.error}"'); // Error of value : "Exception: error" - -// The new value from call `toFailure` on `valueSuccess` keep previous `data`. It provides a simple way to display both error and previous data (for example a refresh failure). -final valueErrorWithPreviousData = valueSuccess.toFailure(Exception('error')); - -print('Data of value : ${valueErrorWithPreviousData.data}'); // Data of value : 1 -print('Error of value : "${valueErrorWithPreviousData.error}"'); // Error of value : "Exception: error" - ``` -Get the state of the value: +#### When the value is in a specific state ```dart -// Get the state of the value. -final state = valueInitial.state; // ValueState.initial - -// Check if the value is in the initial state. -final isInitial = valueInitial.isInitial; // true - -// Check if the value is in the success state. -final isSuccess = valueSuccess.isSuccess; // false - -// Check if the value is in the failure state. -final isFailure = valueError.isFailure; // false +value.when( + initial: () => print('initial'), + success: (data) => print('success: $data'), + failure: (error) => print('failure: $error'), + orElse: () => print('orElse'), +); ``` -Map the value to a different type: +#### Map the value to a different type ```dart -// Map the value to a different type. -final map = valueInitial.map( +valueInitial.map( initial: () => 'initial', success: (data) => 'success: $data', failure: (error) => 'failure: $error', @@ -75,49 +51,48 @@ final map = valueInitial.map( ); ``` -When the value is in a specific state: +#### Merge two values with different types ```dart -// When the value is in a specific state. -final when = value.when( - initial: () => print('initial'), - success: (data) => print('success: $data'), - failure: (error) => print('failure: $error'), - orElse: () => print('orElse'), -); +value1.merge(value2, mapData: (value) => value.length); ``` -Merge two values with different types: +#### Value error +Map a Value to `failure` with actual data if any. There is no `Value.failure` constructor to prevent developers from forgetting to retain the data from a previous state of the Value. ```dart -// Merge two values with different types. -final mergedValue = value1.merge(value2, mapData: (value) => value.length); -``` - -### Fetch +final valueError = Value.initial().toFailure(Exception('error')); -Generate a stream of Value during a processing Future: +print('Data of value : ${valueError.data}'); // Data of value : null +print('Error of value : "${valueError.error}"'); // Error of value : "Exception: error" +``` +The new value from call `toFailure` on `valueSuccess` keep previous `data`. It provides a simple way to display both error and previous data (for example a refresh failure). ```dart -// Generate a stream of Value during a processing Future. -final stream = Future.value(1).toValues(); +final valueErrorWithPreviousData = valueSuccess.toFailure(Exception('error')); + +print('Data of value : ${valueErrorWithPreviousData.data}'); // Data of value : 1 +print('Error of value : "${valueErrorWithPreviousData.error}"'); // Error of value : "Exception: error" ``` -Handle states (isFetching, success, error...) while an action is processed: +#### Handle states (isFetching, success, error...) while an action is processed ```dart -// Handle states (isFetching, success, error...) while an action is processed. -final fetch = value.fetch( - () async => 1, -); +const value = Value.initial(); +print(value); +value.fetchFrom(() async => "result").forEach(print); +// Result : +// Value(state: ValueState.initial, isFetching: false) +// Value(state: ValueState.initial, isFetching: true) +// Value(state: ValueState.success, isFetching: false, data: result) ``` -## License +### License MIT License See the [LICENSE](https://www.google.com/url?sa=E&source=gmail&q=https://www.google.com/url?sa=E%26source=gmail%26q=LICENSE) file for details. -## Feedback +### Feedback -Please file any issues, bugs or feature requests as an issue on the [Github page](https://github.com/devobs/value_state/issues). +Please file any issues, bugs or feature requests as an issue on the [Github page](https://github.com/devobs/value_state/issues). \ No newline at end of file From 659aed814c961c7b2a9cf8de36d06a94543b1eca Mon Sep 17 00:00:00 2001 From: zemanux Date: Thu, 13 Feb 2025 17:56:32 +0100 Subject: [PATCH 4/6] chore(release): publish packages - value_state@2.0.0-beta.1 --- CHANGELOG.md | 23 +++++++++++++++++++++++ packages/value_state/CHANGELOG.md | 6 ++++++ packages/value_state/example/pubspec.yaml | 2 +- packages/value_state/pubspec.yaml | 2 +- 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a1e91f..0fb182d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,29 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## 2025-02-13 + +### Changes + +--- + +Packages with breaking changes: + + - [`value_state` - `v2.0.0-beta.1`](#value_state---v200-beta1) + +Packages with other changes: + + - There are no other changes in this release. + +--- + +#### `value_state` - `v2.0.0-beta.1` + + - **DOCS**: simplify README.md. + - **BREAKING** **REFACTOR**: class ValueState is replaced by Value. + - **BREAKING** **REFACTOR**: class ValueState is replaced by Value. + + ## 2025-02-07 ### Changes diff --git a/packages/value_state/CHANGELOG.md b/packages/value_state/CHANGELOG.md index e182be0..3d7052d 100644 --- a/packages/value_state/CHANGELOG.md +++ b/packages/value_state/CHANGELOG.md @@ -1,3 +1,9 @@ +## 2.0.0-beta.1 + +> Note: This release has breaking changes. + + - **DOCS**: simplify README.md. + ## 2.0.0-beta.0 > Note: This release has breaking changes. diff --git a/packages/value_state/example/pubspec.yaml b/packages/value_state/example/pubspec.yaml index 96ad0bf..acddd9c 100644 --- a/packages/value_state/example/pubspec.yaml +++ b/packages/value_state/example/pubspec.yaml @@ -17,7 +17,7 @@ dependencies: flutter_riverpod: ^2.6.1 riverpod_annotation: ^2.6.1 - value_state: ^2.0.0-beta.0 + value_state: ^2.0.0-beta.1 dev_dependencies: flutter_test: diff --git a/packages/value_state/pubspec.yaml b/packages/value_state/pubspec.yaml index 0d7bfcd..313ad0e 100644 --- a/packages/value_state/pubspec.yaml +++ b/packages/value_state/pubspec.yaml @@ -2,7 +2,7 @@ name: value_state description: A dart package that helps implements basic states for BLoC library repository: https://github.com/devobs/value_state homepage: https://github.com/devobs -version: 2.0.0-beta.0 +version: 2.0.0-beta.1 environment: sdk: ^3.4.0 From f9759c1799908aa97a8f60fe24cf04bd5dffa723 Mon Sep 17 00:00:00 2001 From: zemanux Date: Wed, 5 Mar 2025 18:24:20 +0100 Subject: [PATCH 5/6] docs: added topics --- packages/value_state/pubspec.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/value_state/pubspec.yaml b/packages/value_state/pubspec.yaml index 313ad0e..7caf336 100644 --- a/packages/value_state/pubspec.yaml +++ b/packages/value_state/pubspec.yaml @@ -1,8 +1,9 @@ name: value_state -description: A dart package that helps implements basic states for BLoC library +description: Dart package for handling loading, error, and data states. +version: 2.0.0-beta.1 repository: https://github.com/devobs/value_state homepage: https://github.com/devobs -version: 2.0.0-beta.1 +topics: [error, fetch, init, state, success] environment: sdk: ^3.4.0 From 6be033b8876a30e338852d5266f720b4f0ec3ff1 Mon Sep 17 00:00:00 2001 From: zemanux Date: Wed, 5 Mar 2025 18:24:57 +0100 Subject: [PATCH 6/6] chore(release): publish packages - value_state@2.0.0 --- CHANGELOG.md | 23 +++++++++++++++++++++++ packages/value_state/CHANGELOG.md | 6 ++++++ packages/value_state/example/pubspec.yaml | 2 +- packages/value_state/pubspec.yaml | 2 +- 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0fb182d..d54df54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,29 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## 2025-03-05 + +### Changes + +--- + +Packages with breaking changes: + + - [`value_state` - `v2.0.0`](#value_state---v200) + +Packages with other changes: + + - There are no other changes in this release. + +Packages graduated to a stable release (see pre-releases prior to the stable version for changelog entries): + + - `value_state` - `v2.0.0` + +--- + +#### `value_state` - `v2.0.0` + + ## 2025-02-13 ### Changes diff --git a/packages/value_state/CHANGELOG.md b/packages/value_state/CHANGELOG.md index 3d7052d..a70fda1 100644 --- a/packages/value_state/CHANGELOG.md +++ b/packages/value_state/CHANGELOG.md @@ -1,3 +1,9 @@ +## 2.0.0 + + - Graduate package to a stable release. See pre-releases prior to this version for changelog entries. + - **DOCS**: added topics. + + ## 2.0.0-beta.1 > Note: This release has breaking changes. diff --git a/packages/value_state/example/pubspec.yaml b/packages/value_state/example/pubspec.yaml index acddd9c..da03265 100644 --- a/packages/value_state/example/pubspec.yaml +++ b/packages/value_state/example/pubspec.yaml @@ -17,7 +17,7 @@ dependencies: flutter_riverpod: ^2.6.1 riverpod_annotation: ^2.6.1 - value_state: ^2.0.0-beta.1 + value_state: ^2.0.0 dev_dependencies: flutter_test: diff --git a/packages/value_state/pubspec.yaml b/packages/value_state/pubspec.yaml index 7caf336..55034e0 100644 --- a/packages/value_state/pubspec.yaml +++ b/packages/value_state/pubspec.yaml @@ -1,6 +1,6 @@ name: value_state description: Dart package for handling loading, error, and data states. -version: 2.0.0-beta.1 +version: 2.0.0 repository: https://github.com/devobs/value_state homepage: https://github.com/devobs topics: [error, fetch, init, state, success]