public AppUIStore(AppDispatcher dispatcher, IReadAndWriteMessages messageApi) { if (dispatcher == null) { throw new ArgumentNullException("dispatcher"); } if (messageApi == null) { throw new ArgumentNullException("messageApi"); } NewMessage = GetEmptyNewMessage(); MessageHistory = NonNullList <SavedMessageDetails> .Empty; _saveActionRequestId = null; dispatcher.Receive(a => a .If <StoreInitialised>( condition: action => action.Store == this, work: action => { // When it's time for a Store to be initialised (to set its initial state and call OnChange to let any interested Components know // that it's ready), a StoreInitialised action will be dispatched that references the Store. In a more complicated app, a router // might choose an initial Store based upon the current URL. (We don't need to do anything within this callback, we just need to // match the StoreInitialised so that IfAnyMatched will fire and call OnChange). } ) .Else <MessageEditStateChanged>(action => NewMessage = UpdateValidationFor(action.NewState)) .Else <MessageSaveRequested>(action => { NewMessage = NewMessage.With(_ => _.IsSaveInProgress, true); _saveActionRequestId = messageApi.SaveMessage(action.Message); }) .Else <MessageSaveSucceeded>( condition: action => action.RequestId == _saveActionRequestId, work: action => { // The API's SaveMessage function will fire a MessageSaveSucceeded action when (if) the save is successful and then a subsequent // MessageHistoryUpdated action after it's automatically retrieved fresh data, including the newly-saved item (so we need only // reset the form here, a MessageHistoryUpdated should be along shortly containig the new item..) _saveActionRequestId = null; NewMessage = GetEmptyNewMessage(); } ) .Else <MessageHistoryUpdated>(action => MessageHistory = action.Messages) .IfAnyMatched(OnChange) ); }
public static void RunTests() { Module("AppDispatcher"); Test("Broadcast action to single receiver", assert => { var dispatcher = new AppDispatcher(); dispatcher.Receive(action => { assert.Equal(action.GetType(), typeof(SomethingHappened)); }); dispatcher.Dispatch(new SomethingHappened()); }); Test("Broadcast action to two receivers, where the second to subscribe waits for the first to complete", assert => { var dispatcher = new AppDispatcher(); var firstReceiverProcessed = false; var token = dispatcher.Receive(action => { firstReceiverProcessed = true; }); dispatcher.Receive(action => { dispatcher.WaitFor(token); assert.Equal(firstReceiverProcessed, true); }); dispatcher.Dispatch(new SomethingHappened()); }); Test("Broadcast action to two receivers, where the first to subscribe waits for the second to complete", assert => { var dispatcher = new AppDispatcher(); DispatchToken token = null; var secondReceiverProcessed = false; dispatcher.Receive(action => { dispatcher.WaitFor(token); assert.Equal(secondReceiverProcessed, true); }); token = dispatcher.Receive(action => { secondReceiverProcessed = true; }); dispatcher.Dispatch(new SomethingHappened()); }); Test("Broadcast action to two receivers that WaitFor each other - expect circular dependency failure", assert => { var dispatcher = new AppDispatcher(); DispatchToken token2 = null; var token1 = dispatcher.Receive(action => { dispatcher.WaitFor(token2); }); token2 = dispatcher.Receive(action => { dispatcher.WaitFor(token1); }); assert.Throws(() => dispatcher.Dispatch(new SomethingHappened()), "Dispatch should throw in this case"); }); Test("Broadcast action to three receivers where the first waits on the third, the second waits on the first and the third waits on the second - expect circular dependency failure", assert => { var dispatcher = new AppDispatcher(); DispatchToken token3 = null; var token1 = dispatcher.Receive(action => { dispatcher.WaitFor(token3); }); var token2 = dispatcher.Receive(action => { dispatcher.WaitFor(token1); }); token3 = dispatcher.Receive(action => { dispatcher.WaitFor(token2); }); assert.Throws(() => dispatcher.Dispatch(new SomethingHappened()), "Dispatch should throw in this case"); }); }