Skip to content

Commit c3ba00a

Browse files
Ashish KumarAshish Kumar
authored andcommitted
chore: Graph , UpVote, Hide feature added
1 parent 0753abb commit c3ba00a

File tree

26 files changed

+644
-258
lines changed

26 files changed

+644
-258
lines changed

.dockerignore

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,4 @@
44
/.git
55
.gitignore
66
/env
7-
.env
8-
dev.env
7+
.env

.env

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
PORT=3000
2-
API_URL=https://hn.algolia.com/
3-
HOST_URL=http://localhost:3000
4-
TAG=latest
1+
# add only those variables which are used in docker-compose or Dockerfile
2+
# These are the default ENV VARS for docker
3+
4+
# If you add multiple env files in docker-compose,
5+
# default variables will be override
6+
7+
PORT=5000
8+
TAG=latest
9+
DOCKER_USERNAME=connectashishk
10+
DOCKER_IMAGE_NAME=ssr-build-stream

.eslintrc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
},
3131
"plugins": ["react"],
3232
"rules": {
33+
"no-param-reassign": 0,
3334
"no-console": "off",
3435
"semi": [
3536
"error", "always"
@@ -46,6 +47,7 @@
4647
"checkAttributes": true
4748
}
4849
],
50+
"class-methods-use-this": 0,
4951
"max-len": 0,
5052
"react/jsx-uses-react": "error",
5153
"react/jsx-uses-vars": "error",

.github/workflows/lint.yml

Lines changed: 0 additions & 20 deletions
This file was deleted.

build.sh

Lines changed: 0 additions & 5 deletions
This file was deleted.

client/actions/index.js

Lines changed: 87 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,103 @@
1-
/* eslint-disable import/prefer-default-export */
21
import {
32
FETCH_STORIES_FAILURE,
43
FETCH_STORIES_PENDING,
5-
FETCH_STORIES_SUCCESS
4+
FETCH_STORIES_SUCCESS,
5+
UPVOTE_STORY,
6+
HIDE_STORY
67
} from "../constants/ActionTypes";
78

8-
export const fetchStories = (page = 0) => async (dispatch, getState, api) => {
9+
const appName = require("../../package.json").name;
10+
11+
const upVoteTableName = `${appName}-upvotes`;
12+
const hideStoryTableName = `${appName}-hidden`;
13+
14+
export const updateListWithPersistentData = (data = {}) => async (dispatch, getState, { cookies }) => {
15+
try {
16+
const hiddenStories = cookies.get(hideStoryTableName) || {};
17+
const upVoteStories = cookies.get(upVoteTableName) || {};
18+
return {
19+
...data,
20+
hits: data.hits.map((obj) => {
21+
const myObj = obj;
22+
if (upVoteStories && upVoteStories[myObj.objectID]) {
23+
myObj.points = parseInt(myObj.points, 10) + parseInt(upVoteStories[myObj.objectID], 10);
24+
}
25+
if (hiddenStories && hiddenStories[myObj.objectID]) {
26+
myObj.hidden = true;
27+
}
28+
return myObj;
29+
})
30+
};
31+
} catch (e) {
32+
console.log("Running on server, Will Update on App Mount", e);
33+
return data;
34+
}
35+
};
36+
37+
export const updateStoriesInStore = (data = {}) => async (dispatch) => {
38+
dispatch({
39+
type: FETCH_STORIES_SUCCESS,
40+
payload: data
41+
});
42+
};
43+
44+
export const fetchStories = (page = 0) => async (dispatch, getState, { api }) => {
945
try {
1046
dispatch({ type: FETCH_STORIES_PENDING });
1147

1248
const res = await api.get(`/search?page=${page}&hitsPerPage=30`);
13-
// console.log(res);
14-
dispatch({
15-
type: FETCH_STORIES_SUCCESS,
16-
payload: res.data
17-
});
49+
const modifiedData = await dispatch(updateListWithPersistentData(res.data));
50+
dispatch(updateStoriesInStore(modifiedData));
1851
} catch (err) {
1952
console.log(err);
2053
dispatch({
2154
type: FETCH_STORIES_FAILURE
2255
});
2356
}
2457
};
58+
59+
export const updateVoteInPersistent = (story) => async (dispatch, getState, { cookies }) => {
60+
try {
61+
const votes = cookies.get(upVoteTableName);
62+
const votesObj = votes || {};
63+
if (votesObj[story.objectID]) {
64+
votesObj[story.objectID] += 1;
65+
} else {
66+
votesObj[story.objectID] = 1;
67+
}
68+
cookies.set(upVoteTableName, JSON.stringify(votesObj), { path: "/" });
69+
} catch (e) {
70+
console.log(e);
71+
}
72+
};
73+
74+
export const hideStoryInPersistent = (story) => async (dispatch, getState, { cookies }) => {
75+
try {
76+
const hiddenStories = cookies.get(hideStoryTableName);
77+
const stories = hiddenStories || {};
78+
stories[story.objectID] = true;
79+
cookies.set(hideStoryTableName, JSON.stringify(stories), { path: "/" });
80+
} catch (e) {
81+
console.log(e);
82+
}
83+
};
84+
85+
export const updateUpVote = (story) => async (dispatch) => {
86+
try {
87+
dispatch(updateVoteInPersistent(story));
88+
dispatch({
89+
type: UPVOTE_STORY,
90+
payload: story.objectID
91+
});
92+
} catch (e) {
93+
console.log(e);
94+
}
95+
};
96+
97+
export const hideStory = (story) => async (dispatch) => {
98+
dispatch(hideStoryInPersistent(story));
99+
dispatch({
100+
type: HIDE_STORY,
101+
payload: story.objectID
102+
});
103+
};

client/components/Graph/index.js

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import React, { Component } from "react";
2+
import loadable from "@loadable/component";
3+
import Highcharts from "highcharts";
4+
import { connect } from "react-redux";
5+
6+
const HighchartsReact = loadable(() => import(/* webpackChunkName: "reacthighchart" */"highcharts-react-official"));
7+
8+
class Graph extends Component {
9+
constructor(props) {
10+
super(props);
11+
this.state = {
12+
hoverData: null
13+
};
14+
}
15+
16+
setHoverData = (e) => {
17+
// The chart is not updated because `chartOptions` has not changed.
18+
this.setState({ hoverData: e.target.category });
19+
}
20+
21+
updateSeries = () => {
22+
// The chart is updated only with new options.
23+
this.setState({
24+
chartOptions: {
25+
series: [
26+
{ data: [Math.random() * 5, 2, 1] }
27+
]
28+
}
29+
});
30+
}
31+
32+
render() {
33+
const { data } = this.props;
34+
const votes = [];
35+
const ids = [];
36+
data.forEach((obj) => {
37+
if (obj.title) {
38+
votes.push(obj.points);
39+
ids.push(obj.objectID);
40+
}
41+
return true;
42+
});
43+
const chartOptions = {
44+
responsive: {
45+
rules: [{
46+
condition: {
47+
maxWidth: 500
48+
},
49+
chartOptions: {
50+
chart: {
51+
spacing: [15, 10, 15, 10]
52+
},
53+
yAxis: {
54+
labels: {
55+
align: "left",
56+
x: 0,
57+
y: -5
58+
},
59+
title: {
60+
text: null
61+
}
62+
},
63+
subtitle: {
64+
text: null
65+
},
66+
credits: {
67+
enabled: false
68+
}
69+
}
70+
}]
71+
},
72+
credits: {
73+
enabled: false
74+
},
75+
title: {
76+
text: undefined
77+
},
78+
legend: {
79+
enabled: false
80+
},
81+
yAxis: {
82+
title: {
83+
text: "Votes",
84+
style: {
85+
fontWeight: "bold"
86+
}
87+
}
88+
},
89+
xAxis: {
90+
title: {
91+
text: "ID",
92+
y: 10,
93+
style: {
94+
fontWeight: "bold"
95+
}
96+
},
97+
categories: ids,
98+
labels: {
99+
rotation: -90
100+
}
101+
},
102+
series: [
103+
{ data: votes }
104+
],
105+
plotOptions: {
106+
series: {
107+
point: {
108+
events: {
109+
mouseOver: this.setHoverData.bind(this)
110+
}
111+
}
112+
}
113+
}
114+
};
115+
return (
116+
<div style={{ width: "100%" }}>
117+
<HighchartsReact
118+
allowChartUpdate={true}
119+
highcharts={Highcharts}
120+
options={chartOptions}
121+
/>
122+
</div>
123+
);
124+
}
125+
}
126+
127+
const mapStateToProps = ({ stories }) => ({
128+
data: stories.data.hits
129+
});
130+
export default connect(mapStateToProps, {})(Graph);

client/components/StoryList/ListItem.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,10 @@ const ListItem = (props) => {
2828
points, // Vote Count
2929
title,
3030
url,
31-
author
31+
author,
32+
hidden
3233
} = props.data;
33-
if (!title) {
34+
if (!title || hidden) {
3435
return null;
3536
}
3637
const className = getClassForPoints(points);
@@ -39,7 +40,9 @@ const ListItem = (props) => {
3940
<td className="row-item__td">{numComments || "-"}</td>
4041
<td className={`row-item__td ${className}`}>{points || "-"}</td>
4142
<td className="row-item__td">
42-
<span className="caret-upvote"></span>
43+
<div className="upvote-btn-wrapper" onClick={(e) => props.onUpVote(e, props.data)}>
44+
<span className="caret-upvote" />
45+
</div>
4346
</td>
4447
<td className="row-item__td">
4548
<div className="row-item__info clearfix">
@@ -55,7 +58,7 @@ const ListItem = (props) => {
5558
</div>
5659
<div className="row-item__info--meta-block">
5760
<small>
58-
<a href="#">hide</a>
61+
<a href="#" onClick={(e) => props.hideStory(e, props.data)}>hide</a>
5962
</small>
6063
</div>
6164
</div>

0 commit comments

Comments
 (0)