예제 #1
0
        public static IState Reduce(IState state, ReduxAction action)
        {
            if (state == null)
            {
                return(new ToDoList());
            }

            var    prevList = state as ToDoList;
            string todo     = null;

            if (action.ActionType == ADD_TODO || action.ActionType == TOGGLE_TODO)
            {
                todo = (action as ReduxAction <string>).Payload;
            }

            switch (action.ActionType)
            {
            case ADD_TODO:
                return(prevList.Add(todo, false));

            case TOGGLE_TODO:
                return(prevList.Toggle(todo));

            default:
                return(prevList);
            }
        }
        public async Task <AppState> ReduceAsync(AppState state, ReduxAction action, CancellationToken ct)
        {
            var counter = await counterReducer.ReduceAsync(state.Counter, action, ct);

            var fetchData = await fetchDataReducer.ReduceAsync(state.FetchData, action, ct);

            return(state.Clone(counter, fetchData));
        }
예제 #3
0
        /// <summary>
        /// Processes single action.
        /// </summary>
        /// <param name="action">An action to be processed.</param>
        /// <param name="ct">A cancellation token that can be used to cancel the work</param>
        /// <returns>A task that represents action process.</returns>
        internal async Task ProcessActionAsync(ReduxAction action, CancellationToken ct)
        {
            var oldState = state;

            state = await reducer.ReduceAsync(state, action, CancellationToken.None);

            if (!ct.IsCancellationRequested && !ReferenceEquals(oldState, state))
            {
                await notificationFactory.StartNew(st => OnStateChangedAsync(new StateChangedEventArgs <TState>(action, (TState)st)), state).Unwrap();
            }
        }
예제 #4
0
        public Task <CounterState> ReduceAsync(CounterState state, ReduxAction action, CancellationToken ct)
        {
            switch (action)
            {
            case IncrementCounterAction incrementCounter:
                return(Task.FromResult(state.Clone(value: state.Value + 1)));

            default:
                return(Task.FromResult(state));
            }
        }
예제 #5
0
        public async Task <RootState> ReduceAsync(RootState state, ReduxAction action, CancellationToken ct)
        {
            var navigationReduceTask     = Task.Run(() => NavigationReducer.Reduce(state.Navigation, action));
            var newFirstPageReduceTask   = Task.Run(() => FirstPageReducer.Reduce(state.FirstPage, action));
            var dictionaryPageReduceTask = Task.Run(() => DictionaryPageReducer.Reduce(state.DictionaryPage, action));

            return(state.Clone(
                       navigation: await navigationReduceTask,
                       firstPage: await newFirstPageReduceTask,
                       dictionaryPage: await dictionaryPageReduceTask
                       ));
        }
예제 #6
0
 public Step(int key, ReduxAction action, object state, ObjectData actionData, ObjectTreeItem actionTreeItem, ObjectData stateData, ObjectTreeItem stateTreeItem,
             DifferenceItem differenceItem, bool differenceCalculated)
 {
     Key                  = key;
     Action               = action;
     State                = state;
     ActionData           = actionData;
     ActionTreeItem       = actionTreeItem;
     StateData            = stateData;
     StateTreeItem        = stateTreeItem;
     DifferenceItem       = differenceItem;
     DifferenceCalculated = differenceCalculated;
 }
예제 #7
0
        public static DictionaryPageState Reduce(DictionaryPageState state, ReduxAction action)
        {
            switch (action)
            {
            case AddValueAction addValue:
                return(state.Clone(dictionary: state.Dictionary.Add(addValue.Key, addValue.Value)));

            case RemoveValueAction removeValue:
                return(state.Clone(dictionary: state.Dictionary.Remove(removeValue.Key)));

            default:
                return(state);
            }
        }
예제 #8
0
        public static FirstPageState Reduce(FirstPageState state, ReduxAction action)
        {
            switch (action)
            {
            case InputChangedAction inputChanged:
                return(state.Clone(input: inputChanged.Value));

            case ClickMeAction clickMe:
                return(state.Clone(output: $"Yolo: {state.Input}"));

            default:
                return(state);
            }
        }
예제 #9
0
        public static int Reduce(int state, ReduxAction action)
        {
            switch (action.ActionType)
            {
            case INCREMENT:
                return(state + 1);

            case DECREMENT:
                return(state - 1);

            case CHANGE_BY:
                return(state + (action as ReduxAction <int>).Payload);

            default:
                return(state);
            }
        }
예제 #10
0
        public async Task Emit <T>(ReduxAction <T> action) where T : IViewModel
        {
            var jsonString = JsonConvert.SerializeObject(action, JsonSettings.CamelCaseSerializer);
            var now        = DateTime.Now;

            try
            {
                await _hubContext.Clients.All.SendAsync("SendAction", jsonString);

                _telemeteryClient.TrackDependency("SignalR", "Websocket 'push-all'", $"ReduxEmit - {action.Type}", now, DateTime.Now - now, true);
            }
            catch
            {
                _telemeteryClient.TrackDependency("SignalR", "Websocket 'push-all'", $"ReduxEmit - {action.Type}", now, DateTime.Now - now, false);
                throw;
            }
        }
예제 #11
0
        public async Task Emit <T>(ReduxAction <T> action) where T : IViewModel
        {
            var jsonString = JsonConvert.SerializeObject(action, JsonSettings.CamelCaseSerializer);
            var now        = DateTime.Now;

            try
            {
                await HttpClient.PostAsync(_settings.Value.ConnectionString, new StringContent(jsonString, Encoding.UTF8, "application/json"));

                _telemeteryClient.TrackDependency("Websocket NodeJS API", $"ReduxEmit - {action.Type}", now, DateTime.Now - now, true);
            }
            catch
            {
                _telemeteryClient.TrackDependency("Websocket NodeJS API", $"ReduxEmit - {action.Type}", now, DateTime.Now - now, false);
                throw;
            }
        }
예제 #12
0
        public Task <RootState> ReduceAsync(RootState state, ReduxAction action, CancellationToken ct)
        {
            switch (action)
            {
            case IncrementAction _:
                // increment counter and create new state
                return(Task.FromResult(new RootState(state.Counter + 1)));

            case DecrementAction _:
                // decrement counter and create new state
                return(Task.FromResult(new RootState(state.Counter - 1)));

            default:
                // reducer doesn't process this state, simply return state
                // without modifications
                return(Task.FromResult(state));
            }
        }
예제 #13
0
        public Task <RootState> ReduceAsync(RootState state, ReduxAction action, CancellationToken ct)
        {
            RootState result;

            switch (action)
            {
            case IncreaseFirstPageNumberAction increaseFirstPageNumber:
                result = state.Clone(firstPage: state.FirstPage.Clone(state.FirstPage.Number + increaseFirstPageNumber.Diff));
                break;

            case ChangeSecondPageEntryTextAction changeSecondPageEntryText:
                result = state.Clone(secondPage: state.SecondPage.Clone(text: changeSecondPageEntryText.Text));
                break;

            default:
                return(Task.FromResult(state));
            }
            return(Task.FromResult(result));
        }
예제 #14
0
        public static string GetActionName(ReduxAction action)
        {
            if (action is SpecialReduxAction)
            {
                var fullName = action.GetType().Name;
                return(fullName.Substring(0, fullName.Length - "Action".Length));
            }
            const string Suffix = "Action";
            var          name   = action.GetType().FullName;

            foreach (string prefix in ReduxVisualizer.IgnoredNamespacePrefixes)
            {
                if (name.StartsWith(prefix, StringComparison.Ordinal))
                {
                    name = name.Substring(prefix.Length + 1);
                }
            }
            if (name.EndsWith(Suffix))
            {
                name = name.Substring(0, name.Length - Suffix.Length);
            }
            return(name);
        }
예제 #15
0
        public Task <FetchDataState> ReduceAsync(FetchDataState state, ReduxAction action, CancellationToken ct)
        {
            FetchDataState result;

            switch (action)
            {
            case WeatherForecastFetchStartAction start:
                result = state.Clone(forecasts: new WeatherForecast[0], isLoading: true, error: null);
                break;

            case WeatherForecastFetchFailureAction failure:
                result = state.Clone(isLoading: false, error: failure.Error);
                break;

            case WeatherForecastFetchSuccessAction success:
                result = state.Clone(forecasts: success.Items, isLoading: false);
                break;

            default:
                result = state;
                break;
            }
            return(Task.FromResult(result));
        }
예제 #16
0
 public static NavigationState Reduce(NavigationState state, ReduxAction action)
 {
     return(state);
 }
예제 #17
0
 public void Dispatch(ReduxAction action)
 {
     WrapperStore.Dispatch(action);
 }
예제 #18
0
        public Task <RootState> ReduceAsync(RootState state, ReduxAction action, CancellationToken ct)
        {
            RootState result;

            switch (action)
            {
            case SetFilterAction setFilterAction:
                result = state.Clone(filter: setFilterAction.Filter, filteredItems: GetFilteredItems(state.Items, setFilterAction.Filter));
                break;

            case NewItemTextChangedAction newItemTextAction:
                result = state.Clone(newItemText: newItemTextAction.Text);
                break;

            case AddItemAction addItemAction:
            {
                var newItem = new TodoItem(addItemAction.Key, isChecked: false, text: addItemAction.Text, isEditing: false, editText: null);
                var items   = state.Items.Add(newItem);
                result = state.Clone(newItemText: "", items: items, filteredItems: GetFilteredItems(items, state.Filter));
            }
            break;

            case ChangeIsCheckedItemAction changeIsCheckedItemAction:
            {
                var item        = state.Items.Single(i => i.Key == changeIsCheckedItemAction.Key);
                var changedItem = item.Clone(isChecked: changeIsCheckedItemAction.IsChecked);
                var items       = state.Items.Replace(item, changedItem);
                result = state.Clone(items: items, filteredItems: GetFilteredItems(items, state.Filter));
            }
            break;

            case RemoveCompletedAction _:
            {
                var items = state.Items.Where(i => !i.IsChecked).ToImmutableList();
                result = state.Clone(items: items, filteredItems: GetFilteredItems(items, state.Filter));
            }
            break;

            case ToggleAllIsCheckedAction toggleAllIsCheckedAction:
            {
                var candidates = state.FilteredItems.Where(i => i.IsChecked != toggleAllIsCheckedAction.IsChecked).ToArray();
                if (candidates.Length > 0)
                {
                    var items = state.Items;
                    foreach (var item in candidates)
                    {
                        items = items.Replace(item, item.Clone(isChecked: toggleAllIsCheckedAction.IsChecked));
                    }
                    result = state.Clone(allChecked: toggleAllIsCheckedAction.IsChecked, items: items, filteredItems: GetFilteredItems(items, state.Filter));
                }
                else
                {
                    result = state.Clone(allChecked: toggleAllIsCheckedAction.IsChecked);
                }
            }
            break;

            case StartEditItemAction startEditItemAction:
            {
                var item        = state.Items.Single(i => i.Key == startEditItemAction.Key);
                var changedItem = item.Clone(isEditing: true, editText: item.Text);
                var items       = state.Items.Replace(item, changedItem);
                result = state.Clone(items: items, filteredItems: GetFilteredItems(items, state.Filter));
            }
            break;

            case EditItemChangedTextAction editItemChangedTextAction:
            {
                var item        = state.Items.Single(i => i.IsEditing);
                var changedItem = item.Clone(editText: editItemChangedTextAction.Text);
                var items       = state.Items.Replace(item, changedItem);
                result = state.Clone(items: items, filteredItems: GetFilteredItems(items, state.Filter));
            }
            break;

            case EndEditItemAction _:
            {
                var item        = state.Items.Single(i => i.IsEditing);
                var changedItem = item.Clone(isEditing: false, editText: null, text: item.EditText);
                var items       = state.Items.Replace(item, changedItem);
                result = state.Clone(items: items, filteredItems: GetFilteredItems(items, state.Filter));
            }
            break;

            case CancelEditItemAction _:
            {
                var item        = state.Items.Single(i => i.IsEditing);
                var changedItem = item.Clone(isEditing: false, editText: null);
                var items       = state.Items.Replace(item, changedItem);
                result = state.Clone(items: items, filteredItems: GetFilteredItems(items, state.Filter));
            }
            break;

            default:
                result = state;
                break;
            }
            return(Task.FromResult(result));
        }
예제 #19
0
 /// <summary>
 /// Dispatches given action.
 /// </summary>
 /// <param name="action">An <see cref="ReduxAction"/> to be dispatched.</param>
 /// <remarks>All actions have to be dispatched through this method.</remarks>
 public void Dispatch(ReduxAction action)
 {
     queue.Post(action);
 }
예제 #20
0
        public Task <RootState> ReduceAsync(RootState state, ReduxAction action, CancellationToken ct)
        {
            RootState result;

            switch (action)
            {
            case ServersChangedAction serversChangedAction:
            {
                int index   = 0;
                var servers = serversChangedAction.ServersText.Split(new[] { Environment.NewLine }, StringSplitOptions.None)
                              .Select(url => new Server(index++, url.Trim('\n', '\r'), isAlive: false, state: ServerUpdateState.Idle, error: null))
                              .ToArray();
                result = state.Clone(servers: servers);
            }
            break;

            case UsernameChangedAction usernameChangedAction:
                result = state.Clone(username: usernameChangedAction.Username);
                break;

            case PasswordChangedAction passwordChangedAction:
                result = state.Clone(password: passwordChangedAction.Password);
                break;

            case PortChangedAction portChangedAction:
                result = state.Clone(port: portChangedAction.Port);
                break;

            case StartUpdateAction startUpdateAction:
            {
                string operationInProgress;
                switch (startUpdateAction.Mode)
                {
                case UpdateMode.Connection:
                    operationInProgress = "Testing connection";
                    break;

                case UpdateMode.Firmware:
                    operationInProgress = "Upgrading firmware";
                    break;

                case UpdateMode.Packages:
                    operationInProgress = "Upgrading packages";
                    break;

                default:
                    operationInProgress = "???";
                    break;
                }
                var servers = state.Servers.Select(s => s.Clone(state: ServerUpdateState.Idle, error: "")).ToArray();
                result = state.Clone(servers: servers, isUpdating: true, operationInProgress: operationInProgress);
            }
            break;

            case StopUpdateAction _:
                result = state.Clone(isUpdating: false, operationInProgress: "");
                break;

            case StartUpdatingServerAction startUpdatingServerAction:
            {
                var server  = state.Servers.Single(i => i.Key == startUpdatingServerAction.Key);
                var changed = server.Clone(state: ServerUpdateState.Updating, error: null);
                var servers = state.Servers.Replace(server, changed);
                result = state.Clone(servers: servers);
            }
            break;

            case ServerUpdateSuccessAction serverUpdateSuccessAction:
            {
                var server  = state.Servers.Single(i => i.Key == serverUpdateSuccessAction.Key);
                var changed = server.Clone(state: ServerUpdateState.Success, error: "");
                var servers = state.Servers.Replace(server, changed);
                result = state.Clone(servers: servers);
            }
            break;

            case ServerUpdateFailureAction serverUpdateFailureAction:
            {
                var server  = state.Servers.Single(i => i.Key == serverUpdateFailureAction.Key);
                var changed = server.Clone(state: ServerUpdateState.Failure, error: serverUpdateFailureAction.Error);
                var servers = state.Servers.Replace(server, changed);
                result = state.Clone(servers: servers);
            }
            break;

            case ToggleShowPasswordAction _:
                result = state.Clone(showPassword: !state.ShowPassword);
                break;

            default:
                return(Task.FromResult(state));
            }
            return(Task.FromResult(result));
        }
예제 #21
0
 public InsertNewAction(ReduxAction action, object state)
 {
     Action = action;
     State  = state;
 }
예제 #22
0
 private static void SendAction(ReduxAction action)
 {
     Console.WriteLine("Dispatching action: " + action);
     todoListStore.Dispatch(action);
 }
예제 #23
0
 public RepliedAction(ReduxAction action, object state)
 {
     Action = action;
     State  = state;
 }
        public Task <RootState> ReduceAsync(RootState state, ReduxAction action, CancellationToken ct)
        {
            RootState newState;

            switch (action)
            {
            case ChangeFoulsAction changeFoulsAction:
                if (changeFoulsAction.TeamType == TeamType.Home)
                {
                    newState = state.Clone(homeFouls: Math.Max(0, state.HomeFouls + changeFoulsAction.Difference));
                }
                else
                {
                    newState = state.Clone(awayFouls: Math.Max(0, state.AwayFouls + changeFoulsAction.Difference));
                }
                break;

            case ChangePeriodAction changePeriodAction:
                if (changePeriodAction.Difference == -1 && state.IsEndGame)
                {
                    newState = state.Clone(isEndGame: false);
                }
                else
                {
                    int        period     = state.Period;
                    PeriodType periodType = state.PeriodType;

                    if (changePeriodAction.Difference == +1)
                    {
                        switch (state.PeriodType)
                        {
                        case PeriodType.BeforeGame:
                            period     = 1;
                            periodType = PeriodType.Quarter;
                            break;

                        case PeriodType.Quarter:
                            if (state.Period == 4)
                            {
                                periodType = PeriodType.Overtime;
                                period     = 1;
                            }
                            else if (state.Period == 2)
                            {
                                periodType = PeriodType.HalfTime;
                            }
                            else
                            {
                                //periodType = PeriodType.QuarterBreak;
                                period++;
                            }
                            break;

                        case PeriodType.QuarterBreak:
                            period++;
                            periodType = PeriodType.Quarter;
                            break;

                        case PeriodType.HalfTime:
                            period++;
                            periodType = PeriodType.Quarter;
                            break;

                        case PeriodType.EndRegularGame:
                            period     = 1;
                            periodType = PeriodType.Overtime;
                            break;

                        case PeriodType.Overtime:
                            //periodType = PeriodType.OvertimeBreak;
                            period++;
                            break;

                        case PeriodType.OvertimeBreak:
                            period++;
                            periodType = PeriodType.Overtime;
                            break;
                        }
                    }
                    else
                    {
                        switch (state.PeriodType)
                        {
                        case PeriodType.Quarter:
                            if (state.Period == 3)
                            {
                                period--;
                                periodType = PeriodType.HalfTime;
                            }
                            else if (state.Period == 1)
                            {
                                periodType = PeriodType.BeforeGame;
                            }
                            else
                            {
                                period--;
                                //periodType = PeriodType.QuarterBreak;
                            }
                            break;

                        case PeriodType.QuarterBreak:
                            periodType = PeriodType.Quarter;
                            break;

                        case PeriodType.HalfTime:
                            period     = 2;
                            periodType = PeriodType.Quarter;
                            break;

                        case PeriodType.EndRegularGame:
                            period     = 4;
                            periodType = PeriodType.Quarter;
                            break;

                        case PeriodType.Overtime:
                            if (period == 1)
                            {
                                periodType = PeriodType.Quarter;
                                period     = 4;
                            }
                            else
                            {
                                period--;
                                //periodType = PeriodType.OvertimeBreak;
                            }
                            break;

                        case PeriodType.OvertimeBreak:
                            periodType = PeriodType.Overtime;
                            break;
                        }
                    }
                    newState = state.Clone(period: period, periodType: periodType);
                }
                break;

            case ChangeScoreAction changeScoreAction:
                if (changeScoreAction.TeamType == TeamType.Home)
                {
                    newState = state.Clone(homeScore: Math.Max(0, state.HomeScore + changeScoreAction.Difference));
                }
                else
                {
                    newState = state.Clone(awayScore: Math.Max(0, state.AwayScore + changeScoreAction.Difference));
                }
                break;

            case SetScoreAction setScoreAction:
                if (setScoreAction.TeamType == TeamType.Home)
                {
                    newState = state.Clone(homeScore: Math.Max(0, setScoreAction.Score));
                }
                else
                {
                    newState = state.Clone(awayScore: Math.Max(0, setScoreAction.Score));
                }
                break;

            case SetTeamColorAction setTeamColorAction:
                if (setTeamColorAction.TeamType == TeamType.Home)
                {
                    newState = state.Clone(homeColor: setTeamColorAction.Color);
                }
                else
                {
                    newState = state.Clone(awayColor: setTeamColorAction.Color);
                }
                break;

            case SetTeamLogoAction setTeamLogoAction:
                if (setTeamLogoAction.TeamType == TeamType.Home)
                {
                    newState = state.Clone(homeLogo: setTeamLogoAction.Logo);
                }
                else
                {
                    newState = state.Clone(awayLogo: setTeamLogoAction.Logo);
                }
                break;

            case StartTeamEditAction _:
                newState = state.Clone(isTeamEdit: true);
                break;

            case EndTeamEditAction _:
                newState = state.Clone(isTeamEdit: false);
                break;

            case ResetAction _:
                newState = new RootState("Domači", "Gostujoči", "Domači", "Gostujoči", 0, 0, 0, 0, 0, PeriodType.BeforeGame, isTeamEdit: true, isEndGame: false,
                                         homeColor: 0xF00F, awayColor: 0xFF00, homeLogo: null, awayLogo: null, configuration:  state.Configuration);
                break;

            case SetTeamNameAction setTeamNameAction:
                if (setTeamNameAction.TeamType == TeamType.Home)
                {
                    newState = state.Clone(home: setTeamNameAction.Name, shortHome: setTeamNameAction.ShortName);
                }
                else
                {
                    newState = state.Clone(away: setTeamNameAction.Name, shortAway: setTeamNameAction.ShortName);
                }
                break;

            case LoadStateAction loadStateAction:
                newState = loadStateAction.State;
                break;

            case EndGameAction _:
                newState = state.Clone(isEndGame: true);
                break;

            case ToggleTeamEditAction _:
                newState = state.Clone(isTeamEdit: !state.IsTeamEdit);
                break;

            case LoadConfigurationAction loadConfigurationAction:
                newState = state.Clone(configuration: loadConfigurationAction.Configuration);
                break;

            default:
                newState = state;
                break;
            }
            return(Task.FromResult(newState));
        }
 /// <summary>
 /// Initializes a new instance of <see cref="StateChangedEventArgs"/> containing action that triggered change.
 /// </summary>
 /// <param name="action">Action that triggered this state change.</param>
 /// /// <param name="state">The state after the action.</param>
 public StateChangedEventArgs(ReduxAction action, object state)
 {
     Action = action;
     State  = state;
 }
예제 #26
0
        public async Task <RootState> ReduceAsync(RootState state, ReduxAction action, CancellationToken ct)
        {
            RootState result;

            switch (action)
            {
            case InsertNewAction insertNew:
                int key            = state.Steps.Length;
                var actionDataTask = Task.Run(() => PropertiesCollector.Collect(insertNew.Action), ct);
                var stateDataTask  = Task.Run(() => PropertiesCollector.Collect(insertNew.State), ct);
                await actionDataTask.ConfigureAwait(false);

                var actionTreeItem = await Task.Run(() => {
                    string actionName = StateFormatter.GetActionName(insertNew.Action);
                    return(StateFormatter.ToTreeHierarchy(actionDataTask.Result, actionName));
                }).ConfigureAwait(false);

                await stateDataTask.ConfigureAwait(false);

                result = state.Clone(steps: state.Steps.Spread(
                                         new Step(key, insertNew.Action, insertNew.State, actionData: actionDataTask.Result,
                                                  actionTreeItem: actionTreeItem,
                                                  stateData: stateDataTask.Result, stateTreeItem: null, differenceItem: null, differenceCalculated: false)));
                break;

            case GenerateTreeHierarchyAction generateTreeHierarchy:
            {
                Step selectedStep = state.SelectedStep;
                result = state;
                if (selectedStep != null)
                {
                    if (selectedStep.StateTreeItem is null)
                    {
                        var  hierarchy = StateFormatter.ToTreeHierarchy(selectedStep.StateData);
                        Step updated   = selectedStep.Clone(stateTreeItem: hierarchy);
                        result       = result.Clone(steps: state.Steps.Replace(selectedStep, updated), selectedStep: updated);
                        selectedStep = updated;
                    }
                    if (!selectedStep.DifferenceCalculated)
                    {
                        // first check if previous step has StateTree
                        var            selectedStepIndex = Array.IndexOf(result.Steps, selectedStep);
                        var            previousStep      = selectedStepIndex > 0 ? result.Steps[selectedStepIndex - 1] : null;
                        ObjectTreeItem previousHierarchy = null;
                        if (previousStep != null)
                        {
                            if (previousStep.StateTreeItem == null)
                            {
                                previousHierarchy = StateFormatter.ToTreeHierarchy(previousStep.StateData);
                                Step previousUpdated = previousStep.Clone(stateTreeItem: previousHierarchy);
                                result = result.Clone(steps: result.Steps.Replace(previousStep, previousUpdated));
                            }
                            else
                            {
                                previousHierarchy = previousStep.StateTreeItem;
                            }
                        }
                        var  difference = TreeComparer.CreateDifferenceTree(previousHierarchy, selectedStep.StateTreeItem);
                        Step updated    = selectedStep.Clone(differenceItem: difference, differenceCalculated: true);
                        result = result.Clone(steps: result.Steps.Replace(selectedStep, updated), selectedStep: updated);
                    }
                }
            }
            break;

            case SelectedStepChangedAction selectedStepChanged:
            {
                var selectedStep = selectedStepChanged.Key.HasValue ? state.Steps.Single(s => s.Key == selectedStepChanged.Key) : null;
                result = state.Clone(selectedStep: selectedStep);
            }
            break;

            default:
                result = state;
                break;
            }
            return(result);
        }