diff --git a/reactjs/components/Gallery/index.js b/reactjs/components/Gallery/index.js
new file mode 100644
index 0000000..ab13a9d
--- /dev/null
+++ b/reactjs/components/Gallery/index.js
@@ -0,0 +1,32 @@
+import PropTypes from 'prop-types';
+
+function Gallery(props) {
+ const content = props.giphyData.map((img) =>
+
+
+
+

+
+
{img.title}
+
+
+
+
+
+);
+return (
+
+ )
+}
+
+Gallery.propTypes = {
+ content: PropTypes.array
+}
+
+export default Gallery;
diff --git a/reactjs/components/HtmlHead/index.js b/reactjs/components/HtmlHead/index.js
index 3e9d76b..4f8c980 100644
--- a/reactjs/components/HtmlHead/index.js
+++ b/reactjs/components/HtmlHead/index.js
@@ -25,6 +25,7 @@ SystemSeed Technical Assessment
+
{stylesheets}
);
diff --git a/reactjs/package-lock.json b/reactjs/package-lock.json
index fb33b39..57c3f78 100644
--- a/reactjs/package-lock.json
+++ b/reactjs/package-lock.json
@@ -1369,6 +1369,15 @@
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
"integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ=="
},
+ "axios": {
+ "version": "0.18.0",
+ "resolved": "http://registry.npmjs.org/axios/-/axios-0.18.0.tgz",
+ "integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=",
+ "requires": {
+ "follow-redirects": "1.5.10",
+ "is-buffer": "1.1.6"
+ }
+ },
"axobject-query": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.2.tgz",
@@ -1582,6 +1591,11 @@
"type-is": "1.6.16"
}
},
+ "bootstrap": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.1.3.tgz",
+ "integrity": "sha512-rDFIzgXcof0jDyjNosjv4Sno77X4KuPeFxG2XZZv1/Kc8DRVGVADdoQyyOVDwPqL36DDmtCQbrpMCqvpPLJQ0w=="
+ },
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -3303,6 +3317,24 @@
"readable-stream": "2.3.6"
}
},
+ "follow-redirects": {
+ "version": "1.5.10",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
+ "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
+ "requires": {
+ "debug": "3.1.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ }
+ }
+ },
"for-in": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
@@ -4615,6 +4647,22 @@
"resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
},
+ "isomorphic-unfetch": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/isomorphic-unfetch/-/isomorphic-unfetch-3.0.0.tgz",
+ "integrity": "sha512-V0tmJSYfkKokZ5mgl0cmfQMTb7MLHsBMngTkbLY0eXvKqiVRRoZP04Ly+KhKrJfKtzC9E6Pp15Jo+bwh7Vi2XQ==",
+ "requires": {
+ "node-fetch": "2.3.0",
+ "unfetch": "4.0.1"
+ },
+ "dependencies": {
+ "unfetch": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.0.1.tgz",
+ "integrity": "sha512-HzDM9NgldcRvHVDb/O9vKoUszVij30Yw5ePjOZJig8nF/YisG7QN/9CBXZ8dsHLouXMeLZ82r+Jod9M2wFkEbQ=="
+ }
+ }
+ },
"isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
@@ -5381,6 +5429,11 @@
"lower-case": "1.1.4"
}
},
+ "node-fetch": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.3.0.tgz",
+ "integrity": "sha512-MOd8pV3fxENbryESLgVIeaGKrdl+uaYhCSSVkjeOb/31/njTpcis5aWfdqgNlHIrKOLRbMnfPINPOML2CIFeXA=="
+ },
"node-gyp": {
"version": "3.8.0",
"resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz",
@@ -6139,6 +6192,11 @@
"resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz",
"integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM="
},
+ "random-words": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/random-words/-/random-words-1.1.0.tgz",
+ "integrity": "sha512-GyV8PlSmQE08S/RSCjG9Uh0uQaUC7iRpA18PWk9OSnvNCzKQ+B2NxqqN/cYBej4t7dfBWxh10KFBYSiNcg1jlg=="
+ },
"randombytes": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz",
diff --git a/reactjs/package.json b/reactjs/package.json
index 3397b0f..9ed0517 100644
--- a/reactjs/package.json
+++ b/reactjs/package.json
@@ -3,8 +3,11 @@
"version": "1.1.0",
"license": "MIT",
"dependencies": {
+ "axios": "^0.18.0",
+ "bootstrap": "^4.1.3",
"compression": "^1.7.3",
"express": "^4.16.3",
+ "isomorphic-unfetch": "^3.0.0",
"next": "^7.0.2",
"next-redux-saga": "^3.0.0-beta.1",
"next-redux-wrapper": "^2.0.0",
@@ -12,6 +15,7 @@
"node-sass-glob-importer": "^5.2.0",
"nprogress": "^0.2.0",
"prop-types": "^15.6.2",
+ "random-words": "^1.1.0",
"raw-loader": "^0.5.1",
"react": "^16.4.1",
"react-dom": "^16.4.1",
diff --git a/reactjs/pages/_app.js b/reactjs/pages/_app.js
index ab82d09..2e16664 100644
--- a/reactjs/pages/_app.js
+++ b/reactjs/pages/_app.js
@@ -6,6 +6,8 @@ import withReduxSaga from 'next-redux-saga';
import HtmlHead from '../components/HtmlHead';
import configureStore from '../store/store';
import '../components/PageProgressBar'; // Beautiful page transition indicator.
+import '../node_modules/bootstrap/dist/css/bootstrap.min.css';
+
class Application extends App {
static async getInitialProps({ Component, ctx }) {
diff --git a/reactjs/pages/index.js b/reactjs/pages/index.js
index c6b3226..5a106ec 100644
--- a/reactjs/pages/index.js
+++ b/reactjs/pages/index.js
@@ -1,16 +1,54 @@
import React from 'react';
-import HelloFriend from '../components/HelloFriend';
+import Link from 'next/link';
+import {connect} from 'react-redux';
+import PropTypes from 'prop-types';
+
+import Gallery from '../components/Gallery';
class Page extends React.Component {
- static async getInitialProps() {
- return {
- name: 'value',
- };
+
+ componentDidMount() {
+ this.props.dispatch({type: 'API_CALL_REQUEST'});
+ this.interval = setInterval(() => {
+ this.props.dispatch({type: 'API_CALL_REQUEST'});
+ }, 5000);
+ }
+
+ componentWillUnmount() {
+ clearInterval(this.interval)
}
render() {
- return ;
+ const giphys = this.props.imageData;
+ return (
+
+ );
+ }
+}
+
+Page.propTypes = {
+ imageData: PropTypes.array
+}
+
+function mapStateToProps(state) {
+ return {
+ imageData: state.giphy.images.data,
}
}
-export default Page;
+export default connect(mapStateToProps)(Page);
diff --git a/reactjs/pages/pageTwo.js b/reactjs/pages/pageTwo.js
new file mode 100644
index 0000000..d38fdc9
--- /dev/null
+++ b/reactjs/pages/pageTwo.js
@@ -0,0 +1,53 @@
+import React from 'react';
+import Link from 'next/link';
+import {connect} from 'react-redux';
+import PropTypes from 'prop-types';
+
+import Gallery from '../components/Gallery';
+
+class PageTwo extends React.Component {
+
+ componentDidMount() {
+ this.props.dispatch({type: 'API_CALL_REQUEST'});
+ this.interval = setInterval(() => {
+ this.props.dispatch({type: 'API_CALL_REQUEST'});
+ }, 5000);
+ }
+
+ componentWillUnmount() {
+ clearInterval(this.interval)
+ }
+
+ render() {
+ const giphys = this.props.imageData;
+ return (
+
+ );
+ }
+}
+
+PageTwo.propTypes = {
+ imageData: PropTypes.array
+}
+
+function mapStateToProps(state) {
+ return {
+ imageData: state.giphy.images.data,
+ }
+}
+
+export default connect(mapStateToProps)(PageTwo);
diff --git a/reactjs/store/actions/giphy.js b/reactjs/store/actions/giphy.js
new file mode 100644
index 0000000..77a23b6
--- /dev/null
+++ b/reactjs/store/actions/giphy.js
@@ -0,0 +1,23 @@
+export const API_CALL_REQUEST = "API_CALL_REQUEST";
+export const API_CALL_SUCCESS = "API_CALL_SUCCESS";
+export const API_CALL_FAILURE = "API_CALL_FAILURE";
+
+export function requestImages() {
+ return {
+ type: API_CALL_REQUEST
+ };
+}
+
+export function loadImages(images) {
+ return {
+ type: API_CALL_SUCCESS,
+ images
+ };
+}
+
+export function fetchImagesFail(error) {
+ return {
+ type: API_CALL_FAILURE,
+ error
+ };
+}
diff --git a/reactjs/store/reducers/giphy.js b/reactjs/store/reducers/giphy.js
new file mode 100644
index 0000000..a083b34
--- /dev/null
+++ b/reactjs/store/reducers/giphy.js
@@ -0,0 +1,20 @@
+import {API_CALL_REQUEST, API_CALL_SUCCESS, API_CALL_FAILURE, INCREMENT} from '../actions/giphy';
+
+const defaultState = {
+ images: [],
+ isFetching: false,
+ error: false
+}
+
+export default function images(state = {defaultState}, action) {
+ switch(action.type) {
+ case API_CALL_REQUEST:
+ return {...state, error: false, isFetching: true};
+ case API_CALL_SUCCESS:
+ return {...state, error: false, images: action.images, isFetching: false} ;
+ case API_CALL_FAILURE:
+ return {...state, error: true, isFetching: false};
+ default:
+ return state;
+ }
+}
diff --git a/reactjs/store/reducers/index.js b/reactjs/store/reducers/index.js
index 72a8dcf..d3418fd 100644
--- a/reactjs/store/reducers/index.js
+++ b/reactjs/store/reducers/index.js
@@ -1,7 +1,9 @@
import { combineReducers } from 'redux';
// Put all your reducers here.
import example from './example';
+import giphy from './giphy';
export default combineReducers({
example,
+ giphy,
});
diff --git a/reactjs/store/sagas/giphy.js b/reactjs/store/sagas/giphy.js
new file mode 100644
index 0000000..f71297a
--- /dev/null
+++ b/reactjs/store/sagas/giphy.js
@@ -0,0 +1,33 @@
+import { all, takeLatest, call, put } from 'redux-saga/effects';
+import axios from 'axios';
+
+import { API_CALL_REQUEST, API_CALL_SUCCESS, API_CALL_FAILURE, API_CALL_FETCHED } from '../actions/giphy';
+
+function apiFetch() {
+ let randomWord = require('random-words');
+ let API_ENDPOINT = `https://api.giphy.com/v1/gifs/search?api_key=h6q9hI50TiiUqR8F5D4aGBm2Gqiny0jG&q=${randomWord()}&limit=12`;
+ return axios({
+ method: "get",
+ url: API_ENDPOINT
+ });
+}
+
+export function* fetchImages() {
+ try {
+ const res = yield call(apiFetch)
+ //const res = yield call(() => axios.get(API_ENDPOINT))
+ const images = yield res.data
+ yield put({type: API_CALL_SUCCESS, images})
+
+ } catch (e) {
+ yield put({type: API_CALL_FAILURE, e})
+ console.log('Error fetching giphy data')
+ }
+}
+
+export default function* giphySaga() {
+ yield all([
+ yield takeLatest(API_CALL_REQUEST, fetchImages),
+ yield call(fetchImages)
+ ]);
+}
diff --git a/reactjs/store/sagas/index.js b/reactjs/store/sagas/index.js
index 6ff084d..13851b5 100644
--- a/reactjs/store/sagas/index.js
+++ b/reactjs/store/sagas/index.js
@@ -1,9 +1,11 @@
import { all } from 'redux-saga/effects';
// Put all your sagas here.
import exampleSagas from './example';
+import giphySaga from './giphy';
export default function* rootSaga() {
yield all([
...exampleSagas(),
+ ...giphySaga(),
]);
}