Skip to content

Commit b911ae9

Browse files
committed
Add Step 10 - Mark unread/read
- Add Mark Unread/Read button to `EmailView` - Create `EmailViewButtonBar` helper component
1 parent e605cd3 commit b911ae9

File tree

20 files changed

+1285
-28
lines changed

20 files changed

+1285
-28
lines changed

09-delete-email/README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ None
66

77
## Tasks
88

9-
- Add a delete button to each [`EmailListItem`](src/components/EmailListItem.js) that will make a `fetch` `DELETE` & optimistically update afterwards
10-
- Add a delete button to [`EmailView`](src/components/EmailView.js) that'll do the same
9+
- Add a "Delete" button to each [`EmailListItem`](src/components/EmailListItem.js) that will make a `fetch` `DELETE` & optimistically update `emails` state afterwards
10+
- Add a "Delete" button to [`EmailView`](src/components/EmailView.js) that will do the same
1111

1212
## Next
1313

14-
Coming soon...
14+
Go to [Step 10 - Mark unread/read](https://github.com/benmvp/react-workshop/tree/master/10-mark-unread).
1515

1616
## Resources
1717

10-mark-unread/.eslintignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Ignore built files
2+
src/dist/

10-mark-unread/README.md

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Step 10 - Mark unread/read
2+
3+
## Setup
4+
5+
None
6+
7+
## Tasks
8+
9+
- Add a "Mark Unread" for each [`EmailListItem`](src/components/EmailListItem.js) that only shows when an item is selected (and read), making a `fetch` `PUT` when clicked
10+
- Add "Mark Unread" & "Mark Read" buttons to [`EmailView`](src/components/EmailView.js) that will do the same
11+
- When an `EmailListItem` is selected, it should also mark the email as read
12+
- After making the `fetch` `PUT`, optimistically update the `emails` state
13+
14+
## Next
15+
16+
Coming soon...
17+
18+
## Resources
19+
20+
None

10-mark-unread/api-server.js

+154
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
var fs = require('fs'),
2+
path = require('path'),
3+
express = require('express'),
4+
bodyParser = require('body-parser'),
5+
6+
assign = require('lodash/assign'),
7+
find = require('lodash/find'),
8+
findIndex = require('lodash/findIndex'),
9+
10+
app = express(),
11+
router = express.Router(),
12+
13+
// Using a JSON file as our "database"
14+
EMAILS_FILE = path.join(__dirname, 'data/emails.json'),
15+
16+
port = process.env.PORT || 9090;
17+
18+
function getEmails(callback) {
19+
fs.readFile(EMAILS_FILE, function(err, fileContents) {
20+
if (err) {
21+
console.log(err);
22+
process.exit(1);
23+
}
24+
25+
callback(JSON.parse(fileContents));
26+
});
27+
}
28+
29+
function saveEmails(emails, callback) {
30+
fs.writeFile(EMAILS_FILE, JSON.stringify(emails, null, 4), function(err) {
31+
if (err) {
32+
console.log(err);
33+
process.exit(1);
34+
}
35+
36+
callback();
37+
});
38+
}
39+
40+
app.use(bodyParser.json());
41+
app.use(bodyParser.urlencoded({extended: true}));
42+
43+
// allow for cross-origin API requests
44+
app.use(function(req, res, next) {
45+
res.header('Access-Control-Allow-Origin', '*');
46+
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With');
47+
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,PATCH,OPTIONS');
48+
next();
49+
});
50+
51+
// routes that end in /emails
52+
router.route('/emails')
53+
54+
// create an email (accessed via POST to http://localhost:9090/emails)
55+
.post(function(req, res) {
56+
getEmails(function(emails) {
57+
var newEmail = assign({
58+
id: Date.now(),
59+
date: new Date() + '',
60+
unread: true
61+
}, req.body),
62+
newEmails = emails.concat(newEmail);
63+
64+
// write out file back to disk
65+
saveEmails(newEmails, function() {
66+
res.json({success: true});
67+
});
68+
});
69+
})
70+
71+
// get all the emails (access via GET from http://localhost:9090/emails)
72+
.get(function(req, res) {
73+
getEmails(function(emails) {
74+
// Return back the full list of emails
75+
res.setHeader('Cache-Control', 'no-cache');
76+
res.json(
77+
emails
78+
.filter(function(email) { return !email.deleted; })
79+
.sort(function(emailA, emailB) { return new Date(emailB.date) - new Date(emailA.date); })
80+
);
81+
});
82+
});
83+
84+
// routes that end in emails/:emailId
85+
router.route('/emails/:emailId')
86+
87+
// get the email with this id (accessed via GET from http://localhost:9090/emails/:emailId)
88+
.get(function(req, res) {
89+
getEmails(function(emails) {
90+
var emailIdToGet = +req.params.emailId,
91+
emailToGet = find(emails, function(email) {
92+
return email.id === emailIdToGet;
93+
});
94+
95+
res.json(emailToGet);
96+
});
97+
})
98+
99+
// update the email this id (accessed via PUT on http://localhost:9090/emails/:emailId)
100+
.put(function(req, res) {
101+
getEmails(function(emails) {
102+
var emailIdToUpdate = +req.params.emailId,
103+
104+
// make a new copy of the emails list, updating the appropriate email
105+
updatedEmails = emails.map(function(email) {
106+
if (email.id === emailIdToUpdate) {
107+
// make a copy of the email to update before updating
108+
return assign({}, email, {
109+
unread: !!req.body.unread
110+
});
111+
}
112+
113+
return email;
114+
});
115+
116+
saveEmails(updatedEmails, function() {
117+
res.json({success: true});
118+
});
119+
});
120+
})
121+
122+
// delete the email this id (accessed via PUT on http://localhost:9090/emails/:emailId)
123+
.delete(function(req, res) {
124+
getEmails(function(emails) {
125+
var emailIdToDelete = +req.params.emailId,
126+
127+
// make a new copy of the emails list, marking the appropriate email as deleted
128+
updatedEmails = emails.map(function(email) {
129+
if (email.id === emailIdToDelete) {
130+
// make a copy of the email to update before updating
131+
return assign({}, email, {
132+
deleted: true
133+
});
134+
}
135+
136+
return email;
137+
});
138+
139+
saveEmails(updatedEmails, function() {
140+
res.json({success: true});
141+
});
142+
});
143+
});
144+
145+
// Register the routes
146+
app.use('/', router);
147+
148+
app.get('/ping', function(req, res) {
149+
res.json({success: true});
150+
});
151+
152+
app.listen(port, function() {
153+
console.log('Server started: http://localhost:' + port + '/');
154+
});

0 commit comments

Comments
 (0)