Quellcode durchsuchen

Added annoy button to manually trigger annoy

Fela Maslen vor 7 Jahren
Ursprung
Commit
d2206b2ab4

+ 9 - 0
src/actions/annoy.js

@@ -0,0 +1,9 @@
+import {
+    ANNOYED,
+    ANNOY_RESPONDED
+} from 'constants/actions';
+
+export const annoyed = () => ({ type: ANNOYED });
+
+export const annoyResponded = (err = null) => ({ type: ANNOY_RESPONDED, err });
+

+ 13 - 7
src/components/App/index.js

@@ -2,6 +2,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { Provider } from 'react-redux';
 
+import AnnoyButton from 'containers/AnnoyButton';
 import EmployeeAdmin from 'components/EmployeeAdmin';
 import PhraseAdmin from 'components/PhraseAdmin';
 
@@ -9,13 +10,18 @@ import './style.scss';
 
 export default function App({ store }) {
     return (
-        <div className="gurubot2">
-            <Provider store={store}>
-                <h1>{'GuruBot2 Admin Panel'}</h1>
-                <EmployeeAdmin />
-                <PhraseAdmin />
-            </Provider>
-        </div>
+        <Provider store={store}>
+            <div className="gurubot2">
+                <header className="main-header">
+                    <h1>{'GuruBot2 Admin Panel'}</h1>
+                    <AnnoyButton />
+                </header>
+                <div className="main-body">
+                    <EmployeeAdmin />
+                    <PhraseAdmin />
+                </div>
+            </div>
+        </Provider>
     );
 }
 

+ 3 - 0
src/constants/actions.js

@@ -7,3 +7,6 @@ export const DOC_UPDATE_RESPONDED = 'DOC_UPDATE_RESPONDED';
 export const DOC_DELETED = 'DOC_DELETED';
 export const DOC_DELETE_RESPONDED = 'DOC_DELETE_RESPONDED';
 
+export const ANNOYED = 'ANNOYED';
+export const ANNOY_RESPONDED = 'ANNOY_RESPONDED';
+

+ 40 - 0
src/containers/AnnoyButton/index.js

@@ -0,0 +1,40 @@
+import { connect } from 'react-redux';
+import React from 'react';
+import PropTypes from 'prop-types';
+import classNames from 'classnames';
+
+import { annoyed } from 'actions/annoy';
+
+import './style.scss';
+
+const mapStateToProps = state => ({
+    loading: state.annoy.loading,
+    error: state.annoy.error
+});
+
+const mapDispatchToProps = dispatch => ({
+    onAnnoy: () => dispatch(annoyed())
+});
+
+function AnnoyButton({ loading, error, onAnnoy }) {
+    return (
+        <div className={classNames('annoy-button-wrapper', {
+            loading,
+            error
+        })}>
+            <button className="button-annoy"
+                onClick={onAnnoy}
+                disabled={loading}
+            >{'Annoy'}</button>
+        </div>
+    );
+}
+
+AnnoyButton.propTypes = {
+    loading: PropTypes.bool.isRequired,
+    error: PropTypes.bool.isRequired,
+    onAnnoy: PropTypes.func.isRequired
+};
+
+export default connect(mapStateToProps, mapDispatchToProps)(AnnoyButton);
+

+ 0 - 0
src/containers/AnnoyButton/style.scss


+ 37 - 0
src/reducers/annoy.js

@@ -0,0 +1,37 @@
+import { createReducerObject } from 'create-reducer-object';
+
+import {
+    ANNOYED,
+    ANNOY_RESPONDED
+} from 'constants/actions';
+
+const initialState = {
+    loading: false,
+    error: false
+};
+
+function onAnnoy(state) {
+    if (state.loading) {
+        return {};
+    }
+
+    return {
+        loading: true,
+        error: false
+    };
+}
+
+function onAnnoyed(state, { err }) {
+    return {
+        loading: false,
+        error: Boolean(err)
+    };
+}
+
+const reducerMap = {
+    [ANNOYED]: onAnnoy,
+    [ANNOY_RESPONDED]: onAnnoyed
+};
+
+export const annoy = createReducerObject(reducerMap, initialState);
+

+ 2 - 0
src/reducers/index.js

@@ -1,8 +1,10 @@
 import { combineReducers } from 'redux';
 
+import { annoy } from 'reducers/annoy';
 import { crud } from 'reducers/crud';
 
 export const rootReducer = combineReducers({
+    annoy,
     crud
 });
 

+ 18 - 0
src/sagas/annoy.js

@@ -0,0 +1,18 @@
+import { take, call, put } from 'redux-saga/effects';
+
+import { apiRequest } from 'sagas/api';
+
+import { ANNOYED } from 'constants/actions';
+
+import { annoyResponded } from 'actions/annoy';
+
+export function *annoySaga() {
+    while (true) {
+        yield take(ANNOYED);
+
+        const { err } = yield call(apiRequest, 'post', 'annoy');
+
+        yield put(annoyResponded(err));
+    }
+}
+

+ 2 - 0
src/sagas/index.js

@@ -1,8 +1,10 @@
 import { fork } from 'redux-saga/effects';
 
 import { crudSaga } from 'sagas/crud';
+import { annoySaga } from 'sagas/annoy';
 
 export function *rootSaga() {
     yield fork(crudSaga);
+    yield fork(annoySaga);
 }
 

+ 38 - 0
test/actions/annoy.spec.js

@@ -0,0 +1,38 @@
+import { expect } from 'chai';
+
+import {
+    ANNOYED,
+    ANNOY_RESPONDED
+} from 'constants/actions';
+
+import {
+    annoyed,
+    annoyResponded
+} from 'actions/annoy';
+
+describe('Annoy actions', () => {
+    describe('annoyed', () => {
+        it('should return ANNOYED', () => {
+            expect(annoyed()).to.deep.equal({
+                type: ANNOYED
+            });
+        });
+    });
+
+    describe('annoyResponded', () => {
+        it('should return ANNOY_RESPONDED with optional error', () => {
+            const err = new Error('something bad happened');
+
+            expect(annoyResponded(err)).to.deep.equal({
+                type: ANNOY_RESPONDED,
+                err
+            });
+
+            expect(annoyResponded()).to.deep.equal({
+                type: ANNOY_RESPONDED,
+                err: null
+            });
+        });
+    });
+});
+

+ 45 - 0
test/sagas/annoy.spec.js

@@ -0,0 +1,45 @@
+/* eslint-disable prefer-reflect */
+import { testSaga } from 'redux-saga-test-plan';
+
+import {
+    annoySaga
+} from 'sagas/annoy';
+
+import {
+    apiRequest
+} from 'sagas/api';
+
+import {
+    ANNOYED
+} from 'constants/actions';
+
+import {
+    annoyResponded
+} from 'actions/annoy';
+
+describe('Annoy saga', () => {
+    it('should listen to ANNOYED and send an API request', () => {
+        testSaga(annoySaga)
+            .next()
+            .take(ANNOYED)
+            .next()
+            .call(apiRequest, 'post', 'annoy')
+            .next({ err: null, response: { isResponse: true } })
+            .put(annoyResponded(null))
+            .next()
+            .take(ANNOYED);
+
+        const err = new Error('something bad happened');
+
+        testSaga(annoySaga)
+            .next()
+            .take(ANNOYED)
+            .next()
+            .call(apiRequest, 'post', 'annoy')
+            .next({ err, response: null })
+            .put(annoyResponded(err))
+            .next()
+            .take(ANNOYED);
+    });
+});
+

+ 6 - 0
test/sagas/index.spec.js

@@ -8,12 +8,18 @@ import {
     crudSaga
 } from 'sagas/crud';
 
+import {
+    annoySaga
+} from 'sagas/annoy';
+
 describe('Root saga', () => {
     it('should fork other sagas', () => {
         testSaga(rootSaga)
             .next()
             .fork(crudSaga)
             .next()
+            .fork(annoySaga)
+            .next()
             .isDone();
     });
 });