Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ReplayBloc function onTransition() handles event _Undo instead of user defined event #4028

Open
simphotonics opened this issue Dec 15, 2023 · 2 comments
Assignees
Labels
bug Something isn't working question Further information is requested waiting for response Waiting for follow up

Comments

@simphotonics
Copy link

Description
While trying out the class ReplayBloc I encountered a problem when using the method onTransition. Instead of handling the user defined event Rewind, an internal event _Undo is handled. (The handler of Rewind calls undo(), see function _rewind() below.)

Steps To Reproduce

import 'package:replay_bloc/replay_bloc.dart';
import 'package:equatable/equatable.dart';

/// Bloc state
final class CounterState extends Equatable {
  const CounterState(this.i);
  final int i;

  @override
  List<Object> get props => [i];
}

final class CounterInitial extends CounterState {
  const CounterInitial() : super(0);
}

/// Bloc events
sealed class CounterEvent extends ReplayEvent {}

final class Increment extends CounterEvent {}

final class Decrement extends CounterEvent {}

final class Rewind extends CounterEvent{}



/// Bloc logic
class CounterBloc extends ReplayBloc<CounterEvent, CounterState> {
  CounterBloc() : super(CounterInitial()) {
    on<CounterEvent>((event, emit) => switch (event) {
          Increment() => emit(CounterState(state.i + 1)),
          Decrement() => emit(CounterState(state.i - 1)),
          Rewind() => emit(_rewind()),
        });
  }

  CounterState _rewind() {
    undo();
    return state;
  }

  @override
  void onTransition(
      covariant Transition<ReplayEvent, CounterState> transition) {
    super.onTransition(transition);
    print(transition.event.runtimeType);
    
    if (transition.event is Rewind) {
      print('  Doing something during event Rewind. '); // <-------- Never reached.
    }
  }
}

/// Executable
void main(List<String> args) {
  final bloc = CounterBloc();

  bloc.add(Increment());
  bloc.add(Decrement());
  bloc.add(Rewind());
}

Expected Behavior

$ dart main.dart
Increment
Decrement
Rewind
  Doing something during event Rewind.

Actual Behavior

$ dart main.dart
Increment
Decrement
Undo

Additional Context
The fact that _Undo is private makes it more difficult to filter events in onTransition.

@simphotonics simphotonics added the bug Something isn't working label Dec 15, 2023
@felangel
Copy link
Owner

Hi @simphotonics 👋
Thanks for opening an issue!

Can you elaborate on why you’re creating a separate rewind event when you can just call undo on the bloc directly from the widget tree?

@felangel felangel added question Further information is requested waiting for response Waiting for follow up labels Dec 15, 2023
@felangel felangel self-assigned this Dec 15, 2023
@simphotonics
Copy link
Author

simphotonics commented Dec 15, 2023

Hi @felangel. Thanks for taking the time to have a look.

In the concrete example I am working on the events are AwardPointTeam1, AwardPointTeam2, and ReplayLastPoint:

part of 'score_bloc.dart';

@immutable
sealed class ScoreEvent extends ReplayEvent {
  const ScoreEvent();
  @override
  String toString() => '$runtimeType';
}

@immutable
sealed class AwardPoint extends ScoreEvent {
  const AwardPoint();
}

@immutable
class AwardPointTeam1 extends AwardPoint {
  const AwardPointTeam1();
}

@immutable
class AwardPointTeam2 extends ScoreEvent {
  const AwardPointTeam2();
}

@immutable
class ReplayLastPoint extends ScoreEvent {
  const ReplayLastPoint();
}

and the bloc state represents a tennis score:

part of 'score_bloc.dart';

@immutable
final class ScoreState extends Equatable {
  const ScoreState({
    required this.server,
    this.points = const (0, 0),
    this.pointsWon = const (0, 0),
    this.games = const [(0, 0)],
    this.sets = const (0, 0),
    this.winner,
  });

  final (int, int) points;
  final (int, int) pointsWon;
  final List<(int, int)> games;
  final (int, int) sets;
  final TeamSelector server;
  final TeamSelector? winner;

  bool get hasWinner => winner == null ? false : true;

  @override
  List<Object?> get props => [points, pointsWon, games, sets, winner];
}

During a ReplayLastPoint event some logic is required to change the scoring method if necessary.

For example at games: 6:0, 6:7, 0:0, and points 0:0 the scoring method is that of a standard tennis game in the third set.
After the event ReplayLastPoint at score: 6:0, 6:6, and tiebreak points e.g. 8:9, the scoring method has to be changed to that of a set tiebreaker. I placed this logic into onTransition since it has access to the current and the next state.

@override
  void onTransition(covariant Transition<ReplayEvent, ScoreState> transition) {
    super.onTransition(transition);

    if (transition.event is AwardPoint) {
      return;
    }

    // Set the correct set configuration after an undo.
    if (transition.nextState.sets != decidingSetAtScore &&
        transition.currentState.sets == decidingSetAtScore) {
      _setConfig = matchConfig.setConfig.standard;
    }
    // Set the correct game configuration after an undo.
    _gameConfig =
        (transition.nextState.games.last == _setConfig.tiebreakAtScore)
            ? _setConfig.gameConfig.tiebreak
            : _setConfig.gameConfig.standard;
  }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working question Further information is requested waiting for response Waiting for follow up
Projects
None yet
Development

No branches or pull requests

2 participants