From 3aa287d394332af38169ea6f3681af766224d31a Mon Sep 17 00:00:00 2001 From: canove Date: Wed, 13 Jan 2021 08:08:25 -0300 Subject: [PATCH] improvement: moved user data from localstorage to context --- backend/src/controllers/SessionController.ts | 10 ++-- backend/src/helpers/SerializeUser.ts | 20 +++++++ .../AuthServices/RefreshTokenService.ts | 5 +- .../services/UserServices/AuthUserSerice.ts | 19 +++++-- .../UserServices/CreateUserService.ts | 9 +--- frontend/src/accessRules.js | 11 ++++ frontend/src/components/Can/index.js | 39 ++++++++++++++ frontend/src/components/MessageInput/index.js | 25 ++++----- frontend/src/components/MessagesList/index.js | 2 +- .../src/components/NewTicketModal/index.js | 7 +-- .../components/NotificationsPopOver/index.js | 11 ++-- .../src/components/QueueSelector/index.js | 2 +- .../components/TicketActionButtons/index.js | 11 ++-- .../src/components/TicketListItem/index.js | 7 +-- frontend/src/components/TicketsList/index.js | 11 ++-- .../src/components/TicketsManager/index.js | 6 +-- frontend/src/context/Auth/AuthContext.js | 4 +- frontend/src/context/Auth/useAuth.js | 33 ++++++------ frontend/src/hooks/useLocalStorage/index.js | 29 ++++++++++ frontend/src/layout/MainListItems.js | 54 ++++++++++--------- frontend/src/layout/index.js | 41 +++++--------- frontend/src/pages/Contacts/index.js | 8 +-- frontend/src/pages/Queues/index.js | 1 - 23 files changed, 231 insertions(+), 134 deletions(-) create mode 100644 backend/src/helpers/SerializeUser.ts create mode 100644 frontend/src/accessRules.js create mode 100644 frontend/src/components/Can/index.js create mode 100644 frontend/src/hooks/useLocalStorage/index.js diff --git a/backend/src/controllers/SessionController.ts b/backend/src/controllers/SessionController.ts index b9dcf8e..3dc32c1 100644 --- a/backend/src/controllers/SessionController.ts +++ b/backend/src/controllers/SessionController.ts @@ -8,7 +8,7 @@ import { RefreshTokenService } from "../services/AuthServices/RefreshTokenServic export const store = async (req: Request, res: Response): Promise => { const { email, password } = req.body; - const { token, user, refreshToken } = await AuthUserService({ + const { token, serializedUser, refreshToken } = await AuthUserService({ email, password }); @@ -17,9 +17,7 @@ export const store = async (req: Request, res: Response): Promise => { return res.status(200).json({ token, - username: user.name, - profile: user.profile, - userId: user.id + user: serializedUser }); }; @@ -33,9 +31,9 @@ export const update = async ( throw new AppError("ERR_SESSION_EXPIRED", 401); } - const { newToken, refreshToken } = await RefreshTokenService(token); + const { user, newToken, refreshToken } = await RefreshTokenService(token); SendRefreshToken(res, refreshToken); - return res.json({ token: newToken }); + return res.json({ token: newToken, user }); }; diff --git a/backend/src/helpers/SerializeUser.ts b/backend/src/helpers/SerializeUser.ts new file mode 100644 index 0000000..3802500 --- /dev/null +++ b/backend/src/helpers/SerializeUser.ts @@ -0,0 +1,20 @@ +import Queue from "../models/Queue"; +import User from "../models/User"; + +interface SerializedUser { + id: number; + name: string; + email: string; + profile: string; + queues: Queue[]; +} + +export const SerializeUser = (user: User): SerializedUser => { + return { + id: user.id, + name: user.name, + email: user.email, + profile: user.profile, + queues: user.queues + }; +}; diff --git a/backend/src/services/AuthServices/RefreshTokenService.ts b/backend/src/services/AuthServices/RefreshTokenService.ts index fa7893c..78ed933 100644 --- a/backend/src/services/AuthServices/RefreshTokenService.ts +++ b/backend/src/services/AuthServices/RefreshTokenService.ts @@ -1,4 +1,6 @@ import { verify } from "jsonwebtoken"; + +import User from "../../models/User"; import AppError from "../../errors/AppError"; import ShowUserService from "../UserServices/ShowUserService"; import authConfig from "../../config/auth"; @@ -13,6 +15,7 @@ interface RefreshTokenPayload { } interface Response { + user: User; newToken: string; refreshToken: string; } @@ -37,5 +40,5 @@ export const RefreshTokenService = async (token: string): Promise => { const newToken = createAccessToken(user); const refreshToken = createRefreshToken(user); - return { newToken, refreshToken }; + return { user, newToken, refreshToken }; }; diff --git a/backend/src/services/UserServices/AuthUserSerice.ts b/backend/src/services/UserServices/AuthUserSerice.ts index 38d18dc..f198a7c 100644 --- a/backend/src/services/UserServices/AuthUserSerice.ts +++ b/backend/src/services/UserServices/AuthUserSerice.ts @@ -4,6 +4,16 @@ import { createAccessToken, createRefreshToken } from "../../helpers/CreateTokens"; +import { SerializeUser } from "../../helpers/SerializeUser"; +import Queue from "../../models/Queue"; + +interface SerializedUser { + id: number; + name: string; + email: string; + profile: string; + queues: Queue[]; +} interface Request { email: string; @@ -11,7 +21,7 @@ interface Request { } interface Response { - user: User; + serializedUser: SerializedUser; token: string; refreshToken: string; } @@ -21,7 +31,8 @@ const AuthUserService = async ({ password }: Request): Promise => { const user = await User.findOne({ - where: { email } + where: { email }, + include: ["queues"] }); if (!user) { @@ -35,8 +46,10 @@ const AuthUserService = async ({ const token = createAccessToken(user); const refreshToken = createRefreshToken(user); + const serializedUser = SerializeUser(user); + return { - user, + serializedUser, token, refreshToken }; diff --git a/backend/src/services/UserServices/CreateUserService.ts b/backend/src/services/UserServices/CreateUserService.ts index 930036a..e277085 100644 --- a/backend/src/services/UserServices/CreateUserService.ts +++ b/backend/src/services/UserServices/CreateUserService.ts @@ -1,6 +1,7 @@ import * as Yup from "yup"; import AppError from "../../errors/AppError"; +import { SerializeUser } from "../../helpers/SerializeUser"; import User from "../../models/User"; interface Request { @@ -63,13 +64,7 @@ const CreateUserService = async ({ await user.reload(); - const serializedUser = { - id: user.id, - name: user.name, - email: user.email, - profile: user.profile, - queues: user.queues - }; + const serializedUser = SerializeUser(user); return serializedUser; }; diff --git a/frontend/src/accessRules.js b/frontend/src/accessRules.js new file mode 100644 index 0000000..938dc32 --- /dev/null +++ b/frontend/src/accessRules.js @@ -0,0 +1,11 @@ +const rules = { + user: { + static: [], + }, + + admin: { + static: ["drawer-admin-items:view"], + }, +}; + +export default rules; diff --git a/frontend/src/components/Can/index.js b/frontend/src/components/Can/index.js new file mode 100644 index 0000000..96837c5 --- /dev/null +++ b/frontend/src/components/Can/index.js @@ -0,0 +1,39 @@ +import rules from "../../accessRules"; + +const check = (rules, role, action, data) => { + const permissions = rules[role]; + if (!permissions) { + // role is not present in the rules + return false; + } + + const staticPermissions = permissions.static; + + if (staticPermissions && staticPermissions.includes(action)) { + // static rule not provided for action + return true; + } + + const dynamicPermissions = permissions.dynamic; + + if (dynamicPermissions) { + const permissionCondition = dynamicPermissions[action]; + if (!permissionCondition) { + // dynamic rule not provided for action + return false; + } + + return permissionCondition(data); + } + return false; +}; + +const Can = ({ role, perform, data, yes, no }) => + check(rules, role, perform, data) ? yes() : no(); + +Can.defaultProps = { + yes: () => null, + no: () => null, +}; + +export default Can; diff --git a/frontend/src/components/MessageInput/index.js b/frontend/src/components/MessageInput/index.js index a5222f7..a6ce895 100644 --- a/frontend/src/components/MessageInput/index.js +++ b/frontend/src/components/MessageInput/index.js @@ -25,6 +25,8 @@ import { i18n } from "../../translate/i18n"; import api from "../../services/api"; import RecordingTimer from "./RecordingTimer"; import { ReplyMessageContext } from "../../context/ReplyingMessage/ReplyingMessageContext"; +import { AuthContext } from "../../context/Auth/AuthContext"; +import { useLocalStorage } from "../../hooks/useLocalStorage"; import toastError from "../../errors/toastError"; const Mp3Recorder = new MicRecorder({ bitRate: 128 }); @@ -164,7 +166,6 @@ const useStyles = makeStyles(theme => ({ const MessageInput = ({ ticketStatus }) => { const classes = useStyles(); const { ticketId } = useParams(); - const username = localStorage.getItem("username"); const [medias, setMedias] = useState([]); const [inputMessage, setInputMessage] = useState(""); @@ -175,17 +176,9 @@ const MessageInput = ({ ticketStatus }) => { const { setReplyingMessage, replyingMessage } = useContext( ReplyMessageContext ); + const { user } = useContext(AuthContext); - const [signMessage, setSignMessage] = useState(false); - - useEffect(() => { - const storedSignOption = localStorage.getItem("signOption"); - if (storedSignOption === "true") setSignMessage(true); - }, []); - - useEffect(() => { - localStorage.setItem("signOption", signMessage); - }, [signMessage]); + const [signMessage, setSignMessage] = useLocalStorage("signOption", true); useEffect(() => { inputRef.current.focus(); @@ -255,7 +248,7 @@ const MessageInput = ({ ticketStatus }) => { fromMe: true, mediaUrl: "", body: signMessage - ? `*${username}:*\n${inputMessage.trim()}` + ? `*${user?.name}:*\n${inputMessage.trim()}` : inputMessage.trim(), quotedMsg: replyingMessage, }; @@ -279,7 +272,7 @@ const MessageInput = ({ ticketStatus }) => { setRecording(true); setLoading(false); } catch (err) { - console.log(err); + toastError(err); setLoading(false); } }; @@ -314,7 +307,7 @@ const MessageInput = ({ ticketStatus }) => { await Mp3Recorder.stop().getMp3(); setRecording(false); } catch (err) { - console.log(err); + toastError(err); } }; @@ -428,8 +421,8 @@ const MessageInput = ({ ticketStatus }) => { { - setSignMessage(prevState => !prevState); + onChange={e => { + setSignMessage(e.target.checked); }} name="showAllTickets" color="primary" diff --git a/frontend/src/components/MessagesList/index.js b/frontend/src/components/MessagesList/index.js index d3fa2a0..e22af75 100644 --- a/frontend/src/components/MessagesList/index.js +++ b/frontend/src/components/MessagesList/index.js @@ -301,7 +301,7 @@ const reducer = (state, action) => { } }; -const MessagesList = ({ ticketId, isGroup, setReplyingMessage }) => { +const MessagesList = ({ ticketId, isGroup }) => { const classes = useStyles(); const [messagesList, dispatch] = useReducer(reducer, []); diff --git a/frontend/src/components/NewTicketModal/index.js b/frontend/src/components/NewTicketModal/index.js index 674f2e0..898aa92 100644 --- a/frontend/src/components/NewTicketModal/index.js +++ b/frontend/src/components/NewTicketModal/index.js @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useContext } from "react"; import { useHistory } from "react-router-dom"; import Button from "@material-ui/core/Button"; @@ -18,6 +18,7 @@ import api from "../../services/api"; import ButtonWithSpinner from "../ButtonWithSpinner"; import ContactModal from "../ContactModal"; import toastError from "../../errors/toastError"; +import { AuthContext } from "../../context/Auth/AuthContext"; const filter = createFilterOptions({ trim: true, @@ -25,7 +26,6 @@ const filter = createFilterOptions({ const NewTicketModal = ({ modalOpen, onClose }) => { const history = useHistory(); - const userId = +localStorage.getItem("userId"); const [options, setOptions] = useState([]); const [loading, setLoading] = useState(false); @@ -33,6 +33,7 @@ const NewTicketModal = ({ modalOpen, onClose }) => { const [selectedContact, setSelectedContact] = useState(null); const [newContact, setNewContact] = useState({}); const [contactModalOpen, setContactModalOpen] = useState(false); + const { user } = useContext(AuthContext); useEffect(() => { if (!modalOpen || searchParam.length < 3) { @@ -71,7 +72,7 @@ const NewTicketModal = ({ modalOpen, onClose }) => { try { const { data: ticket } = await api.post("/tickets", { contactId: contactId, - userId: userId, + userId: user.id, status: "open", }); history.push(`/tickets/${ticket.id}`); diff --git a/frontend/src/components/NotificationsPopOver/index.js b/frontend/src/components/NotificationsPopOver/index.js index 4c408aa..886e622 100644 --- a/frontend/src/components/NotificationsPopOver/index.js +++ b/frontend/src/components/NotificationsPopOver/index.js @@ -1,4 +1,4 @@ -import React, { useState, useRef, useEffect } from "react"; +import React, { useState, useRef, useEffect, useContext } from "react"; import { useHistory } from "react-router-dom"; import { format } from "date-fns"; @@ -18,6 +18,7 @@ import TicketListItem from "../TicketListItem"; import { i18n } from "../../translate/i18n"; import useTickets from "../../hooks/useTickets"; import alertSound from "../../assets/sound.mp3"; +import { AuthContext } from "../../context/Auth/AuthContext"; const useStyles = makeStyles(theme => ({ tabContainer: { @@ -43,7 +44,7 @@ const NotificationsPopOver = () => { const classes = useStyles(); const history = useHistory(); - const userId = +localStorage.getItem("userId"); + const { user } = useContext(AuthContext); const ticketIdUrl = +history.location.pathname.split("/")[2]; const ticketIdRef = useRef(ticketIdUrl); const anchorEl = useRef(); @@ -108,7 +109,7 @@ const NotificationsPopOver = () => { if ( data.action === "create" && !data.message.read && - (data.ticket.userId === userId || !data.ticket.userId) + (data.ticket.userId === user?.id || !data.ticket.userId) ) { setNotifications(prevState => { const ticketIndex = prevState.findIndex(t => t.id === data.ticket.id); @@ -122,7 +123,7 @@ const NotificationsPopOver = () => { const shouldNotNotificate = (data.message.ticketId === ticketIdRef.current && document.visibilityState === "visible") || - (data.ticket.userId && data.ticket.userId !== userId) || + (data.ticket.userId && data.ticket.userId !== user?.id) || data.ticket.isGroup; if (shouldNotNotificate) return; @@ -134,7 +135,7 @@ const NotificationsPopOver = () => { return () => { socket.disconnect(); }; - }, [history, userId]); + }, [history, user]); const handleNotifications = (data, history) => { const { message, contact, ticket } = data; diff --git a/frontend/src/components/QueueSelector/index.js b/frontend/src/components/QueueSelector/index.js index 545131c..f420105 100644 --- a/frontend/src/components/QueueSelector/index.js +++ b/frontend/src/components/QueueSelector/index.js @@ -41,7 +41,7 @@ const QueueSelector = ({ selectedQueueIds, onChange }) => { return (
- + Filas