Skip to content

Commit 2433eb0

Browse files
committed
add rss feed components
1 parent ffa368e commit 2433eb0

File tree

10 files changed

+158
-75
lines changed

10 files changed

+158
-75
lines changed

public/index.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
name="description"
1010
content="Web site created using create-react-app"
1111
/>
12+
<link
13+
href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap"
14+
rel="stylesheet"
15+
/>
1216
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
1317
<!--
1418
manifest.json provides metadata used when your web app is installed on a

src/App.css

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

src/App.js

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,25 @@
1-
import logo from './logo.svg';
2-
import './App.css';
1+
import { useState } from "react";
2+
import News from "./News";
3+
import NewsContainer from "./NewsContainer";
34

45
function App() {
6+
const [source, setSource] = useState("https://dev.to/feed/");
7+
const [saved, setSaved] = useState([]);
8+
9+
const handleChange = (e) => {
10+
setSource(e.target.value);
11+
}
12+
13+
const handleSave = (item) => {
14+
setSaved([...saved, item]);
15+
}
16+
517
return (
6-
<div className="App">
7-
<header className="App-header">
8-
<img src={logo} className="App-logo" alt="logo" />
9-
<p>
10-
Edit <code>src/App.js</code> and save to reload.
11-
</p>
12-
<a
13-
className="App-link"
14-
href="https://reactjs.org"
15-
target="_blank"
16-
rel="noopener noreferrer"
17-
>
18-
Learn React
19-
</a>
20-
</header>
18+
<div>
19+
<input type="text" value={source} onChange={handleChange} />
20+
<NewsContainer url={source} onSave={handleSave} />
21+
<h1>Saved news</h1>
22+
<News news={saved} />
2123
</div>
2224
);
2325
}

src/App.test.js

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

src/Item.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
function Item(props) {
2+
const item = props.item;
3+
const onSave = props.onSave;
4+
5+
const title = item.title;
6+
const desc = item.desc;
7+
const link = item.link;
8+
9+
const handleClickOnSaveBtn = () => {
10+
onSave({ title, desc, link });
11+
}
12+
13+
return (
14+
<div className="item">
15+
<h1>{title}</h1>
16+
<div>{desc}</div>
17+
<a href={link}>{link}</a>
18+
{onSave ? (
19+
<div>
20+
<button onClick={handleClickOnSaveBtn}>Save</button>
21+
</div>
22+
) : null}
23+
</div>
24+
);
25+
}
26+
27+
export default Item;

src/News.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import Item from "./Item";
2+
3+
function News(props) {
4+
const news = props.news;
5+
const onSave = props.onSave;
6+
7+
return (
8+
<div className="container">
9+
{news.map((item) => (
10+
<Item key={item.link} item={item} onSave={onSave} />
11+
))}
12+
</div>
13+
);
14+
}
15+
16+
export default News;

src/NewsContainer.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { useEffect, useState } from "react";
2+
import News from "./News";
3+
4+
function parseXml(str) {
5+
const parser = new DOMParser();
6+
return parser.parseFromString(str, "text/xml");
7+
}
8+
9+
function parseHtml(str) {
10+
const parser = new DOMParser();
11+
return parser.parseFromString(str, "text/html");
12+
}
13+
14+
function parseNews(newsDomTree) {
15+
const items = newsDomTree.getElementsByTagName("item");
16+
const news = [];
17+
for (const item of items) {
18+
const title = item.getElementsByTagName("title")[0].innerHTML;
19+
const link = item.getElementsByTagName("link")[0].innerHTML;
20+
let desc = item.getElementsByTagName("description")[0];
21+
22+
desc =
23+
parseHtml(desc.textContent).body.textContent.substring(0, 250) + "...";
24+
25+
news.push({ title, desc, link });
26+
}
27+
return news;
28+
}
29+
30+
function NewsContainer(props) {
31+
const { url, onSave } = props;
32+
const [news, setNews] = useState([]);
33+
34+
useEffect(() => {
35+
fetch(url)
36+
.then((res) => res.text())
37+
.then((res) => parseXml(res))
38+
.then((res) => parseNews(res))
39+
.then((res) => {
40+
setNews(res);
41+
})
42+
.catch(() => {
43+
setNews([]);
44+
});
45+
}, [url]);
46+
47+
return <News news={news} onSave={onSave} />;
48+
}
49+
50+
NewsContainer.defaultProps = {
51+
url: "https://dev.to/feed/"
52+
}
53+
54+
export default NewsContainer;

src/index.css

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,42 @@
1+
* {
2+
box-sizing: border-box;
3+
}
4+
15
body {
6+
font-family: 'Roboto', sans-serif;
7+
}
8+
9+
.container {
10+
display: grid;
11+
grid-template-columns: 1fr 1fr 1fr;
12+
grid-auto-rows: minmax(15rem, auto);
13+
column-gap: 1rem;
14+
row-gap: 1rem;
15+
margin: 1rem;
16+
}
17+
18+
@media screen and (max-width: 768px) {
19+
.container {
20+
grid-template-columns: 1fr 1fr;
21+
}
22+
}
23+
24+
@media screen and (max-width: 450px) {
25+
.container {
26+
grid-template-columns: 1fr;
27+
}
28+
}
29+
30+
.item {
31+
box-shadow: 0px 0px 5px 0px #00000085;
32+
border-radius: 4px;
33+
padding: 0.5rem 1rem 1rem 1rem;
34+
}
35+
36+
.item > h1 {
237
margin: 0;
3-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4-
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5-
sans-serif;
6-
-webkit-font-smoothing: antialiased;
7-
-moz-osx-font-smoothing: grayscale;
838
}
939

10-
code {
11-
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12-
monospace;
40+
.item > div {
41+
margin: 0.5rem 0;
1342
}

src/index.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@ import App from './App';
55
import reportWebVitals from './reportWebVitals';
66

77
ReactDOM.render(
8-
<React.StrictMode>
9-
<App />
10-
</React.StrictMode>,
8+
<App />,
119
document.getElementById('root')
1210
);
1311

src/logo.svg

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)