diff --git a/src/features/room-admin/redux/actions.js b/src/features/room-admin/redux/actions.js index dfa3047..a10d213 100644 --- a/src/features/room-admin/redux/actions.js +++ b/src/features/room-admin/redux/actions.js @@ -1 +1,2 @@ export { fetchPublicRooms, dismissFetchPublicRoomsError } from './fetchPublicRooms'; +export { createRoom, dismissCreateRoomError } from './createRoom'; diff --git a/src/features/room-admin/redux/constants.js b/src/features/room-admin/redux/constants.js index eccaf57..db23b41 100644 --- a/src/features/room-admin/redux/constants.js +++ b/src/features/room-admin/redux/constants.js @@ -2,3 +2,7 @@ export const ROOM_ADMIN_FETCH_PUBLIC_ROOMS_BEGIN = 'ROOM_ADMIN_FETCH_PUBLIC_ROOM export const ROOM_ADMIN_FETCH_PUBLIC_ROOMS_SUCCESS = 'ROOM_ADMIN_FETCH_PUBLIC_ROOMS_SUCCESS'; export const ROOM_ADMIN_FETCH_PUBLIC_ROOMS_FAILURE = 'ROOM_ADMIN_FETCH_PUBLIC_ROOMS_FAILURE'; export const ROOM_ADMIN_FETCH_PUBLIC_ROOMS_DISMISS_ERROR = 'ROOM_ADMIN_FETCH_PUBLIC_ROOMS_DISMISS_ERROR'; +export const ROOM_ADMIN_CREATE_ROOM_BEGIN = 'ROOM_ADMIN_CREATE_ROOM_BEGIN'; +export const ROOM_ADMIN_CREATE_ROOM_SUCCESS = 'ROOM_ADMIN_CREATE_ROOM_SUCCESS'; +export const ROOM_ADMIN_CREATE_ROOM_FAILURE = 'ROOM_ADMIN_CREATE_ROOM_FAILURE'; +export const ROOM_ADMIN_CREATE_ROOM_DISMISS_ERROR = 'ROOM_ADMIN_CREATE_ROOM_DISMISS_ERROR'; diff --git a/src/features/room-admin/redux/createRoom.js b/src/features/room-admin/redux/createRoom.js new file mode 100644 index 0000000..442d6ee --- /dev/null +++ b/src/features/room-admin/redux/createRoom.js @@ -0,0 +1,86 @@ +import { + ROOM_ADMIN_CREATE_ROOM_BEGIN, + ROOM_ADMIN_CREATE_ROOM_SUCCESS, + ROOM_ADMIN_CREATE_ROOM_FAILURE, + ROOM_ADMIN_CREATE_ROOM_DISMISS_ERROR, +} from './constants'; + +// For options see https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-createroom +export function createRoom(options) { + return (dispatch, getState) => { + dispatch({ + type: ROOM_ADMIN_CREATE_ROOM_BEGIN, + }); + + const promise = new Promise((resolve, reject) => { + const mtx = getState().common.mtx; + if (!mtx) return; + const doRequest = mtx.createRoom(options); + doRequest.then( + (res) => { + dispatch({ + type: ROOM_ADMIN_CREATE_ROOM_SUCCESS, + data: res, + }); + resolve(res); + }, + // Use rejectHandler as the second argument so that render errors won't be caught. + (err) => { + dispatch({ + type: ROOM_ADMIN_CREATE_ROOM_FAILURE, + data: { error: err }, + }); + reject(err); + }, + ); + }); + + return promise; + }; +} + +// Async action saves request error by default, this method is used to dismiss the error info. +// If you don't want errors to be saved in Redux store, just ignore this method. +export function dismissCreateRoomError() { + return { + type: ROOM_ADMIN_CREATE_ROOM_DISMISS_ERROR, + }; +} + +export function reducer(state, action) { + switch (action.type) { + case ROOM_ADMIN_CREATE_ROOM_BEGIN: + // Just after a request is sent + return { + ...state, + createRoomPending: true, + createRoomError: null, + }; + + case ROOM_ADMIN_CREATE_ROOM_SUCCESS: + // The request is success + return { + ...state, + createRoomPending: false, + createRoomError: null, + }; + + case ROOM_ADMIN_CREATE_ROOM_FAILURE: + // The request is failed + return { + ...state, + createRoomPending: false, + createRoomError: action.data.error, + }; + + case ROOM_ADMIN_CREATE_ROOM_DISMISS_ERROR: + // Dismiss the request failure error + return { + ...state, + createRoomError: null, + }; + + default: + return state; + } +} diff --git a/src/features/room-admin/redux/initialState.js b/src/features/room-admin/redux/initialState.js index f3860da..7674a7b 100644 --- a/src/features/room-admin/redux/initialState.js +++ b/src/features/room-admin/redux/initialState.js @@ -6,9 +6,11 @@ // NOTE: initialState constant is necessary so that Rekit could auto add initial state when creating async actions. const initialState = { + roomList: [], fetchPublicRoomsPending: false, fetchPublicRoomsError: null, - roomList: [], + createRoomPending: false, + createRoomError: null, }; export default initialState; diff --git a/src/features/room-admin/redux/reducer.js b/src/features/room-admin/redux/reducer.js index 3da8f75..1b9c9b7 100644 --- a/src/features/room-admin/redux/reducer.js +++ b/src/features/room-admin/redux/reducer.js @@ -8,9 +8,11 @@ import initialState from './initialState'; import { reducer as fetchPublicRoomsReducer } from './fetchPublicRooms'; +import { reducer as createRoomReducer } from './createRoom'; const reducers = [ fetchPublicRoomsReducer, + createRoomReducer, ]; export default function reducer(state = initialState, action) { diff --git a/tests/features/room-admin/redux/createRoom.test.js b/tests/features/room-admin/redux/createRoom.test.js new file mode 100644 index 0000000..0e8204d --- /dev/null +++ b/tests/features/room-admin/redux/createRoom.test.js @@ -0,0 +1,97 @@ +import configureMockStore from 'redux-mock-store'; +import thunk from 'redux-thunk'; +import nock from 'nock'; + +import { + ROOM_ADMIN_CREATE_ROOM_BEGIN, + ROOM_ADMIN_CREATE_ROOM_SUCCESS, + ROOM_ADMIN_CREATE_ROOM_FAILURE, + ROOM_ADMIN_CREATE_ROOM_DISMISS_ERROR, +} from '../../../../src/features/room-admin/redux/constants'; + +import { + createRoom, + dismissCreateRoomError, + reducer, +} from '../../../../src/features/room-admin/redux/createRoom'; + +const middlewares = [thunk]; +const mockStore = configureMockStore(middlewares); + +describe('room-admin/redux/createRoom', () => { + afterEach(() => { + nock.cleanAll(); + }); + + it('dispatches success action when createRoom succeeds', () => { + const store = mockStore({}); + + return store.dispatch(createRoom()) + .then(() => { + const actions = store.getActions(); + expect(actions[0]).toHaveProperty('type', ROOM_ADMIN_CREATE_ROOM_BEGIN); + expect(actions[1]).toHaveProperty('type', ROOM_ADMIN_CREATE_ROOM_SUCCESS); + }); + }); + + it('dispatches failure action when createRoom fails', () => { + const store = mockStore({}); + + return store.dispatch(createRoom({ error: true })) + .catch(() => { + const actions = store.getActions(); + expect(actions[0]).toHaveProperty('type', ROOM_ADMIN_CREATE_ROOM_BEGIN); + expect(actions[1]).toHaveProperty('type', ROOM_ADMIN_CREATE_ROOM_FAILURE); + expect(actions[1]).toHaveProperty('data.error', expect.anything()); + }); + }); + + it('returns correct action by dismissCreateRoomError', () => { + const expectedAction = { + type: ROOM_ADMIN_CREATE_ROOM_DISMISS_ERROR, + }; + expect(dismissCreateRoomError()).toEqual(expectedAction); + }); + + it('handles action type ROOM_ADMIN_CREATE_ROOM_BEGIN correctly', () => { + const prevState = { registerRoomPending: false }; + const state = reducer( + prevState, + { type: ROOM_ADMIN_CREATE_ROOM_BEGIN } + ); + expect(state).not.toBe(prevState); // should be immutable + expect(state.registerRoomPending).toBe(true); + }); + + it('handles action type ROOM_ADMIN_CREATE_ROOM_SUCCESS correctly', () => { + const prevState = { registerRoomPending: true }; + const state = reducer( + prevState, + { type: ROOM_ADMIN_CREATE_ROOM_SUCCESS, data: {} } + ); + expect(state).not.toBe(prevState); // should be immutable + expect(state.registerRoomPending).toBe(false); + }); + + it('handles action type ROOM_ADMIN_CREATE_ROOM_FAILURE correctly', () => { + const prevState = { registerRoomPending: true }; + const state = reducer( + prevState, + { type: ROOM_ADMIN_CREATE_ROOM_FAILURE, data: { error: new Error('some error') } } + ); + expect(state).not.toBe(prevState); // should be immutable + expect(state.registerRoomPending).toBe(false); + expect(state.registerRoomError).toEqual(expect.anything()); + }); + + it('handles action type ROOM_ADMIN_CREATE_ROOM_DISMISS_ERROR correctly', () => { + const prevState = { registerRoomError: new Error('some error') }; + const state = reducer( + prevState, + { type: ROOM_ADMIN_CREATE_ROOM_DISMISS_ERROR } + ); + expect(state).not.toBe(prevState); // should be immutable + expect(state.registerRoomError).toBe(null); + }); +}); +