Skip to content

Commit 2cf811f

Browse files
committed
wip
fix cors react logic implemented chatmessage fmt refactored
1 parent 26961e1 commit 2cf811f

11 files changed

Lines changed: 172 additions & 33 deletions

File tree

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,9 @@ run outside docker
3131
## References
3232

3333
https://medium.com/hacktive-devs/building-the-backend-of-chat-applications-with-spring-webflux-and-reactive-mongodb-26347a1ddce4
34+
35+
## TODO's
36+
37+
- Proper Input Validation on the react client side
38+
- Generally a lot of error handling on the react side (EventSource onError - fetch errors etc, check HTTP status codes )
39+
- Monitoring of the springboot app (actuator)

backend/src/main/java/com/ssechat/controller/ChatMessageController.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010

1111
import javax.validation.Valid;
1212

13+
@CrossOrigin(value = { "*" },
14+
maxAge = 900
15+
)
1316
@RestController
1417
public class ChatMessageController {
1518
@Autowired
Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,25 @@
11
package com.ssechat.model;
22

3+
import org.springframework.data.annotation.CreatedDate;
34
import org.springframework.data.annotation.Id;
45
import org.springframework.data.mongodb.core.mapping.Document;
56

7+
import java.time.Instant;
8+
import java.util.Date;
9+
610
@Document(collection = "chatmessages")
711
public class ChatMessage {
812
@Id
913
private String id;
1014
private String message;
11-
private String sender;
12-
private String recipient;
15+
@CreatedDate
16+
private Instant createdDate = Instant.now();
1317
private String channelId;
1418

15-
public ChatMessage(String message, String sender, String recipient, String channelId) {
19+
public ChatMessage(String id, String message, String channelId) {
1620
this.message = message;
17-
this.sender = sender;
18-
this.recipient = recipient;
1921
this.channelId = channelId;
22+
this.id = id;
2023
}
2124

2225
public String getMessage() {
@@ -27,27 +30,27 @@ public void setMessage(String message) {
2730
this.message = message;
2831
}
2932

30-
public String getSender() {
31-
return sender;
33+
public String getChannelId() {
34+
return channelId;
3235
}
3336

34-
public void setSender(String sender) {
35-
this.sender = sender;
37+
public void setChannelId(String channelId) {
38+
this.channelId = channelId;
3639
}
3740

38-
public String getRecipient() {
39-
return recipient;
41+
public String getId() {
42+
return id;
4043
}
4144

42-
public void setRecipient(String recipient) {
43-
this.recipient = recipient;
45+
public void setId(String id) {
46+
this.id = id;
4447
}
4548

46-
public String getChannelId() {
47-
return channelId;
49+
public Instant getCreatedDate() {
50+
return createdDate;
4851
}
4952

50-
public void setChannelId(String channelId) {
51-
this.channelId = channelId;
53+
public void setCreatedDate(Instant createdDate) {
54+
this.createdDate = createdDate;
5255
}
5356
}

client/src/App.css

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
}
44

55
.App-logo {
6-
height: 40vmin;
6+
height: 4vmin;
77
pointer-events: none;
88
}
99

@@ -15,7 +15,7 @@
1515

1616
.App-header {
1717
background-color: #282c34;
18-
min-height: 100vh;
18+
min-height: 10vh;
1919
display: flex;
2020
flex-direction: column;
2121
align-items: center;

client/src/App.tsx

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,16 @@
1-
import React from 'react';
2-
import logo from './logo.svg';
3-
import './App.css';
1+
import React from "react";
2+
import logo from "./logo.svg";
3+
import "./App.css";
4+
import { ChatList, ChatForm } from "./components";
45

56
function App() {
67
return (
78
<div className="App">
89
<header className="App-header">
910
<img src={logo} className="App-logo" alt="logo" />
10-
<p>
11-
Edit <code>src/App.tsx</code> and save to reload.
12-
</p>
13-
<a
14-
className="App-link"
15-
href="https://reactjs.org"
16-
target="_blank"
17-
rel="noopener noreferrer"
18-
>
19-
Learn React
20-
</a>
2111
</header>
12+
<ChatList channelId={1} />
13+
<ChatForm channelId={1} />
2214
</div>
2315
);
2416
}

client/src/components/ChatForm.tsx

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import React, { ChangeEvent, KeyboardEvent, MouseEvent } from "react";
2+
import { addMessage } from "../lib/api";
3+
4+
interface ChatFormProps {
5+
channelId: number;
6+
}
7+
8+
const ChatForm = ({ channelId }: ChatFormProps) => {
9+
const [message, setMessage] = React.useState("");
10+
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
11+
setMessage(event.target.value);
12+
};
13+
14+
const sendMessage = () => {
15+
if (message !== "") {
16+
addMessage({ channelId, message });
17+
setMessage("");
18+
}
19+
};
20+
21+
const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
22+
if (e.key === "Enter") {
23+
sendMessage();
24+
}
25+
};
26+
27+
const handleSubmit = (e: MouseEvent) => {
28+
e.preventDefault();
29+
sendMessage();
30+
};
31+
32+
return (
33+
<form>
34+
<input
35+
id="message"
36+
type="text"
37+
placeholder="Please Enter"
38+
value={message}
39+
onKeyDown={handleKeyDown}
40+
onChange={handleChange}
41+
/>
42+
43+
<button onClick={handleSubmit} type="submit">
44+
Send
45+
</button>
46+
</form>
47+
);
48+
};
49+
50+
export default ChatForm;

client/src/components/ChatList.tsx

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import React, { Component } from "react";
2+
import { ChatMessage } from "../lib/types";
3+
import { ChatRow } from "./";
4+
5+
interface ChatListState {
6+
messages: ChatMessage[];
7+
}
8+
9+
interface ChatListProps {
10+
channelId: number;
11+
}
12+
13+
const url = process.env.API_BASE_URL
14+
? process.env.API_BASE_URL
15+
: "http://localhost:8080";
16+
17+
class ChatList extends Component<ChatListProps, ChatListState> {
18+
state: ChatListState = { messages: [] };
19+
20+
// After the component did mount, we set the state each second.
21+
componentDidMount() {
22+
const source = new EventSource(
23+
`${url}/chats/stream?channelId=${this.props.channelId}`
24+
);
25+
26+
source.onmessage = (event) => {
27+
const messages = this.state.messages.concat(JSON.parse(event.data));
28+
this.setState({ messages });
29+
};
30+
}
31+
32+
render() {
33+
const list = this.state.messages.map((row) => {
34+
return <ChatRow key={row.id} message={row} />;
35+
});
36+
37+
return <ul>{list}</ul>;
38+
}
39+
}
40+
export default ChatList;

client/src/components/ChatRow.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import React from "react";
2+
import { ChatMessage } from "../lib/types";
3+
4+
interface ChatRowProps {
5+
message: ChatMessage;
6+
}
7+
8+
const ChatRow = ({ message }: ChatRowProps) => {
9+
return (
10+
<li>
11+
{message.createdDate && new Date(message.createdDate).toLocaleString()}
12+
{message.message}
13+
</li>
14+
);
15+
};
16+
17+
export default ChatRow;

client/src/components/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export { default as ChatForm } from "./ChatForm";
2+
export { default as ChatList } from "./ChatList";
3+
export { default as ChatRow } from "./ChatRow";

client/src/lib/api.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { ChatMessage } from "./types";
2+
3+
const url = process.env.API_BASE_URL
4+
? process.env.API_BASE_URL
5+
: "http://localhost:8080";
6+
7+
export async function addMessage(item: ChatMessage) {
8+
const data = await fetch(`${url}/chats`, {
9+
method: "POST",
10+
headers: {
11+
"Content-Type": "application/json",
12+
},
13+
body: JSON.stringify(item),
14+
});
15+
return data.status;
16+
}

0 commit comments

Comments
 (0)