├── .gitignore
├── assets
└── cover.png
├── vite.config.ts
├── src
├── main.tsx
├── index.css
├── favicon.svg
├── App.tsx
└── chat-client.tsx
├── index.html
├── package.json
├── tsconfig.json
├── readme.md
└── LICENSE
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | dist
4 | dist-ssr
5 | *.local
6 |
--------------------------------------------------------------------------------
/assets/cover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexkrkn/lambda-websocket-client/HEAD/assets/cover.png
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import reactRefresh from '@vitejs/plugin-react-refresh'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [reactRefresh()]
7 | })
8 |
--------------------------------------------------------------------------------
/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import './index.css'
4 | import App from './App'
5 |
6 | ReactDOM.render(
7 |
8 |
9 | ,
10 | document.getElementById('root')
11 | )
12 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite App
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | 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;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "lambda-websocket-client",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "dev": "vite",
6 | "build": "tsc && vite build",
7 | "serve": "vite preview"
8 | },
9 | "dependencies": {
10 | "@material-ui/core": "^4.11.4",
11 | "react": "^17.0.0",
12 | "react-dom": "^17.0.0"
13 | },
14 | "devDependencies": {
15 | "@types/react": "^17.0.0",
16 | "@types/react-dom": "^17.0.0",
17 | "@vitejs/plugin-react-refresh": "^1.3.1",
18 | "typescript": "^4.1.2",
19 | "vite": "^2.3.3"
20 | }
21 | }
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "lib": [
5 | "DOM",
6 | "DOM.Iterable",
7 | "ESNext"
8 | ],
9 | "types": [
10 | "vite/client"
11 | ],
12 | "allowJs": false,
13 | "skipLibCheck": false,
14 | "esModuleInterop": false,
15 | "allowSyntheticDefaultImports": true,
16 | "strict": true,
17 | "forceConsistentCasingInFileNames": true,
18 | "module": "ESNext",
19 | "moduleResolution": "Node",
20 | "resolveJsonModule": true,
21 | "isolatedModules": true,
22 | "noEmit": true,
23 | "jsx": "react"
24 | },
25 | "include": [
26 | "./src"
27 | ]
28 | }
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # Description
6 |
7 | Source code for the reactjs client from the screencast How to build a chat using Lambda + WebSocket + API Gateway? (nodejs)
8 |
9 | The backend lambda code is [here](https://github.com/alexkrkn/lambda-websocket-server)
10 |
11 | # Run
12 |
13 | - setup and run the [WebSocket](https://github.com/alexkrkn/lambda-websocket-server)
14 | - copy the websocket url as described in the video to [this constant](src/App.tsx#L4)
15 | - run `npm run dev` 🚀
16 |
17 |
18 | Don't forget to subscribe
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021-present alexkrkn
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/src/favicon.svg:
--------------------------------------------------------------------------------
1 |
16 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect, useCallback, useRef } from 'react'
2 | import { ChatClient } from './chat-client';
3 |
4 | const URL = 'ENTER_YOUR_WEBSOCKET_URL_HERE';
5 |
6 | const App = () => {
7 |
8 | const socket = useRef(null);
9 | const [isConnected, setIsConnected] = useState(false);
10 | const [members, setMembers] = useState([]);
11 | const [chatRows, setChatRows] = useState([]);
12 |
13 | const onSocketOpen = useCallback(() => {
14 | setIsConnected(true);
15 | const name = prompt('Enter your name');
16 | socket.current?.send(JSON.stringify({ action: 'setName', name }));
17 | }, []);
18 |
19 | const onSocketClose = useCallback(() => {
20 | setMembers([]);
21 | setIsConnected(false);
22 | setChatRows([]);
23 | }, []);
24 |
25 | const onSocketMessage = useCallback((dataStr) => {
26 | const data = JSON.parse(dataStr);
27 | if (data.members) {
28 | setMembers(data.members);
29 | } else if (data.publicMessage) {
30 | setChatRows(oldArray => [...oldArray, {data.publicMessage}]);
31 | } else if (data.privateMessage) {
32 | alert(data.privateMessage);
33 | } else if (data.systemMessage) {
34 | setChatRows(oldArray => [...oldArray, {data.systemMessage}]);
35 | }
36 | }, []);
37 |
38 | const onConnect = useCallback(() => {
39 | if (socket.current?.readyState !== WebSocket.OPEN) {
40 | socket.current = new WebSocket(URL);
41 | socket.current.addEventListener('open', onSocketOpen);
42 | socket.current.addEventListener('close', onSocketClose);
43 | socket.current.addEventListener('message', (event) => {
44 | onSocketMessage(event.data);
45 | });
46 | }
47 | }, []);
48 |
49 | useEffect(() => {
50 | return () => {
51 | socket.current?.close();
52 | };
53 | }, []);
54 |
55 | const onSendPrivateMessage = useCallback((to: string) => {
56 | const message = prompt('Enter private message for ' + to);
57 | socket.current?.send(JSON.stringify({
58 | action: 'sendPrivate',
59 | message,
60 | to,
61 | }));
62 | }, []);
63 |
64 | const onSendPublicMessage = useCallback(() => {
65 | const message = prompt('Enter public message');
66 | socket.current?.send(JSON.stringify({
67 | action: 'sendPublic',
68 | message,
69 | }));
70 | }, []);
71 |
72 | const onDisconnect = useCallback(() => {
73 | if (isConnected) {
74 | socket.current?.close();
75 | }
76 | }, [isConnected]);
77 |
78 | return ;
87 |
88 | }
89 |
90 | export default App
--------------------------------------------------------------------------------
/src/chat-client.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Button } from '@material-ui/core';
3 | import ListItemText from '@material-ui/core/ListItemText';
4 | import CssBaseline from '@material-ui/core/CssBaseline';
5 | import Container from '@material-ui/core/Container';
6 | import Grid from '@material-ui/core/Grid';
7 | import List from '@material-ui/core/List';
8 | import ListItem from '@material-ui/core/ListItem';
9 | import Paper from '@material-ui/core/Paper';
10 |
11 | interface Props {
12 | isConnected: boolean;
13 | members: string[];
14 | chatRows: React.ReactNode[];
15 | onPublicMessage: () => void;
16 | onPrivateMessage: (to: string) => void;
17 | onConnect: () => void;
18 | onDisconnect: () => void;
19 | }
20 |
21 | export const ChatClient = (props: Props) => {
22 | return (
23 |
31 |
32 |
33 |
34 |
35 |
36 | {props.members.map(item =>
37 | { props.onPrivateMessage(item); }} button>
38 |
39 |
40 | )}
41 |
42 |
43 |
44 |
45 |
46 |
47 |
52 | {props.chatRows.map((item, i) =>
53 | - {item}
54 | )}
55 |
56 |
57 |
58 | {props.isConnected && }
59 | {props.isConnected && }
60 | {!props.isConnected && }
61 |
62 |
63 |
72 |
73 |
74 |
75 |
76 |
77 | )
78 | };
--------------------------------------------------------------------------------