Skip to content

Commit

Permalink
isWaiting fix when checking multiple actions.
Browse files Browse the repository at this point in the history
  • Loading branch information
marcglasberg committed Sep 7, 2024
1 parent e2a1e58 commit a676184
Show file tree
Hide file tree
Showing 2 changed files with 179 additions and 8 deletions.
169 changes: 169 additions & 0 deletions example/lib/main_is_waiting_works_when_multiple_actions.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import 'package:async_redux/async_redux.dart';
import 'package:flutter/material.dart';

void main() {
runApp(const MyApp());
}

class MyApp extends StatelessWidget {
const MyApp({super.key});

@override
Widget build(BuildContext context) {
var store = Store<AppState>(initialState: AppState(counter: 0));
store.onChange.listen(print);

return MaterialApp(
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: StoreProvider(
store: store,
child: const MyHomePage(),
),
);
}
}

class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});

@override
Widget build(BuildContext context) {
return StoreConnector<AppState, CounterVm>(
vm: () => CounterVmFactory(),
shouldUpdateModel: (s) => s.counter >= 0,
builder: (context, vm) {
return MyHomePageContent(
title: 'IsWaiting works when multiple actions',
counter: vm.counter,
isCalculating: vm.isCalculating,
increment: vm.increment,
multiply: vm.multiply,
);
},
);
}
}

class MyHomePageContent extends StatelessWidget {
const MyHomePageContent({
super.key,
required this.title,
required this.counter,
required this.isCalculating,
required this.increment,
required this.multiply,
});

final String title;
final int counter;
final bool isCalculating;
final VoidCallback increment, multiply;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('Result:'),
Text(
'$counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: Column(
mainAxisSize: MainAxisSize.min,
children: [
FloatingActionButton(
onPressed: isCalculating ? null : increment,
elevation: isCalculating ? 0 : 6,
backgroundColor: isCalculating ? Colors.grey[300] : Colors.blue,
child: isCalculating
? const Padding(
padding: const EdgeInsets.all(16.0),
child: const CircularProgressIndicator(),
)
: const Icon(Icons.add),
),
const SizedBox(height: 16),
FloatingActionButton(
onPressed: isCalculating ? null : multiply,
elevation: isCalculating ? 0 : 6,
backgroundColor: isCalculating ? Colors.grey[300] : Colors.blue,
child: isCalculating
? const Padding(
padding: const EdgeInsets.all(16.0),
child: const CircularProgressIndicator(),
)
: const Icon(Icons.close),
)
],
),
);
}
}

class AppState {
final int counter;

AppState({required this.counter});

AppState copy({int? counter}) => AppState(counter: counter ?? this.counter);

@override
String toString() {
return '.\n.\n.\nAppState{counter: $counter}\n.\n.\n';
}
}

class CounterVm extends Vm {
final int counter;
final bool isCalculating;
final VoidCallback increment, multiply;

CounterVm({
required this.counter,
required this.isCalculating,
required this.increment,
required this.multiply,
}) : super(equals: [
counter,
isCalculating,
]);
}

class CounterVmFactory extends VmFactory<AppState, MyHomePage, CounterVm> {
@override
CounterVm fromStore() => CounterVm(
counter: state.counter,
isCalculating: isWaiting([IncrementAction, MultiplyAction]),
increment: () => dispatch(IncrementAction()),
multiply: () => dispatch(MultiplyAction()),
);
}

class IncrementAction extends ReduxAction<AppState> {
@override
Future<AppState?> reduce() async {
await Future.delayed(const Duration(seconds: 1));
return AppState(counter: state.counter + 1);
}
}

class MultiplyAction extends ReduxAction<AppState> {
@override
Future<AppState?> reduce() async {
await Future.delayed(const Duration(seconds: 1));
return AppState(counter: state.counter * 2);
}
}
18 changes: 10 additions & 8 deletions lib/src/store.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1581,44 +1581,46 @@ class Store<St> {
// 3) If an iterable was passed:
// 3.1) For each action or action type in the iterable...
else if (actionOrActionTypeOrList is Iterable) {
bool isWaiting = false;
for (var actionOrType in actionOrActionTypeOrList) {
//
// 3.2) If it's a type.
if (actionOrType is Type) {
_awaitableActions.add(actionOrType);

// 3.2.1) Return true if any of the actions in progress has that exact type.
return _actionsInProgress.any((action) => action.runtimeType == actionOrType);
// 3.2.1) Is waiting if any of the actions in progress has that exact type.
if (!isWaiting)
isWaiting = _actionsInProgress.any((action) => action.runtimeType == actionOrType);
}
//
// 3.3) If it's an action.
else if (actionOrType is ReduxAction) {
_awaitableActions.add(actionOrType.runtimeType);

// 3.3.1) Return true if any of the actions in progress is the exact action.
return _actionsInProgress.contains(actionOrType);
// 3.3.1) Is waiting if any of the actions in progress is the exact action.
if (!isWaiting) isWaiting = _actionsInProgress.contains(actionOrType);
}
//
// 3.4) If it's not an action and not an action type, throw an exception.
// The exception is thrown after the async gap, so that it doesn't interrupt the processes.
else {
Future.microtask(() {
throw StoreException(
"You can't do isWaiting([${actionOrActionTypeOrList.runtimeType}]), "
"but only an action Type, a ReduxAction, or a List of them.");
"You can't do isWaiting([${actionOrActionTypeOrList.runtimeType}]). "
"Use only actions, action types, or a list of them.");
});
}
}

// 3.5) If the `for` finished without matching any items, return false (it's NOT waiting).
return false;
return isWaiting;
}
// 4) If something different was passed, it's an error. We show the error after the
// async gap, so we don't interrupt the code. But we return false (not waiting).
else {
Future.microtask(() {
throw StoreException("You can't do isWaiting(${actionOrActionTypeOrList.runtimeType}), "
"but only an action Type, a ReduxAction, or a List of them.");
"Use only actions, action types, or a list of them.");
});

return false;
Expand Down

0 comments on commit a676184

Please sign in to comment.