Add "carousel" redux logic
This commit is contained in:
parent
2a7fd15717
commit
33bebf5bba
5 changed files with 166 additions and 0 deletions
58
app/soapbox/actions/__tests__/carousels.test.ts
Normal file
58
app/soapbox/actions/__tests__/carousels.test.ts
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
import { __stub } from 'soapbox/api';
|
||||||
|
import { mockStore, rootState } from 'soapbox/jest/test-helpers';
|
||||||
|
|
||||||
|
import { fetchCarouselAvatars } from '../carousels';
|
||||||
|
|
||||||
|
describe('fetchCarouselAvatars()', () => {
|
||||||
|
let store;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
store = mockStore(rootState);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with a successful API request', () => {
|
||||||
|
let avatars;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
avatars = [
|
||||||
|
{ account_id: '1', username: 'jl', account_avatar: 'https://example.com/some.jpg' },
|
||||||
|
];
|
||||||
|
|
||||||
|
__stub((mock) => {
|
||||||
|
mock.onGet('/api/v1/truth/carousels/avatars').reply(200, avatars);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fetch the users from the API', async() => {
|
||||||
|
const expectedActions = [
|
||||||
|
{ type: 'CAROUSEL_AVATAR_REQUEST' },
|
||||||
|
{ type: 'CAROUSEL_AVATAR_SUCCESS', payload: avatars },
|
||||||
|
];
|
||||||
|
|
||||||
|
await store.dispatch(fetchCarouselAvatars());
|
||||||
|
const actions = store.getActions();
|
||||||
|
|
||||||
|
expect(actions).toEqual(expectedActions);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with an unsuccessful API request', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
__stub((mock) => {
|
||||||
|
mock.onGet('/api/v1/truth/carousels/avatars').networkError();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should dispatch failed action', async() => {
|
||||||
|
const expectedActions = [
|
||||||
|
{ type: 'CAROUSEL_AVATAR_REQUEST' },
|
||||||
|
{ type: 'CAROUSEL_AVATAR_FAIL' },
|
||||||
|
];
|
||||||
|
|
||||||
|
await store.dispatch(fetchCarouselAvatars());
|
||||||
|
const actions = store.getActions();
|
||||||
|
|
||||||
|
expect(actions).toEqual(expectedActions);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
25
app/soapbox/actions/carousels.ts
Normal file
25
app/soapbox/actions/carousels.ts
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import { AxiosResponse } from 'axios';
|
||||||
|
|
||||||
|
import { AppDispatch, RootState } from 'soapbox/store';
|
||||||
|
|
||||||
|
import api from '../api';
|
||||||
|
|
||||||
|
const CAROUSEL_AVATAR_REQUEST = 'CAROUSEL_AVATAR_REQUEST';
|
||||||
|
const CAROUSEL_AVATAR_SUCCESS = 'CAROUSEL_AVATAR_SUCCESS';
|
||||||
|
const CAROUSEL_AVATAR_FAIL = 'CAROUSEL_AVATAR_FAIL';
|
||||||
|
|
||||||
|
const fetchCarouselAvatars = () => (dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
|
dispatch({ type: CAROUSEL_AVATAR_REQUEST });
|
||||||
|
|
||||||
|
return api(getState)
|
||||||
|
.get('/api/v1/truth/carousels/avatars')
|
||||||
|
.then((response: AxiosResponse) => dispatch({ type: CAROUSEL_AVATAR_SUCCESS, payload: response.data }))
|
||||||
|
.catch(() => dispatch({ type: CAROUSEL_AVATAR_FAIL }));
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
CAROUSEL_AVATAR_REQUEST,
|
||||||
|
CAROUSEL_AVATAR_SUCCESS,
|
||||||
|
CAROUSEL_AVATAR_FAIL,
|
||||||
|
fetchCarouselAvatars,
|
||||||
|
};
|
45
app/soapbox/reducers/__tests__/carousels.test.ts
Normal file
45
app/soapbox/reducers/__tests__/carousels.test.ts
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
import { AnyAction } from 'redux';
|
||||||
|
|
||||||
|
import {
|
||||||
|
CAROUSEL_AVATAR_REQUEST,
|
||||||
|
CAROUSEL_AVATAR_SUCCESS,
|
||||||
|
CAROUSEL_AVATAR_FAIL,
|
||||||
|
} from 'soapbox/actions/carousels';
|
||||||
|
|
||||||
|
import reducer from '../carousels';
|
||||||
|
|
||||||
|
describe('carousels reducer', () => {
|
||||||
|
it('should return the initial state', () => {
|
||||||
|
expect(reducer(undefined, {} as AnyAction)).toEqual({
|
||||||
|
avatars: [],
|
||||||
|
isLoading: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('CAROUSEL_AVATAR_REQUEST', () => {
|
||||||
|
it('sets "isLoading" to "true"', () => {
|
||||||
|
const initialState = { isLoading: false, avatars: [] };
|
||||||
|
const action = { type: CAROUSEL_AVATAR_REQUEST };
|
||||||
|
expect(reducer(initialState, action).isLoading).toEqual(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('CAROUSEL_AVATAR_SUCCESS', () => {
|
||||||
|
it('sets the next state', () => {
|
||||||
|
const initialState = { isLoading: true, avatars: [] };
|
||||||
|
const action = { type: CAROUSEL_AVATAR_SUCCESS, payload: [45] };
|
||||||
|
const result = reducer(initialState, action);
|
||||||
|
|
||||||
|
expect(result.isLoading).toEqual(false);
|
||||||
|
expect(result.avatars).toEqual([45]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('CAROUSEL_AVATAR_FAIL', () => {
|
||||||
|
it('sets "isLoading" to "true"', () => {
|
||||||
|
const initialState = { isLoading: true, avatars: [] };
|
||||||
|
const action = { type: CAROUSEL_AVATAR_FAIL };
|
||||||
|
expect(reducer(initialState, action).isLoading).toEqual(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
36
app/soapbox/reducers/carousels.ts
Normal file
36
app/soapbox/reducers/carousels.ts
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import { AnyAction } from 'redux';
|
||||||
|
|
||||||
|
import {
|
||||||
|
CAROUSEL_AVATAR_REQUEST,
|
||||||
|
CAROUSEL_AVATAR_SUCCESS,
|
||||||
|
CAROUSEL_AVATAR_FAIL,
|
||||||
|
} from '../actions/carousels';
|
||||||
|
|
||||||
|
type Avatar = {
|
||||||
|
account_id: string
|
||||||
|
account_avatar: string
|
||||||
|
username: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type CarouselsState = {
|
||||||
|
avatars: Avatar[],
|
||||||
|
isLoading: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState: CarouselsState = {
|
||||||
|
avatars: [],
|
||||||
|
isLoading: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function rules(state: CarouselsState = initialState, action: AnyAction): CarouselsState {
|
||||||
|
switch (action.type) {
|
||||||
|
case CAROUSEL_AVATAR_REQUEST:
|
||||||
|
return { ...state, isLoading: true };
|
||||||
|
case CAROUSEL_AVATAR_SUCCESS:
|
||||||
|
return { ...state, isLoading: false, avatars: action.payload };
|
||||||
|
case CAROUSEL_AVATAR_FAIL:
|
||||||
|
return { ...state, isLoading: false };
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,6 +14,7 @@ import alerts from './alerts';
|
||||||
import aliases from './aliases';
|
import aliases from './aliases';
|
||||||
import auth from './auth';
|
import auth from './auth';
|
||||||
import backups from './backups';
|
import backups from './backups';
|
||||||
|
import carousels from './carousels';
|
||||||
import chat_message_lists from './chat_message_lists';
|
import chat_message_lists from './chat_message_lists';
|
||||||
import chat_messages from './chat_messages';
|
import chat_messages from './chat_messages';
|
||||||
import chats from './chats';
|
import chats from './chats';
|
||||||
|
@ -122,6 +123,7 @@ const reducers = {
|
||||||
onboarding,
|
onboarding,
|
||||||
rules,
|
rules,
|
||||||
history,
|
history,
|
||||||
|
carousels,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Build a default state from all reducers: it has the key and `undefined`
|
// Build a default state from all reducers: it has the key and `undefined`
|
||||||
|
|
Loading…
Reference in a new issue