├── .eslintrc.json
├── components
├── InputArea
│ ├── index.js
│ └── InputArea.js
├── RoomList
│ ├── index.js
│ └── RoomList.js
├── ConversationBar
│ ├── index.js
│ └── ConversationBar.js
└── Message
│ ├── index.js
│ ├── MessageList.js
│ └── MessageItem.js
├── next.config.js
├── public
├── favicon.ico
└── vercel.svg
├── pages
├── api
│ └── hello.js
├── _app.js
├── index.js
└── rooms
│ └── [roomId].js
├── CODE_OF_CONDUCT.md
├── package.json
├── styles
├── globals.css
└── Home.module.css
├── src
└── sample.aws-exports.js
├── LICENSE
├── .gitignore
├── README.md
└── CONTRIBUTING.md
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/components/InputArea/index.js:
--------------------------------------------------------------------------------
1 | export { InputArea } from './InputArea'
2 |
--------------------------------------------------------------------------------
/components/RoomList/index.js:
--------------------------------------------------------------------------------
1 | export { RoomList } from './RoomList'
2 |
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | reactStrictMode: true,
3 | }
4 |
--------------------------------------------------------------------------------
/components/ConversationBar/index.js:
--------------------------------------------------------------------------------
1 | export { ConversationBar } from './ConversationBar'
2 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/amplify-nextjs-chat-app/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/components/Message/index.js:
--------------------------------------------------------------------------------
1 | export { MessageItem } from './MessageItem'
2 | export { MessageList } from './MessageList'
3 |
--------------------------------------------------------------------------------
/pages/api/hello.js:
--------------------------------------------------------------------------------
1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction
2 |
3 | export default function handler(req, res) {
4 | res.status(200).json({ name: 'John Doe' })
5 | }
6 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | ## Code of Conduct
2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
4 | opensource-codeofconduct@amazon.com with any additional questions or comments.
5 |
--------------------------------------------------------------------------------
/pages/_app.js:
--------------------------------------------------------------------------------
1 | import { Amplify } from 'aws-amplify'
2 | import { AmplifyProvider } from '@aws-amplify/ui-react'
3 | import config from '../src/aws-exports'
4 | import '@aws-amplify/ui-react/styles.css'
5 |
6 | Amplify.configure({ ...config, ssr: true })
7 | function MyApp({ Component, pageProps }) {
8 | return (
9 |
10 |
11 |
12 | )
13 | }
14 |
15 | export default MyApp
16 |
--------------------------------------------------------------------------------
/components/Message/MessageList.js:
--------------------------------------------------------------------------------
1 | import { Flex } from '@aws-amplify/ui-react'
2 | import { MessageItem } from './index'
3 |
4 | export const MessageList = ({ messages = [], myUsername }) => {
5 | return (
6 |
13 | {messages.map((msg) => (
14 |
15 | ))}
16 |
17 | )
18 | }
19 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "appsync-chat-app",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@aws-amplify/ui-react": "^3.0.4",
13 | "aws-amplify": "^4.3.27",
14 | "next": "13.5.4",
15 | "react": "18.2.0",
16 | "react-dom": "18.2.0",
17 | "react-infinite-scroller": "^1.2.6"
18 | },
19 | "devDependencies": {
20 | "eslint": "8.19.0",
21 | "eslint-config-next": "12.2.2"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/styles/globals.css:
--------------------------------------------------------------------------------
1 | html,
2 | #__next,
3 | body {
4 | padding: 0;
5 | margin: 0;
6 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
7 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
8 | }
9 |
10 | a {
11 | color: inherit;
12 | text-decoration: none;
13 | }
14 |
15 | * {
16 | box-sizing: border-box;
17 | }
18 |
19 | /* styles.css */
20 |
21 | .amplify-textareafield {
22 | --amplify-components-fieldcontrol-color: black;
23 | --amplify-components-fieldcontrol-border-color: rgba(255, 0, 0, 0);
24 | --amplify-components-fieldcontrol-focus-border-color: rgba(0, 0, 255, 0);
25 | }
26 |
--------------------------------------------------------------------------------
/src/sample.aws-exports.js:
--------------------------------------------------------------------------------
1 | // note that Auth is top-level, whereas the API stuff is not
2 | const awsmobile = {
3 | Auth: {
4 | region: 'your-region',
5 | userPoolId: 'your-region_YOUR_REGION_ID',
6 | userPoolWebClientId: '6vstsuciYOURCLIENTID',
7 | identityPoolId: 'identitypoolID',
8 |
9 | },
10 | Storage: {
11 | AWSS3: {
12 | bucket: 'bucket-name',
13 | region: 'your-region',
14 | },
15 | },
16 | aws_project_region: 'your-region',
17 | aws_appsync_graphqlEndpoint:
18 | 'https://your-api.appsync-api.your-region.amazonaws.com/graphql',
19 | aws_appsync_region: 'your-region',
20 | aws_appsync_authenticationType: 'AMAZON_COGNITO_USER_POOLS',
21 | }
22 |
23 | export default awsmobile
24 |
--------------------------------------------------------------------------------
/components/RoomList/RoomList.js:
--------------------------------------------------------------------------------
1 | import {
2 | Table,
3 | TableBody,
4 | TableCell,
5 | TableHead,
6 | TableRow,
7 | View,
8 | } from '@aws-amplify/ui-react'
9 |
10 | export const RoomList = ({ handleMenuToggle, rooms = [] }) => {
11 | return (
12 |
13 |
14 |
15 |
16 | Application Rooms
17 |
18 |
19 |
20 | {rooms.map((room) => (
21 | {
24 | console.log(room.id)
25 | handleMenuToggle(room.id)
26 | }}
27 | >
28 | {room.name}
29 |
30 | ))}
31 |
32 |
33 |
34 | )
35 | }
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of
4 | this software and associated documentation files (the "Software"), to deal in
5 | the Software without restriction, including without limitation the rights to
6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7 | the Software, and to permit persons to whom the Software is furnished to do so.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
10 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
11 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
13 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
14 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
15 |
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # local env files
28 | .env.local
29 | .env.development.local
30 | .env.test.local
31 | .env.production.local
32 |
33 | # vercel
34 | .vercel
35 |
36 | #amplify-do-not-edit-begin
37 | amplify/\#current-cloud-backend
38 | amplify/.config/local-*
39 | amplify/logs
40 | amplify/mock-data
41 | amplify/backend/amplify-meta.json
42 | amplify/backend/.temp
43 | build/
44 | dist/
45 | node_modules/
46 | aws-exports.js
47 | awsconfiguration.json
48 | amplifyconfiguration.json
49 | amplifyconfiguration.dart
50 | amplify-build-config.json
51 | amplify-gradle-config.json
52 | amplifytools.xcconfig
53 | .secret-*
54 | **.sample
55 | #amplify-do-not-edit-end
56 |
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/components/ConversationBar/ConversationBar.js:
--------------------------------------------------------------------------------
1 | import { Flex, Menu, useBreakpointValue } from '@aws-amplify/ui-react'
2 | import { useRouter } from 'next/router'
3 | import { useState } from 'react'
4 | import { RoomList } from '../RoomList'
5 |
6 | export const ConversationBar = ({ rooms = [], onRoomChange }) => {
7 | const [isMenuOpen, setIsMenuOpen] = useState(false)
8 | const variation = useBreakpointValue({
9 | base: 'isMobile',
10 | medium: 'isTabletOrHigher',
11 | })
12 | const toggleMenu = (roomId) => {
13 | setIsMenuOpen(false)
14 | onRoomChange(roomId)
15 | }
16 |
17 | const ConversationDisplay = ({ rooms = [] }) => {
18 | if (variation === 'isMobile') {
19 | return (
20 |
21 |
30 |
31 | )
32 | } else if (variation === 'isTabletOrHigher') {
33 | return
34 | }
35 | }
36 | return
37 | }
38 |
--------------------------------------------------------------------------------
/components/InputArea/InputArea.js:
--------------------------------------------------------------------------------
1 | import {
2 | Button,
3 | Flex,
4 | TextAreaField,
5 | TextField,
6 | View,
7 | } from '@aws-amplify/ui-react'
8 | import { Storage } from 'aws-amplify'
9 | import { useState } from 'react'
10 |
11 | export const InputArea = ({ onMessageSend }) => {
12 | const [selectedImage, setSelectedImage] = useState(null)
13 | const [messageText, setMessageText] = useState('')
14 |
15 | const uploadFile = async (selectedPic) => {
16 | const { key } = await Storage.put(selectedPic.name, selectedPic, {
17 | contentType: selectedPic.type,
18 | })
19 |
20 | return key
21 | }
22 |
23 | const handleFormSubmit = async (e) => {
24 | e.preventDefault()
25 | let key
26 | if (selectedImage) {
27 | key = await uploadFile(selectedImage)
28 | }
29 |
30 | onMessageSend(messageText, key)
31 | setMessageText('')
32 | }
33 | return (
34 |
40 |
41 |
61 |
62 |
63 | )
64 | }
65 |
--------------------------------------------------------------------------------
/pages/index.js:
--------------------------------------------------------------------------------
1 | import {
2 | Button,
3 | Flex,
4 | Link,
5 | Text,
6 | TextField,
7 | View,
8 | withAuthenticator,
9 | } from '@aws-amplify/ui-react'
10 | import { API } from 'aws-amplify'
11 | import { useEffect, useState } from 'react'
12 | import { listRooms } from '../src/graphql/queries'
13 | import { createRoom } from '../src/graphql/mutations'
14 | import NextLink from 'next/link'
15 | import config from '../src/aws-exports'
16 | import { Amplify } from 'aws-amplify'
17 |
18 | Amplify.configure({ ...config, ssr: true })
19 |
20 | function Home({ signOut, user }) {
21 | const [rooms, setRooms] = useState([])
22 | const [roomName, setRoomName] = useState('')
23 |
24 | useEffect(() => {
25 | API.graphql({
26 | query: listRooms,
27 | }).then(({ data }) => {
28 | setRooms(data.listRooms.items)
29 | })
30 | }, [])
31 |
32 | const handleSubmit = async (e) => {
33 | e.preventDefault()
34 | const { data } = await API.graphql({
35 | query: createRoom,
36 | variables: {
37 | input: {
38 | name: roomName,
39 | },
40 | },
41 | })
42 |
43 | setRooms([...rooms, data.createRoom])
44 | }
45 |
46 | return (
47 |
48 |
49 |
50 |
51 |
52 | Hey, {user.username}! Select a room to chat in or create your own public
53 | room.
54 |
55 |
63 |
64 | {rooms.map((room) => (
65 | -
66 |
67 | {room.name}
68 |
69 |
70 | ))}
71 |
72 |
73 | )
74 | }
75 |
76 | export default withAuthenticator(Home, {
77 | signUpAttributes: ['email', 'given_name', 'family_name'],
78 | })
79 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Modern Chat app Frontend
2 |
3 | 🚨 Note that this project does not create our backend services via the Amplify CLI, but relies on exported values from the AWS CDK.
4 |
5 | 🚨 This application is part of a blog post that explains both the frontend and the backend as a whole. The backend repo can be found [here](https://github.com/Focus-Otter/fullstack-cdk-helpers/blob/main/README.md)
6 |
7 | 
8 |
9 | ## Overview
10 |
11 | This repo is the frontend to building a fullstack chat app. The backend can be found [here](https://github.com/Focus-Otter/chat-cdk-backend).
12 |
13 | 
14 |
15 | ## Tech Stack
16 |
17 | - React Framework: NextJS
18 | - UI Library: AWS Amplify UI primitives
19 | - API: GraphQL via AWS AppSync
20 | - File uploads: Sent to Amazon S3
21 | - Signup/SignIn: Managed with Cognito
22 | - Backend Binding: amplify-js
23 |
24 | ## Steps to get started
25 |
26 | Once [the backend ](https://github.com/Focus-Otter/fullstack-cdk-helpers/blob/main/README.md) is deployed, it will output a set of values. The outputted values are what you'll need to get this project working.
27 |
28 | 1. Run `amplify init`
29 | 2. Run `amplify add codegen --apiId YOUR_APPID` (value generated from the backend)
30 | 3. `amplify codegen` (accept the defaults, but set the max-depth to 4)
31 | 4. Create a `src/aws-exports.js` file and bring over the values from your CDK backend and ensure your project looks like the `sample.aws-exports.js` file.
32 | 5. Run the app and create 2 users
33 | 6. Once signed in, create a room.
34 | 7. You should now be able to view the rooms on the homepage. Click one and begin creating messages.
35 |
36 | ## Security
37 |
38 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information.
39 |
40 | ## License
41 |
42 | This library is licensed under the MIT-0 License. See the LICENSE file.
43 |
--------------------------------------------------------------------------------
/components/Message/MessageItem.js:
--------------------------------------------------------------------------------
1 | import {
2 | Card,
3 | Flex,
4 | Heading,
5 | Image,
6 | Text,
7 | useTheme,
8 | View,
9 | } from '@aws-amplify/ui-react'
10 | import { Storage } from 'aws-amplify'
11 | import { useEffect, useState } from 'react'
12 |
13 | export const MessageItem = ({ msg = {}, myUsername }) => {
14 | const { tokens } = useTheme()
15 | if (msg.content.imageId) {
16 | // console.log('the message', msg)
17 | }
18 | const isMyMsg = msg.owner === myUsername
19 | const isEdited = msg.createdAt !== msg.updatedAt
20 |
21 | return (
22 |
29 |
30 |
37 |
38 |
39 |
40 |
41 | {msg.owner}{' '}
42 |
48 | {msg.createdAt}
49 |
50 |
51 |
52 |
53 |
58 |
59 |
60 |
61 |
62 | )
63 | }
64 |
65 | const TextMessage = ({ isMyMsg, msgContent, isEdited }) => {
66 | return (
67 |
68 | {msgContent}{' '}
69 |
70 | )
71 | }
72 |
73 | const PicMessage = ({ msgContent }) => {
74 | const [picUrl, setPicUrl] = useState('')
75 | console.log(msgContent)
76 | useEffect(() => {
77 | Storage.get(msgContent).then((url) => {
78 | console.log(url)
79 | setPicUrl(url)
80 | })
81 | }, [msgContent])
82 | return
83 | }
84 |
--------------------------------------------------------------------------------
/styles/Home.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | min-height: 100vh;
3 | padding: 0 0.5rem;
4 | display: flex;
5 | flex-direction: column;
6 | justify-content: center;
7 | align-items: center;
8 | height: 100vh;
9 | }
10 |
11 | .main {
12 | padding: 5rem 0;
13 | flex: 1;
14 | display: flex;
15 | flex-direction: column;
16 | justify-content: center;
17 | align-items: center;
18 | }
19 |
20 | .footer {
21 | width: 100%;
22 | height: 100px;
23 | border-top: 1px solid #eaeaea;
24 | display: flex;
25 | justify-content: center;
26 | align-items: center;
27 | }
28 |
29 | .footer a {
30 | display: flex;
31 | justify-content: center;
32 | align-items: center;
33 | flex-grow: 1;
34 | }
35 |
36 | .title a {
37 | color: #0070f3;
38 | text-decoration: none;
39 | }
40 |
41 | .title a:hover,
42 | .title a:focus,
43 | .title a:active {
44 | text-decoration: underline;
45 | }
46 |
47 | .title {
48 | margin: 0;
49 | line-height: 1.15;
50 | font-size: 4rem;
51 | }
52 |
53 | .title,
54 | .description {
55 | text-align: center;
56 | }
57 |
58 | .description {
59 | line-height: 1.5;
60 | font-size: 1.5rem;
61 | }
62 |
63 | .code {
64 | background: #fafafa;
65 | border-radius: 5px;
66 | padding: 0.75rem;
67 | font-size: 1.1rem;
68 | font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
69 | Bitstream Vera Sans Mono, Courier New, monospace;
70 | }
71 |
72 | .grid {
73 | display: flex;
74 | align-items: center;
75 | justify-content: center;
76 | flex-wrap: wrap;
77 | max-width: 800px;
78 | margin-top: 3rem;
79 | }
80 |
81 | .card {
82 | margin: 1rem;
83 | padding: 1.5rem;
84 | text-align: left;
85 | color: inherit;
86 | text-decoration: none;
87 | border: 1px solid #eaeaea;
88 | border-radius: 10px;
89 | transition: color 0.15s ease, border-color 0.15s ease;
90 | width: 45%;
91 | }
92 |
93 | .card:hover,
94 | .card:focus,
95 | .card:active {
96 | color: #0070f3;
97 | border-color: #0070f3;
98 | }
99 |
100 | .card h2 {
101 | margin: 0 0 1rem 0;
102 | font-size: 1.5rem;
103 | }
104 |
105 | .card p {
106 | margin: 0;
107 | font-size: 1.25rem;
108 | line-height: 1.5;
109 | }
110 |
111 | .logo {
112 | height: 1em;
113 | margin-left: 0.5rem;
114 | }
115 |
116 | @media (max-width: 600px) {
117 | .grid {
118 | width: 100%;
119 | flex-direction: column;
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing Guidelines
2 |
3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional
4 | documentation, we greatly value feedback and contributions from our community.
5 |
6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary
7 | information to effectively respond to your bug report or contribution.
8 |
9 |
10 | ## Reporting Bugs/Feature Requests
11 |
12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features.
13 |
14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already
15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful:
16 |
17 | * A reproducible test case or series of steps
18 | * The version of our code being used
19 | * Any modifications you've made relevant to the bug
20 | * Anything unusual about your environment or deployment
21 |
22 |
23 | ## Contributing via Pull Requests
24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that:
25 |
26 | 1. You are working against the latest source on the *main* branch.
27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already.
28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted.
29 |
30 | To send us a pull request, please:
31 |
32 | 1. Fork the repository.
33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change.
34 | 3. Ensure local tests pass.
35 | 4. Commit to your fork using clear commit messages.
36 | 5. Send us a pull request, answering any default questions in the pull request interface.
37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation.
38 |
39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and
40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/).
41 |
42 |
43 | ## Finding contributions to work on
44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start.
45 |
46 |
47 | ## Code of Conduct
48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
50 | opensource-codeofconduct@amazon.com with any additional questions or comments.
51 |
52 |
53 | ## Security issue notifications
54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue.
55 |
56 |
57 | ## Licensing
58 |
59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution.
60 |
--------------------------------------------------------------------------------
/pages/rooms/[roomId].js:
--------------------------------------------------------------------------------
1 | import { Flex, Heading, useTheme, View } from '@aws-amplify/ui-react'
2 | import { useEffect, useState } from 'react'
3 | import { withSSRContext } from 'aws-amplify'
4 | import { InputArea } from '../../components/InputArea'
5 | import { MessageList } from '../../components/Message'
6 | import { ConversationBar } from '../../components/ConversationBar'
7 | import config from '../../src/aws-exports'
8 | import { Amplify, API } from 'aws-amplify'
9 | import { listMessagesForRoom, listRooms } from '../../src/graphql/queries'
10 | import { createMessage } from '../../src/graphql/mutations'
11 | import { onCreateMessageByRoomId } from '../../src/graphql/subscriptions'
12 | import { useRouter } from 'next/router'
13 | Amplify.configure({ ...config, ssr: true })
14 |
15 | function RoomPage({ roomsList, currentRoomData, username }) {
16 | console.log(username)
17 | const { tokens } = useTheme()
18 | const router = useRouter()
19 | const [messages, setMessages] = useState([])
20 | const [rooms, setRooms] = useState(roomsList)
21 | const [currentRoom, setCurrentRoom] = useState(currentRoomData)
22 |
23 | const handleMessageSend = async (newMessage, imgKey) => {
24 | const createNewMsg = async (text, imageId) => {
25 | let content = { text, imageId }
26 | return await API.graphql({
27 | query: createMessage,
28 | variables: {
29 | input: {
30 | content,
31 | roomId: currentRoom.id,
32 | },
33 | },
34 | })
35 | }
36 | if (newMessage && !imgKey) {
37 | createNewMsg(newMessage).then(({ data }) =>
38 | setMessages([data.createMessage, ...messages])
39 | )
40 | } else if (!newMessage && imgKey) {
41 | console.log('the imgkey', imgKey)
42 | createNewMsg(undefined, imgKey).then(({ data }) =>
43 | setMessages([data.createMessage, ...messages])
44 | )
45 | } else if (newMessage && imgKey) {
46 | createNewMsg(newMessage, imgKey).then(({ data }) =>
47 | setMessages([data.createMessage, ...messages])
48 | )
49 | }
50 | }
51 |
52 | const handleRoomChange = (roomID) => {
53 | const newRoom = rooms.find((room) => room.id === roomID)
54 | setCurrentRoom(newRoom)
55 | router.push(`/rooms/${roomID}`)
56 | }
57 |
58 | useEffect(() => {
59 | API.graphql({
60 | query: listMessagesForRoom,
61 | variables: {
62 | roomId: currentRoom.id,
63 | sortDirection: 'DESC',
64 | },
65 | }).then(({ data }) => setMessages(data.listMessagesForRoom.items))
66 | }, [currentRoom.id])
67 |
68 | useEffect(() => {
69 | const subscription = API.graphql({
70 | query: onCreateMessageByRoomId,
71 | variables: { roomId: currentRoom.id },
72 | }).subscribe({
73 | next: ({ value }) => {
74 | if (value.data.onCreateMessageByRoomId.owner !== username) {
75 | console.log(value.data.onCreateMessageByRoomId)
76 | setMessages((currMsgs) => [
77 | value.data.onCreateMessageByRoomId,
78 | ...currMsgs,
79 | ])
80 | }
81 | },
82 | })
83 |
84 | return () => subscription.unsubscribe()
85 | }, [currentRoom.id, username])
86 |
87 | return (
88 | <>
89 |
90 |
91 |
92 |
93 |
94 |
101 | {currentRoom.name}
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 | >
112 | )
113 | }
114 |
115 | export default RoomPage
116 |
117 | export async function getServerSideProps(context) {
118 | const { API, Auth } = withSSRContext(context)
119 | try {
120 | const user = await Auth.currentAuthenticatedUser()
121 | const { data } = await API.graphql({
122 | query: listRooms,
123 | })
124 |
125 | const currentRoomData = data.listRooms.items.find(
126 | (room) => room.id === context.params.roomId
127 | )
128 |
129 | return {
130 | props: {
131 | currentRoomData,
132 | username: user.username,
133 | roomsList: data.listRooms.items,
134 | },
135 | }
136 | } catch (err) {
137 | return {
138 | redirect: {
139 | destination: '/',
140 | permanent: false,
141 | },
142 | }
143 | }
144 | }
145 |
--------------------------------------------------------------------------------