From ae0106b8d13954a63d04fc780e0ea0a004ca86cd Mon Sep 17 00:00:00 2001 From: Guillermo Zevallos Date: Tue, 27 Dec 2022 19:25:12 -0500 Subject: [PATCH 01/20] added sendmessage with formik and fetch --- client/src/App.tsx | 3 ++ client/src/components/Board.tsx | 2 +- client/src/components/SendMessage.tsx | 67 +++++++++++++++++++++++++++ client/src/service/message.ts | 24 ++++++++-- 4 files changed, 91 insertions(+), 5 deletions(-) create mode 100644 client/src/components/SendMessage.tsx diff --git a/client/src/App.tsx b/client/src/App.tsx index d93e2ed..ebb4232 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1,6 +1,7 @@ import { useState } from 'react' import { GlobalStyle } from './theme/GlobalStyle' import Board from './components/Board' +import SendMessage from './components/SendMessage' function App() { @@ -9,6 +10,8 @@ function App() {

Mini Message Board

+ + ) } diff --git a/client/src/components/Board.tsx b/client/src/components/Board.tsx index d429a20..eb297f7 100644 --- a/client/src/components/Board.tsx +++ b/client/src/components/Board.tsx @@ -7,7 +7,7 @@ const Board = () => { useEffect(() => { getMessage().then((data)=>setMessages(data)) - console.log(messages); + console.log(messages,33); }, []) diff --git a/client/src/components/SendMessage.tsx b/client/src/components/SendMessage.tsx new file mode 100644 index 0000000..043fa28 --- /dev/null +++ b/client/src/components/SendMessage.tsx @@ -0,0 +1,67 @@ +import { Formik, Field, Form } from "formik"; +import { FormikHelpers } from "formik/dist/types"; +import { createMessage } from "../service/message"; + +type FormValues = { + username: string; + text: string; +}; + +const SendMessage = () => { + const initialValues: FormValues = { username: "", text: "" }; + + const handleSubmit = async ( + values: FormValues, + actions: FormikHelpers + ) => { + await createMessage(values) + .then(res => console.log(res)) + .catch(err => console.log(err)); + }; + + return ( +
+

Formi

+ { + const errors: { username?: string, text?:string } = {}; + + if (!values.username) { + errors.username = "Required"; + } + + if (!values.text) { + errors.text = "Required"; + } + + return errors; + }} + onSubmit={handleSubmit}> + {({ isSubmitting, errors }) => ( +
+ + {errors.username ?
{errors.username}
: null} + + {errors.text ?
{errors.text}
: null} + + + )} +
+
+ ); +}; + +export default SendMessage; diff --git a/client/src/service/message.ts b/client/src/service/message.ts index 34abf48..d6df902 100644 --- a/client/src/service/message.ts +++ b/client/src/service/message.ts @@ -1,8 +1,24 @@ import { Message } from "../types/messageType"; -export const getMessage = async() => { - const res = await fetch('https://mini-message-board-server.onrender.com/new'); - const data = await res.json(); - +export const getMessage = async () => { + const res = await fetch('https://mini-message-board-server.onrender.com/new'); + const data = await res.json(); + return [...data.messages] as Message[] } + +export const createMessage = async (data: Omit) => { + + const res = await fetch('https://mini-message-board-server.onrender.com/new', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(data) + }); + const json = await res.json(); + + return json; +} + + From 5f9ef657e08c2699f32e2ecc0f37f258826c319c Mon Sep 17 00:00:00 2001 From: Guillermo Zevallos Date: Wed, 4 Jan 2023 18:25:41 -0500 Subject: [PATCH 02/20] Added fonts --- client/index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/client/index.html b/client/index.html index e0d1c84..a38bab6 100644 --- a/client/index.html +++ b/client/index.html @@ -5,6 +5,7 @@ Vite + React + TS +
From ad2b5d83e4091b20c5f74549b5d113edd2a55c4e Mon Sep 17 00:00:00 2001 From: Guillermo Zevallos Date: Wed, 4 Jan 2023 18:26:35 -0500 Subject: [PATCH 03/20] Added globalstyles --- client/src/theme/GlobalStyle.tsx | 50 ++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/client/src/theme/GlobalStyle.tsx b/client/src/theme/GlobalStyle.tsx index b9ee507..04aabb1 100644 --- a/client/src/theme/GlobalStyle.tsx +++ b/client/src/theme/GlobalStyle.tsx @@ -1,22 +1,40 @@ import { createGlobalStyle } from "styled-components"; - +export const palette = ["#FACCAA", "#A1BCE7", "#9ED9B3", "#DAB3B4", "#AAA2C9"]; export const GlobalStyle = createGlobalStyle` + :root{ + --white-bg: #F2F3F7; + --white: #FFFFFF; + --dark: #00132B; + } - * { - box-sizing: border-box; - line-height: normal; - } - - body { - height: 100vh; - overflow-x: hidden; - -webkit-text-size-adjust: 100%; - -webkit-font-smoothing: antialiased; - font-family: 'Outfit', sans-serif; - font-size: 14px; - line-height: 17.64px; - } + * { + padding: 0; + margin: 0; + box-sizing: border-box; + line-height: normal; + } + + body { + min-height: 100vh; + overflow-x: hidden; + -webkit-text-size-adjust: 100%; + -webkit-font-smoothing: antialiased; + font-family: 'Outfit', sans-serif; + font-size: 14px; + line-height: 17.64px; + background-color: var(--dark); + font-family: 'Lato', sans-serif; + display: flex; + align-items: center; + justify-content: center; + } + + h1,h2,h3,h4,h5,h6{ + /* color: #6F718A; */ + font-family: 'Syne', sans-serif; + } + -`; \ No newline at end of file +`; From 07548e47dbf461ddc971eb9fa222787d3b7477eb Mon Sep 17 00:00:00 2001 From: Guillermo Zevallos Date: Wed, 4 Jan 2023 18:26:52 -0500 Subject: [PATCH 04/20] Created components --- client/src/components/Button.tsx | 46 +++++++++++++ client/src/components/Card.tsx | 80 ++++++++++++++++++++++ client/src/components/Input.tsx | 74 ++++++++++++++++++++ client/src/components/Loading.tsx | 108 ++++++++++++++++++++++++++++++ 4 files changed, 308 insertions(+) create mode 100644 client/src/components/Button.tsx create mode 100644 client/src/components/Card.tsx create mode 100644 client/src/components/Input.tsx create mode 100644 client/src/components/Loading.tsx diff --git a/client/src/components/Button.tsx b/client/src/components/Button.tsx new file mode 100644 index 0000000..91c6639 --- /dev/null +++ b/client/src/components/Button.tsx @@ -0,0 +1,46 @@ +import styled from "styled-components"; + +type ButtonProps = { + type: "button" | "submit" | "reset" | undefined; + disabled: boolean | undefined; +}; + +const Button = ({ type, disabled }: ButtonProps) => { + return ( + + + + + + + ); +}; + +const StyledButton = styled.button` + background: none; + border: none; + padding: 15px 15px; + border-radius: 10px; + background-color: black; + + &:hover { + background: rgba(116, 77, 77, 0.774); + transition: 0.5s; + } + + & svg { + color: #fff; + } +`; + +export default Button; diff --git a/client/src/components/Card.tsx b/client/src/components/Card.tsx new file mode 100644 index 0000000..dffcfb3 --- /dev/null +++ b/client/src/components/Card.tsx @@ -0,0 +1,80 @@ +import styled from "styled-components"; + +type Cardprops = { + children: JSX.Element | JSX.Element[]; +}; + +const Card = ({ children }: Cardprops) => { + return ( + +
+
+ +
+
+ +
+
+ +
+
+
{children}
+
+ ); +}; + +const StyledCard = styled.div` + width: 80vw; + height: 90vh; + position: relative; + margin: 0 auto; + background-color: #f8fbfe; + border-radius: 8px; + z-index: 1; + + .tools { + position: absolute; + border-radius: 8px 8px 0 0 ; + background: white; + width: 100%; + display: flex; + align-items: center; + padding: 9px; + .circle { + padding: 0 4px; + } + + .box { + display: inline-block; + align-items: center; + width: 10px; + height: 10px; + padding: 1px; + border-radius: 50%; + } + + .red { + background-color: #ff605c; + } + + .yellow { + background-color: #ffbd44; + } + + .green { + background-color: #00ca4e; + } + } + + .card__content { + padding: 2.5rem 9px 9px; + height: 100%; + border-radius: 8px; + } + + @media (min-width: 900px) { + width: 50vw; + } +`; + +export default Card; diff --git a/client/src/components/Input.tsx b/client/src/components/Input.tsx new file mode 100644 index 0000000..f67d079 --- /dev/null +++ b/client/src/components/Input.tsx @@ -0,0 +1,74 @@ +import { Field } from "formik"; +import styled from "styled-components"; + +type InputProps = { + id: string, + name: string, + placeholder: string +} + +const Input = ({id, name, placeholder}:InputProps) => { + return ( + + + + + ); +}; + +const StyledInput = styled.div` + --width-of-input: 200px; + --border-height: 1px; + --border-before-color: rgba(221, 221, 221, 0.39); + --border-after-color: #5891ff; + --input-hovered-color: #4985e01f; + position: relative; + width: var(--width-of-input); + + /* styling of Input */ + .input { + color: var(--dark); + font-size: 0.9rem; + background-color: transparent; + width: 100%; + box-sizing: border-box; + padding-inline: 0.5em; + padding-block: 0.7em; + border: none; + border-bottom: var(--border-height) solid var(--border-before-color); + } + /* styling of animated border */ + .input-border { + position: absolute; + background: var(--border-after-color); + width: 0%; + height: 2px; + bottom: 0; + left: 0; + transition: 0.3s; + } + /* Hover on Input */ + input:hover { + background: var(--input-hovered-color); + } + + input:focus { + outline: none; + } + /* here is code of animated border */ + input:focus ~ .input-border { + width: 100%; + } + /* === if you want to do animated border on typing === */ + /* remove input:focus code and uncomment below code */ + /* input:valid ~ .input-border{ + width: 100%; +} */ +`; + +export default Input; diff --git a/client/src/components/Loading.tsx b/client/src/components/Loading.tsx new file mode 100644 index 0000000..d6aba9c --- /dev/null +++ b/client/src/components/Loading.tsx @@ -0,0 +1,108 @@ +import React from "react"; +import styled from "styled-components"; + + +const Loading = () => { + return ( + +
+ + + + + + + + + +
+ ); +}; + +const StyledLoading = styled.div` + .jelly { + --uib-size: 40px; + --uib-speed: 0.8s; + --uib-color: #2c2b2b; + position: relative; + height: calc(var(--uib-size) / 2); + width: var(--uib-size); + filter: url("#uib-jelly-ooze"); + animation: rotate72317 calc(var(--uib-speed) * 2) linear infinite; + } + + .jelly::before, + .jelly::after { + content: ""; + position: absolute; + top: 0%; + left: 25%; + width: 50%; + height: 100%; + background: var(--uib-color); + border-radius: 100%; + } + + .jelly::before { + animation: shift-left var(--uib-speed) ease infinite; + } + + .jelly::after { + animation: shift-right var(--uib-speed) ease infinite; + } + + .jelly-maker { + width: 0; + height: 0; + position: absolute; + } + + @keyframes rotate72317 { + 0%, + 49.999%, + 100% { + transform: none; + } + + 50%, + 99.999% { + transform: rotate(90deg); + } + } + + @keyframes shift-left { + 0%, + 100% { + transform: translateX(0%); + } + + 50% { + transform: scale(0.65) translateX(-75%); + } + } + + @keyframes shift-right { + 0%, + 100% { + transform: translateX(0%); + } + + 50% { + transform: scale(0.65) translateX(75%); + } + } +`; + +export default Loading; From 805f2c91800f2aa117f36e3900f7d953ebab2167 Mon Sep 17 00:00:00 2001 From: Guillermo Zevallos Date: Wed, 4 Jan 2023 18:27:06 -0500 Subject: [PATCH 05/20] Update Board and layout --- client/src/App.tsx | 65 ++++++++++++++++++---- client/src/components/Board.tsx | 77 ++++++++++++++++++--------- client/src/components/SendMessage.tsx | 28 ++++++---- 3 files changed, 125 insertions(+), 45 deletions(-) diff --git a/client/src/App.tsx b/client/src/App.tsx index ebb4232..0d63184 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1,19 +1,62 @@ -import { useState } from 'react' -import { GlobalStyle } from './theme/GlobalStyle' -import Board from './components/Board' -import SendMessage from './components/SendMessage' +import { useEffect, useState } from "react"; +import { GlobalStyle } from "./theme/GlobalStyle"; +import Board from "./components/Board"; +import SendMessage from "./components/SendMessage"; +import { Message } from "./types/messageType"; +import { getMessage } from "./service/message"; +import Loading from "./components/Loading"; +import Card from "./components/Card"; +import styled from "styled-components"; function App() { + const [messages, setMessages] = useState(); + const [loading, setLoading] = useState(false); + + useEffect(() => { + setLoading(true); + getMessage().then(data => { + setMessages(data); + setLoading(false); + }); + }, []); return (
- -

Mini Message Board

- - - + + + +

Mini Message Board

+ {!loading ? ( +
+ +
+ ) : ( +
+ +
+ )} + +
+
- ) + ); } -export default App +const StyledConent = styled.main` + height: 100%; + + display: flex; + flex-direction: column; + justify-content: space-between; + + .main-content{ + flex: 1; + } + .loading{ + display: flex; + justify-content: center; + align-items: center; + } +`; + +export default App; diff --git a/client/src/components/Board.tsx b/client/src/components/Board.tsx index eb297f7..d7e9fe2 100644 --- a/client/src/components/Board.tsx +++ b/client/src/components/Board.tsx @@ -1,30 +1,59 @@ -import { useEffect, useState } from 'react'; -import { getMessage } from "../service/message" -import { Message } from '../types/messageType'; +import { useEffect, useState } from "react"; +import styled from "styled-components"; +import { getMessage } from "../service/message"; +import { Message } from "../types/messageType"; -const Board = () => { - const [messages, setMessages] = useState() - - useEffect(() => { - getMessage().then((data)=>setMessages(data)) - console.log(messages,33); - - }, []) - +type BoardProps = { + messages: Message[] | undefined; +}; +const Board = ({ messages }: BoardProps) => { return ( -
-

Messages

+ +

Messages

+ {!!messages && (
- {messages?.map(message => ( -
-

{message.username}

-

{message.text}

-
- ))} + {messages.map(message => ( +
+

{message.username}

+

{message.text}

+
+ ))}
-
- ) -} + )} + + ); +}; + +const StyledBoard = styled.main` + .messages { + } + .messages-animate { + -webkit-animation: slide-top 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both; + animation: slide-top 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both; + } + @-webkit-keyframes slide-top { + 0% { + -webkit-transform: translateY(100px); + transform: translateY(100px); + } + 100% { + -webkit-transform: translateY(0px); + transform: translateY(0px); + } + } + @keyframes slide-top { + 0% { + -webkit-transform: translateY(100px); + transform: translateY(100px); + } + 100% { + -webkit-transform: translateY(0px); + transform: translateY(0px); + } + } +`; -export default Board \ No newline at end of file +export default Board; diff --git a/client/src/components/SendMessage.tsx b/client/src/components/SendMessage.tsx index 043fa28..0970994 100644 --- a/client/src/components/SendMessage.tsx +++ b/client/src/components/SendMessage.tsx @@ -1,13 +1,20 @@ import { Formik, Field, Form } from "formik"; import { FormikHelpers } from "formik/dist/types"; import { createMessage } from "../service/message"; +import { Message } from "../types/messageType"; +import Button from "./Button"; +import Input from "./Input"; type FormValues = { username: string; text: string; }; -const SendMessage = () => { +type SendMessageProps = { + setMessages: React.Dispatch>; +}; + +const SendMessage = ({ setMessages }: SendMessageProps) => { const initialValues: FormValues = { username: "", text: "" }; const handleSubmit = async ( @@ -15,17 +22,19 @@ const SendMessage = () => { actions: FormikHelpers ) => { await createMessage(values) - .then(res => console.log(res)) + .then(res => { + console.log(res); + setMessages(m => [...m!, res.messageDB]); + }) .catch(err => console.log(err)); }; return (
-

Formi

{ - const errors: { username?: string, text?:string } = {}; + const errors: { username?: string; text?: string } = {}; if (!values.username) { errors.username = "Required"; @@ -40,23 +49,22 @@ const SendMessage = () => { onSubmit={handleSubmit}> {({ isSubmitting, errors }) => (
- {errors.username ?
{errors.username}
: null} - {errors.text ?
{errors.text}
: null} - + disabled={isSubmitting} + /> )}
From 9f791e6bebb6e0190b6c6ccc4d6e2573705e0f5a Mon Sep 17 00:00:00 2001 From: Guillermo Zevallos Date: Thu, 5 Jan 2023 12:22:30 -0500 Subject: [PATCH 06/20] Added patterns and colors to messages --- client/src/App.tsx | 6 +++++- client/src/components/Board.tsx | 6 +++++- client/src/components/Button.tsx | 12 +++--------- client/src/components/Card.tsx | 7 ++++--- client/src/components/SvgPattern.tsx | 26 ++++++++++++++++++++++++++ client/src/theme/GlobalStyle.tsx | 13 ++++++++++++- client/src/utils/getColor.ts | 6 ++++++ 7 files changed, 61 insertions(+), 15 deletions(-) create mode 100644 client/src/components/SvgPattern.tsx create mode 100644 client/src/utils/getColor.ts diff --git a/client/src/App.tsx b/client/src/App.tsx index 0d63184..3a00af7 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -7,6 +7,7 @@ import { getMessage } from "./service/message"; import Loading from "./components/Loading"; import Card from "./components/Card"; import styled from "styled-components"; +import SvgPattern from "./components/SvgPattern"; function App() { const [messages, setMessages] = useState(); @@ -23,6 +24,8 @@ function App() { return (
+ +

Mini Message Board

@@ -44,11 +47,12 @@ function App() { const StyledConent = styled.main` height: 100%; - display: flex; flex-direction: column; justify-content: space-between; + + .main-content{ flex: 1; } diff --git a/client/src/components/Board.tsx b/client/src/components/Board.tsx index d7e9fe2..85c743a 100644 --- a/client/src/components/Board.tsx +++ b/client/src/components/Board.tsx @@ -2,6 +2,7 @@ import { useEffect, useState } from "react"; import styled from "styled-components"; import { getMessage } from "../service/message"; import { Message } from "../types/messageType"; +import { getRandomColor } from "../utils/getColor"; type BoardProps = { messages: Message[] | undefined; @@ -16,7 +17,10 @@ const Board = ({ messages }: BoardProps) => { {messages.map(message => (
+ className="messages messages-animate" + style={{ + backgroundColor: getRandomColor() + }}>

{message.username}

{message.text}

diff --git a/client/src/components/Button.tsx b/client/src/components/Button.tsx index 91c6639..5968b69 100644 --- a/client/src/components/Button.tsx +++ b/client/src/components/Button.tsx @@ -13,14 +13,8 @@ const Button = ({ type, disabled }: ButtonProps) => { - - + viewBox="0 0 512 512"> + ); @@ -39,7 +33,7 @@ const StyledButton = styled.button` } & svg { - color: #fff; + fill: #fff; } `; diff --git a/client/src/components/Card.tsx b/client/src/components/Card.tsx index dffcfb3..cfa450b 100644 --- a/client/src/components/Card.tsx +++ b/client/src/components/Card.tsx @@ -28,14 +28,15 @@ const StyledCard = styled.div` height: 90vh; position: relative; margin: 0 auto; - background-color: #f8fbfe; + background-color: var(--white); border-radius: 8px; z-index: 1; + box-shadow: rgba(50, 50, 93, 0.25) 0px 6px 12px -2px, rgba(0, 0, 0, 0.3) 0px 3px 7px -3px; .tools { position: absolute; - border-radius: 8px 8px 0 0 ; - background: white; + border-radius: 8px 8px 0 0; + background: #2F283B; width: 100%; display: flex; align-items: center; diff --git a/client/src/components/SvgPattern.tsx b/client/src/components/SvgPattern.tsx new file mode 100644 index 0000000..464b463 --- /dev/null +++ b/client/src/components/SvgPattern.tsx @@ -0,0 +1,26 @@ +type SvgPatternProps = { + className?: string; +}; + +const SvgPattern = ({ className = "" }: SvgPatternProps) => { + return ( +
+ + + + + + + + +
+ ); +}; + +export default SvgPattern; diff --git a/client/src/theme/GlobalStyle.tsx b/client/src/theme/GlobalStyle.tsx index 04aabb1..99b7c66 100644 --- a/client/src/theme/GlobalStyle.tsx +++ b/client/src/theme/GlobalStyle.tsx @@ -24,11 +24,12 @@ export const GlobalStyle = createGlobalStyle` font-family: 'Outfit', sans-serif; font-size: 14px; line-height: 17.64px; - background-color: var(--dark); + background-color: var(--white-bg); font-family: 'Lato', sans-serif; display: flex; align-items: center; justify-content: center; + position: relative; } h1,h2,h3,h4,h5,h6{ @@ -36,5 +37,15 @@ export const GlobalStyle = createGlobalStyle` font-family: 'Syne', sans-serif; } + .pattern{ + position: absolute; + bottom: 5rem; + right: -6rem; + } + .pattern-2{ + position: absolute; + top: -5rem; + left: -5rem; + } `; diff --git a/client/src/utils/getColor.ts b/client/src/utils/getColor.ts new file mode 100644 index 0000000..1bf3d1d --- /dev/null +++ b/client/src/utils/getColor.ts @@ -0,0 +1,6 @@ +import { palette } from "../theme/GlobalStyle" + +export const getRandomColor = () => { + const randomIndex = Math.floor(Math.random() * palette.length); + return palette[randomIndex]; +} \ No newline at end of file From 83bfed72598529e4474df6ce42ce2b19b0e25f8e Mon Sep 17 00:00:00 2001 From: Guillermo Zevallos Date: Thu, 5 Jan 2023 21:24:56 -0500 Subject: [PATCH 07/20] Refactor styles --- client/index.html | 1 + client/src/App.tsx | 5 +- client/src/components/Board.tsx | 69 +++++++++++++++++++++------ client/src/components/Button.tsx | 27 ++++++++--- client/src/components/Card.tsx | 32 +++++++++++-- client/src/components/Input.tsx | 42 ++++++++++------ client/src/components/SendMessage.tsx | 42 ++++++++++------ client/src/theme/GlobalStyle.tsx | 22 ++++++++- 8 files changed, 185 insertions(+), 55 deletions(-) diff --git a/client/index.html b/client/index.html index a38bab6..b1b706a 100644 --- a/client/index.html +++ b/client/index.html @@ -6,6 +6,7 @@ Vite + React + TS +
diff --git a/client/src/App.tsx b/client/src/App.tsx index 3a00af7..8e10793 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -51,10 +51,13 @@ const StyledConent = styled.main` flex-direction: column; justify-content: space-between; - + h1{ + padding: 1rem 0; + } .main-content{ flex: 1; + overflow: auto; } .loading{ display: flex; diff --git a/client/src/components/Board.tsx b/client/src/components/Board.tsx index 85c743a..69f0279 100644 --- a/client/src/components/Board.tsx +++ b/client/src/components/Board.tsx @@ -9,30 +9,69 @@ type BoardProps = { }; const Board = ({ messages }: BoardProps) => { + const showMessages = messages?.map(message => { + let color = getRandomColor(); + return ( +
+

{message.username}

+

+ {message.text} +

+
+ ); + }); + return ( -

Messages

{!!messages && ( -
- {messages.map(message => ( -
-

{message.username}

-

{message.text}

-
- ))} -
+
{showMessages}
)}
); }; const StyledBoard = styled.main` - .messages { + .messages-section { + padding: 0.5rem 1rem; + overflow: hidden; + } + h3{ + padding-bottom:.5rem ; + } + .message { + color: #363f4d; + padding: 18px 20px; + line-height: 26px; + font-size: 16px; + border-radius: 7px; + margin-bottom: 30px; + width: fit-content; + max-width: 100%; + position: relative; + white-space: initial; + border: none; + + &::after { + content: ""; + bottom: 98%; + left: .7rem; + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; + border-bottom-color: inherit; + border-width: 7px; + margin-left: -7px; + } } .messages-animate { -webkit-animation: slide-top 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both; diff --git a/client/src/components/Button.tsx b/client/src/components/Button.tsx index 5968b69..d254c2b 100644 --- a/client/src/components/Button.tsx +++ b/client/src/components/Button.tsx @@ -23,17 +23,32 @@ const Button = ({ type, disabled }: ButtonProps) => { const StyledButton = styled.button` background: none; border: none; - padding: 15px 15px; + padding: 20px 20px; border-radius: 10px; - background-color: black; + background-color: var(--white); + cursor: pointer; + transition: 0.5s all ease-out; &:hover { - background: rgba(116, 77, 77, 0.774); - transition: 0.5s; + background: #ffffff; + box-shadow: 10px 10px 20px #ededed, -10px -10px 20px #ffffff; } - & svg { - fill: #fff; + fill: var(--dark); + } + + &:hover svg { + fill: #504464; + } + + &:disabled { + background: linear-gradient(145deg, #f0efef, #ffffff); + box-shadow: 6px 6px 12px #f3f3f3, -6px -6px 12px #f1eded; + } + + &:disabled svg { + cursor: wait; + fill: #cac9c9; } `; diff --git a/client/src/components/Card.tsx b/client/src/components/Card.tsx index cfa450b..1ea15a0 100644 --- a/client/src/components/Card.tsx +++ b/client/src/components/Card.tsx @@ -18,25 +18,39 @@ const Card = ({ children }: Cardprops) => {
+
+ Coded by{" "} + + {" "} + ZevaGuillo + +
{children}
); }; const StyledCard = styled.div` - width: 80vw; + width: 95vw; height: 90vh; position: relative; margin: 0 auto; background-color: var(--white); border-radius: 8px; z-index: 1; - box-shadow: rgba(50, 50, 93, 0.25) 0px 6px 12px -2px, rgba(0, 0, 0, 0.3) 0px 3px 7px -3px; + box-shadow: rgba(50, 50, 93, 0.25) 0px 6px 12px -2px, + rgba(0, 0, 0, 0.3) 0px 3px 7px -3px; .tools { position: absolute; border-radius: 8px 8px 0 0; - background: #2F283B; + background: #2f283b; width: 100%; display: flex; align-items: center; @@ -66,6 +80,18 @@ const StyledCard = styled.div` background-color: #00ca4e; } } + .attribution { + position: absolute; + top: 9px; + left: 50%; + transform: translateX(-50%); + color: #aaa2c9; + a { + padding-left: .2rem; + text-decoration: none; + color: #dab3b4; + } + } .card__content { padding: 2.5rem 9px 9px; diff --git a/client/src/components/Input.tsx b/client/src/components/Input.tsx index f67d079..b45d36b 100644 --- a/client/src/components/Input.tsx +++ b/client/src/components/Input.tsx @@ -2,16 +2,17 @@ import { Field } from "formik"; import styled from "styled-components"; type InputProps = { - id: string, - name: string, - placeholder: string -} + id: string; + name: string; + placeholder: string; + className?: string; +}; -const Input = ({id, name, placeholder}:InputProps) => { +const Input = ({ id, name, placeholder, className="" }: InputProps) => { return ( - + { }; const StyledInput = styled.div` - --width-of-input: 200px; + --width-of-input: 100%; --border-height: 1px; --border-before-color: rgba(221, 221, 221, 0.39); - --border-after-color: #5891ff; - --input-hovered-color: #4985e01f; + --border-after-color: #434b5c; + --input-hovered-color: #6062641f; position: relative; width: var(--width-of-input); + &.error::after{ + content: 'Required'; + position: absolute; + right: .5rem; + top: 50%; + transform: translateY(-50%); + color: salmon; + opacity: .7; + } + /* styling of Input */ .input { color: var(--dark); @@ -42,6 +53,12 @@ const StyledInput = styled.div` border: none; border-bottom: var(--border-height) solid var(--border-before-color); } + .input.error{ + border-bottom: var(--border-height) solid salmon; + } + .input.error::after{ + content: 'd'; + } /* styling of animated border */ .input-border { position: absolute; @@ -64,11 +81,6 @@ const StyledInput = styled.div` input:focus ~ .input-border { width: 100%; } - /* === if you want to do animated border on typing === */ - /* remove input:focus code and uncomment below code */ - /* input:valid ~ .input-border{ - width: 100%; -} */ `; export default Input; diff --git a/client/src/components/SendMessage.tsx b/client/src/components/SendMessage.tsx index 0970994..7e0bb79 100644 --- a/client/src/components/SendMessage.tsx +++ b/client/src/components/SendMessage.tsx @@ -1,5 +1,6 @@ import { Formik, Field, Form } from "formik"; import { FormikHelpers } from "formik/dist/types"; +import styled from "styled-components"; import { createMessage } from "../service/message"; import { Message } from "../types/messageType"; import Button from "./Button"; @@ -30,7 +31,7 @@ const SendMessage = ({ setMessages }: SendMessageProps) => { }; return ( -
+ { @@ -49,18 +50,21 @@ const SendMessage = ({ setMessages }: SendMessageProps) => { onSubmit={handleSubmit}> {({ isSubmitting, errors }) => (
- - {errors.username ?
{errors.username}
: null} - - {errors.text ?
{errors.text}
: null} +
+ + {} + +
+ ); }; +const StyledSendMessage = styled.div` + padding: 1rem 0; + form{ + display: flex; + } + .inputs{ + flex: 1; + } +`; + export default SendMessage; diff --git a/client/src/theme/GlobalStyle.tsx b/client/src/theme/GlobalStyle.tsx index 99b7c66..39859dc 100644 --- a/client/src/theme/GlobalStyle.tsx +++ b/client/src/theme/GlobalStyle.tsx @@ -6,7 +6,27 @@ export const GlobalStyle = createGlobalStyle` :root{ --white-bg: #F2F3F7; --white: #FFFFFF; - --dark: #00132B; + --dark: #2F283B; + --thumb-bg: #d2d2e0; + } + + ::-webkit-scrollbar { + width: 5px; + height: 5px; + } + + ::-webkit-scrollbar-thumb { + background: var(--thumb-bg); + border-radius: var(--radius); + } + + ::-webkit-scrollbar-thumb:active, + ::-webkit-scrollbar-thumb:hover { + background: var(--dark); + } + + ::-webkit-scrollbar-track { + background: #80808014; } * { From 1a2267e5f7bdafcf43c607e20ff56e02a66d8d66 Mon Sep 17 00:00:00 2001 From: Guillermo Zevallos Date: Thu, 5 Jan 2023 21:35:36 -0500 Subject: [PATCH 08/20] Add variables --- client/.env.example | 1 + client/.gitignore | 2 +- client/src/service/message.ts | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 client/.env.example diff --git a/client/.env.example b/client/.env.example new file mode 100644 index 0000000..ce41a35 --- /dev/null +++ b/client/.env.example @@ -0,0 +1 @@ +VITE_SERVER=... \ No newline at end of file diff --git a/client/.gitignore b/client/.gitignore index 7a9d1bc..b7adbe3 100644 --- a/client/.gitignore +++ b/client/.gitignore @@ -11,7 +11,7 @@ node_modules dist dist-ssr *.local - +.env # Editor directories and files .vscode/* !.vscode/extensions.json diff --git a/client/src/service/message.ts b/client/src/service/message.ts index d6df902..9203d9c 100644 --- a/client/src/service/message.ts +++ b/client/src/service/message.ts @@ -1,7 +1,7 @@ import { Message } from "../types/messageType"; export const getMessage = async () => { - const res = await fetch('https://mini-message-board-server.onrender.com/new'); + const res = await fetch(import.meta.env.VITE_SERVER); const data = await res.json(); return [...data.messages] as Message[] @@ -9,7 +9,7 @@ export const getMessage = async () => { export const createMessage = async (data: Omit) => { - const res = await fetch('https://mini-message-board-server.onrender.com/new', { + const res = await fetch(import.meta.env.VITE_SERVER, { method: 'POST', headers: { 'Content-Type': 'application/json' From 68f521ae844e7a1179842a209b0403085fe4cd7c Mon Sep 17 00:00:00 2001 From: Guillermo Zevallos Date: Thu, 5 Jan 2023 22:04:22 -0500 Subject: [PATCH 09/20] added date to messages --- client/src/components/Board.tsx | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/client/src/components/Board.tsx b/client/src/components/Board.tsx index 69f0279..bca8b72 100644 --- a/client/src/components/Board.tsx +++ b/client/src/components/Board.tsx @@ -1,9 +1,6 @@ -import { useEffect, useState } from "react"; import styled from "styled-components"; -import { getMessage } from "../service/message"; import { Message } from "../types/messageType"; import { getRandomColor } from "../utils/getColor"; - type BoardProps = { messages: Message[] | undefined; }; @@ -15,7 +12,18 @@ const Board = ({ messages }: BoardProps) => {
-

{message.username}

+
+

{message.username}

+

+ {new Date(message.added).toLocaleDateString("en-US", { + year: "numeric", + month: "2-digit", + day: "2-digit", + hour: "2-digit", + minute: "2-digit", + })} +

+

{message.username}

- {new Date(message.added).toLocaleDateString("en-US", { + {new Date(message.added).toLocaleDateString("default", { year: "numeric", month: "2-digit", day: "2-digit", From 6ed5ba0cb3185ab90164a77a556e38ae6aaa8de1 Mon Sep 17 00:00:00 2001 From: Guillermo Zevallos Date: Thu, 2 Feb 2023 20:30:11 -0500 Subject: [PATCH 14/20] fix get messages --- server/controllers/new.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server/controllers/new.ts b/server/controllers/new.ts index eacc79d..7cb4890 100644 --- a/server/controllers/new.ts +++ b/server/controllers/new.ts @@ -6,8 +6,9 @@ export const getMessages = async (req: Request, res: Response) => { const { limit = 10, since = 0 } = req.query; const messages = await Message.find() - .skip(Number(since)) - .limit(Number(limit)) + // TODO: pages + // .skip(Number(since)) + // .limit(Number(limit)) res.json({ messages From 184432635cf39f85df8329849a01cdaf3653b832 Mon Sep 17 00:00:00 2001 From: Guillermo Zevallos Date: Fri, 10 Feb 2023 14:34:13 -0500 Subject: [PATCH 15/20] Added infiniteScroll --- client/src/App.tsx | 64 +++++++++++++++++---------- client/src/components/Board.tsx | 43 ++++++++++++++++-- client/src/components/SendMessage.tsx | 2 +- client/src/hooks/useInfiniteScroll.ts | 29 ++++++++++++ client/src/service/message.ts | 29 +++++++----- 5 files changed, 129 insertions(+), 38 deletions(-) create mode 100644 client/src/hooks/useInfiniteScroll.ts diff --git a/client/src/App.tsx b/client/src/App.tsx index 8e10793..ea26b49 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1,43 +1,61 @@ -import { useEffect, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import { GlobalStyle } from "./theme/GlobalStyle"; import Board from "./components/Board"; import SendMessage from "./components/SendMessage"; import { Message } from "./types/messageType"; import { getMessage } from "./service/message"; -import Loading from "./components/Loading"; import Card from "./components/Card"; import styled from "styled-components"; import SvgPattern from "./components/SvgPattern"; +import useInfiniteScroll from "./hooks/useInfiniteScroll"; +import Loading from "./components/Loading"; function App() { - const [messages, setMessages] = useState(); + const [messages, setMessages] = useState([]); const [loading, setLoading] = useState(false); + const [last, setLast] = useState(false); + const { loadMoreRef, skip } = useInfiniteScroll(); - useEffect(() => { - setLoading(true); - getMessage().then(data => { - setMessages(data); + const fetchMessages = useCallback(async () => { + try { + setLoading(true); + if (!last) { + getMessage(skip).then(data => { + setMessages(prev => [...data.reverse(), ...prev]); + if (data.length < 10 && data.length > 0) { + setLast(true); + } + }); + } + } catch (error) { + console.log(error); + alert(error); + } finally { setLoading(false); - }); - }, []); + } + }, [skip]); + + useEffect(() => { + fetchMessages(); + }, [fetchMessages]); return (

- - + +

Mini Message Board

- {!loading ? ( -
- -
- ) : ( -
- -
- )} + +
+ +
+
@@ -51,15 +69,15 @@ const StyledConent = styled.main` flex-direction: column; justify-content: space-between; - h1{ + h1 { padding: 1rem 0; } - .main-content{ + .main-content { flex: 1; overflow: auto; } - .loading{ + .loading { display: flex; justify-content: center; align-items: center; diff --git a/client/src/components/Board.tsx b/client/src/components/Board.tsx index 8532abf..d070ce2 100644 --- a/client/src/components/Board.tsx +++ b/client/src/components/Board.tsx @@ -1,16 +1,31 @@ +import { useEffect, useRef } from "react"; import styled from "styled-components"; import { Message } from "../types/messageType"; import { getRandomColor } from "../utils/getColor"; +import Loading from "./Loading"; + type BoardProps = { messages: Message[] | undefined; + last: boolean; + loadMoreRef: React.RefObject; }; -const Board = ({ messages }: BoardProps) => { +const Board = ({ messages, loadMoreRef, last }: BoardProps) => { + const messagesEndRef = useRef(null); + + useEffect(() => { + messagesEndRef?.current?.scrollIntoView({ + block: "end", + behavior: "smooth", + }); + }); + const showMessages = messages?.map(message => { let color = getRandomColor(); return (

{message.username}

@@ -39,16 +54,38 @@ const Board = ({ messages }: BoardProps) => { return ( {!!messages && ( -
{showMessages}
+
+ {!last && ( +
+ +
+ )} + {showMessages} +
)}
); }; const StyledBoard = styled.main` + + .loader { + min-height: 3rem; + display: flex; + align-items: center; + justify-content: center; + } + + .height{ + height: 100%; + } + .messages-section { + height: 100%; padding: 0.5rem 1rem; - overflow: hidden; + overflow-y: hidden; } h3 { padding-bottom: 0.5rem; diff --git a/client/src/components/SendMessage.tsx b/client/src/components/SendMessage.tsx index 7e0bb79..51f207f 100644 --- a/client/src/components/SendMessage.tsx +++ b/client/src/components/SendMessage.tsx @@ -12,7 +12,7 @@ type FormValues = { }; type SendMessageProps = { - setMessages: React.Dispatch>; + setMessages: React.Dispatch>; }; const SendMessage = ({ setMessages }: SendMessageProps) => { diff --git a/client/src/hooks/useInfiniteScroll.ts b/client/src/hooks/useInfiniteScroll.ts new file mode 100644 index 0000000..4876d58 --- /dev/null +++ b/client/src/hooks/useInfiniteScroll.ts @@ -0,0 +1,29 @@ +import { useState, useCallback, useEffect, useRef } from 'react'; + +export const useInfiniteScroll = () => { + const [skip, setSkip] = useState(0); + const loadMoreRef = useRef(null); + + const handleObserver = useCallback((entries:any) => { + const [target] = entries; + if (target.isIntersecting) { + setSkip(a => a + 10); + } + }, []); + + useEffect(() => { + const option = { + root: null, + rootMargin: '0px', + threshold: 1.0, + }; + + const observer = new IntersectionObserver(handleObserver, option); + + if (loadMoreRef.current) observer.observe(loadMoreRef.current); + }, [handleObserver]); + + return { loadMoreRef, skip }; + } + + export default useInfiniteScroll; \ No newline at end of file diff --git a/client/src/service/message.ts b/client/src/service/message.ts index 9203d9c..f2650d3 100644 --- a/client/src/service/message.ts +++ b/client/src/service/message.ts @@ -1,7 +1,9 @@ import { Message } from "../types/messageType"; -export const getMessage = async () => { - const res = await fetch(import.meta.env.VITE_SERVER); +export const getMessage = async (since?: number) => { + console.log(since); + + const res = await fetch(`${import.meta.env.VITE_SERVER}${since ? `/?since=${since}`:''}`); const data = await res.json(); return [...data.messages] as Message[] @@ -9,16 +11,21 @@ export const getMessage = async () => { export const createMessage = async (data: Omit) => { - const res = await fetch(import.meta.env.VITE_SERVER, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(data) - }); - const json = await res.json(); + try { + const res = await fetch(import.meta.env.VITE_SERVER, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(data) + }); + const json = await res.json(); - return json; + return json; + } catch (error) { + + alert('server not') + } } From 206e561fdde045f642c498e841c031a2f29c5553 Mon Sep 17 00:00:00 2001 From: Guillermo Zevallos Date: Fri, 10 Feb 2023 14:37:49 -0500 Subject: [PATCH 16/20] styles button --- client/src/components/Button.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/Button.tsx b/client/src/components/Button.tsx index d254c2b..054ed81 100644 --- a/client/src/components/Button.tsx +++ b/client/src/components/Button.tsx @@ -25,7 +25,7 @@ const StyledButton = styled.button` border: none; padding: 20px 20px; border-radius: 10px; - background-color: var(--white); + background-color: #f8f8f8; cursor: pointer; transition: 0.5s all ease-out; From 6eb931c66b493111da1cae33130696b2bb1781e1 Mon Sep 17 00:00:00 2001 From: Guillermo Zevallos Date: Fri, 10 Feb 2023 14:38:07 -0500 Subject: [PATCH 17/20] refactor server --- server/controllers/new.ts | 8 +-- server/models/server.ts | 2 + server/package.json | 6 +- server/routes/new.ts | 4 +- server/yarn.lock | 143 +++++++++++++++++++++++++++++++++++++- 5 files changed, 155 insertions(+), 8 deletions(-) diff --git a/server/controllers/new.ts b/server/controllers/new.ts index 7cb4890..e710cc0 100644 --- a/server/controllers/new.ts +++ b/server/controllers/new.ts @@ -4,11 +4,11 @@ import Message from '../models/message' export const getMessages = async (req: Request, res: Response) => { const { limit = 10, since = 0 } = req.query; - + const messages = await Message.find() - // TODO: pages - // .skip(Number(since)) - // .limit(Number(limit)) + .sort({$natural:-1}) + .skip(Number(since)) + .limit(Number(limit)) res.json({ messages diff --git a/server/models/server.ts b/server/models/server.ts index 8a91fde..f286fb9 100644 --- a/server/models/server.ts +++ b/server/models/server.ts @@ -1,5 +1,6 @@ import express, { Application } from 'express'; import cors from 'cors'; +import morgan from 'morgan' import newRouter from '../routes/new'; import { dbConnection } from '../database/config'; @@ -29,6 +30,7 @@ class Server { middlewares() { this.app.use(express.json()); this.app.use(cors()) + this.app.use(morgan('dev')) } routes(){ diff --git a/server/package.json b/server/package.json index 39dda12..d56bfbc 100644 --- a/server/package.json +++ b/server/package.json @@ -7,6 +7,7 @@ "scripts": { "build": "tsc", "start": " node dist/app.js", + "dev": "nodemon app.ts", "deploy": "yarn build && yarn start" }, "dependencies": { @@ -16,10 +17,13 @@ "express": "^4.18.2", "express-validator": "^6.14.2", "mongoose": "^6.8.0", + "morgan": "^1.10.0", "tslint": "^6.1.3", "typescript": "^4.9.4" }, "devDependencies": { - "@types/cors": "^2.8.13" + "@types/cors": "^2.8.13", + "@types/morgan": "^1.9.4", + "ts-node": "^10.9.1" } } diff --git a/server/routes/new.ts b/server/routes/new.ts index b33f0e2..1b93433 100644 --- a/server/routes/new.ts +++ b/server/routes/new.ts @@ -8,8 +8,8 @@ const router = Router(); router.get('/',getMessages); router.post('/',[ - check('username', 'Username is requires').exists(), - check('text', 'Text is requires').exists(), + check('username', 'Username is requires').not().isEmpty(), + check('text', 'Text is requires').not().isEmpty(), validateFields ],newMessage); diff --git a/server/yarn.lock b/server/yarn.lock index 4032e27..036172c 100644 --- a/server/yarn.lock +++ b/server/yarn.lock @@ -767,6 +767,51 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + +"@jridgewell/resolve-uri@^3.0.3": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" + integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== + +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.14" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" + integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== + +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@tsconfig/node10@^1.0.7": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" + integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" + integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== + "@types/body-parser@*": version "1.19.2" resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" @@ -813,6 +858,13 @@ resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10" integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA== +"@types/morgan@^1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@types/morgan/-/morgan-1.9.4.tgz#99965ad2bdc7c5cee28d8ce95cfa7300b19ea562" + integrity sha512-cXoc4k+6+YAllH3ZHmx4hf7La1dzUk6keTR4bF4b4Sc0mZxU/zK4wO7l+ZzezXm/jkYj/qC+uYGZrarZdIVvyQ== + dependencies: + "@types/node" "*" + "@types/node@*": version "18.11.16" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.16.tgz#966cae211e970199559cfbd295888fca189e49af" @@ -857,6 +909,16 @@ accepts@~1.3.8: mime-types "~2.1.34" negotiator "0.6.3" +acorn-walk@^8.1.1: + version "8.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" + integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== + +acorn@^8.4.1: + version "8.8.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" + integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== + ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -864,6 +926,11 @@ ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -886,6 +953,13 @@ base64-js@^1.3.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +basic-auth@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a" + integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg== + dependencies: + safe-buffer "5.1.2" + body-parser@1.20.1: version "1.20.1" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" @@ -1011,6 +1085,11 @@ cors@^2.8.5: object-assign "^4" vary "^1" +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + debug@2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -1025,7 +1104,7 @@ debug@4.x: dependencies: ms "2.1.2" -depd@2.0.0: +depd@2.0.0, depd@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== @@ -1274,6 +1353,11 @@ lodash@^4.17.21: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -1363,6 +1447,17 @@ mongoose@^6.8.0: ms "2.1.3" sift "16.0.1" +morgan@^1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7" + integrity sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ== + dependencies: + basic-auth "~2.0.1" + debug "2.6.9" + depd "~2.0.0" + on-finished "~2.3.0" + on-headers "~1.0.2" + mpath@0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/mpath/-/mpath-0.9.0.tgz#0c122fe107846e31fc58c75b09c35514b3871904" @@ -1412,6 +1507,18 @@ on-finished@2.4.1: dependencies: ee-first "1.1.1" +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww== + dependencies: + ee-first "1.1.1" + +on-headers@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -1483,6 +1590,11 @@ resolve@^1.3.2: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +safe-buffer@5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + safe-buffer@5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" @@ -1612,6 +1724,25 @@ tr46@^3.0.0: dependencies: punycode "^2.1.1" +ts-node@^10.9.1: + version "10.9.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" + integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.1" + tslib@^1.11.1, tslib@^1.13.0, tslib@^1.8.1: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" @@ -1676,6 +1807,11 @@ uuid@^8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + validator@^13.7.0: version "13.7.0" resolved "https://registry.yarnpkg.com/validator/-/validator-13.7.0.tgz#4f9658ba13ba8f3d82ee881d3516489ea85c0857" @@ -1703,3 +1839,8 @@ wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== From f136a4ee745289cc449302919a9abe16c297598f Mon Sep 17 00:00:00 2001 From: Guillermo Zevallos Date: Fri, 10 Feb 2023 16:03:34 -0500 Subject: [PATCH 18/20] install emoji-picker-react --- client/package.json | 1 + client/yarn.lock | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/client/package.json b/client/package.json index ba719be..ed73246 100644 --- a/client/package.json +++ b/client/package.json @@ -11,6 +11,7 @@ }, "dependencies": { "@types/styled-components": "^5.1.26", + "emoji-picker-react": "^4.4.7", "formik": "^2.2.9", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/client/yarn.lock b/client/yarn.lock index e4612f8..46c900e 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -493,6 +493,11 @@ chalk@^2.0.0: escape-string-regexp "^1.0.5" supports-color "^5.3.0" +clsx@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" + integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== + color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -546,6 +551,13 @@ electron-to-chromium@^1.4.251: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz#61046d1e4cab3a25238f6bf7413795270f125592" integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA== +emoji-picker-react@^4.4.7: + version "4.4.7" + resolved "https://registry.yarnpkg.com/emoji-picker-react/-/emoji-picker-react-4.4.7.tgz#faff798cf7acad41c5eb6b103c93480e777f2222" + integrity sha512-HcB1Fr57W6M+gLLm/W4YhbAnUQqYBUAhZBOHmPlSQODLtsDI8vecyqNIF9RrStZHfZwzUSLlpZMfY07AA6gxmA== + dependencies: + clsx "^1.2.1" + esbuild@^0.16.3: version "0.16.7" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.16.7.tgz#3288f685a83c6097dea8ddf1759ca30d6e06535b" From f3f24e421d2a71d3e65da556a3d407076730d5a7 Mon Sep 17 00:00:00 2001 From: Guillermo Zevallos Date: Fri, 10 Feb 2023 16:03:57 -0500 Subject: [PATCH 19/20] added emoji button --- client/src/components/Button.tsx | 2 +- client/src/components/Emoji.tsx | 63 ++++++++++++++++++++++++++ client/src/components/SendMessage.tsx | 64 ++++++++++++++++----------- client/src/hooks/useOnClickOutside.ts | 22 +++++++++ client/src/types/messageType.ts | 5 +++ 5 files changed, 129 insertions(+), 27 deletions(-) create mode 100644 client/src/components/Emoji.tsx create mode 100644 client/src/hooks/useOnClickOutside.ts diff --git a/client/src/components/Button.tsx b/client/src/components/Button.tsx index 054ed81..276c1b7 100644 --- a/client/src/components/Button.tsx +++ b/client/src/components/Button.tsx @@ -24,7 +24,7 @@ const StyledButton = styled.button` background: none; border: none; padding: 20px 20px; - border-radius: 10px; + border-radius: 0 8px 8px 0; background-color: #f8f8f8; cursor: pointer; transition: 0.5s all ease-out; diff --git a/client/src/components/Emoji.tsx b/client/src/components/Emoji.tsx new file mode 100644 index 0000000..972579f --- /dev/null +++ b/client/src/components/Emoji.tsx @@ -0,0 +1,63 @@ +import { useRef, useState } from "react"; +import EmojiPicker from "emoji-picker-react"; +import styled from "styled-components"; +import { useOnClickOutside } from "../hooks/useOnClickOutside"; +import { FormikState, useFormikContext } from "formik"; +import { MessageForm } from "../types/messageType"; + +type EmojiProp = { + setEmoji: React.Dispatch> +} + +const Emoji = ({setEmoji}:EmojiProp) => { + const { values , resetForm} = useFormikContext(); + + const [isPickerVisible, setIsPickerVisible] = useState(false); + const emojiPickerRef = useRef(null); + + const handlePicker = ()=> setIsPickerVisible(!isPickerVisible); + + useOnClickOutside(emojiPickerRef, () => handlePicker()); + + + return ( + + {isPickerVisible && ( +
+ { + setEmoji(e.emoji) + handlePicker() + }} + /> +
+ )} + +
+ ); +}; + +const StyledEmoji = styled.div` + position: relative; + width: 45px; + .picker { + z-index: 11; + bottom: 1rem; + position: absolute; + } + button{ + border: none; + height: 100%; + width: 100%; + border-radius: 8px 0 0 8px; + font-size: 1.5rem; + background: #f8f8f8; + } + button:hover{ + background: #e4e3e3; + } +`; + +export default Emoji; diff --git a/client/src/components/SendMessage.tsx b/client/src/components/SendMessage.tsx index 51f207f..985f7a6 100644 --- a/client/src/components/SendMessage.tsx +++ b/client/src/components/SendMessage.tsx @@ -1,9 +1,12 @@ +import EmojiPicker from "emoji-picker-react"; import { Formik, Field, Form } from "formik"; import { FormikHelpers } from "formik/dist/types"; +import { useEffect, useRef, useState } from "react"; import styled from "styled-components"; import { createMessage } from "../service/message"; -import { Message } from "../types/messageType"; +import { Message, MessageForm } from "../types/messageType"; import Button from "./Button"; +import Emoji from "./Emoji"; import Input from "./Input"; type FormValues = { @@ -32,7 +35,7 @@ const SendMessage = ({ setMessages }: SendMessageProps) => { return ( - initialValues={initialValues} validate={values => { const errors: { username?: string; text?: string } = {}; @@ -48,29 +51,38 @@ const SendMessage = ({ setMessages }: SendMessageProps) => { return errors; }} onSubmit={handleSubmit}> - {({ isSubmitting, errors }) => ( -
-
- - {} - { + const [emoji, setEmoji] = useState(''); + + useEffect(() => { + setFieldValue('text', values.text.concat(emoji) , false); + }, [emoji]); + + return ( + + +
+ + {} + +
+
-