improvement: remove tickets logic from useTickets to improve notifications

This commit is contained in:
canove
2020-09-25 18:53:14 -03:00
parent c7d3807219
commit 5396837db4
10 changed files with 190 additions and 163 deletions

View File

@@ -66,7 +66,7 @@ export const store = async (req: Request, res: Response): Promise<Response> => {
});
const io = getIO();
io.to(ticketId).to("notification").emit("appMessage", {
io.to(ticketId).to("notification").to(ticket.status).emit("appMessage", {
action: "create",
message,
ticket,

View File

@@ -18,6 +18,7 @@ type IndexQuery = {
interface TicketData {
contactId: number;
status: string;
userId: number;
}
export const index = async (req: Request, res: Response): Promise<Response> => {
@@ -46,12 +47,12 @@ export const index = async (req: Request, res: Response): Promise<Response> => {
};
export const store = async (req: Request, res: Response): Promise<Response> => {
const { contactId, status }: TicketData = req.body;
const { contactId, status, userId }: TicketData = req.body;
const ticket = await CreateTicketService({ contactId, status });
const ticket = await CreateTicketService({ contactId, status, userId });
const io = getIO();
io.to("notification").emit("ticket", {
io.to(ticket.status).emit("ticket", {
action: "create",
ticket
});
@@ -66,10 +67,21 @@ export const update = async (
const { ticketId } = req.params;
const ticketData: TicketData = req.body;
const ticket = await UpdateTicketService({ ticketData, ticketId });
const { ticket, oldStatus } = await UpdateTicketService({
ticketData,
ticketId
});
const io = getIO();
io.to("notification").emit("ticket", {
if (ticket.status !== oldStatus) {
io.to(oldStatus).emit("ticket", {
action: "delete",
ticketId: ticket.id
});
}
io.to(ticket.status).emit("ticket", {
action: "updateStatus",
ticket
});
@@ -83,13 +95,15 @@ export const remove = async (
): Promise<Response> => {
const { ticketId } = req.params;
await DeleteTicketService(ticketId);
const ticket = await DeleteTicketService(ticketId);
const io = getIO();
io.to("notification").emit("ticket", {
action: "delete",
ticketId: +ticketId
});
io.to(ticket.status)
.to("notification")
.emit("ticket", {
action: "delete",
ticketId: +ticketId
});
return res.status(200).json({ message: "ticket deleted" });
};

View File

@@ -24,7 +24,7 @@ const SetTicketMessagesAsRead = async (ticket: Ticket): Promise<void> => {
}
const io = getIO();
io.to("notification").emit("ticket", {
io.to(ticket.status).to("notification").emit("ticket", {
action: "updateUnread",
ticketId: ticket.id
});

View File

@@ -18,6 +18,11 @@ export const initIO = (httpServer: Server): SocketIO => {
socket.join("notification");
});
socket.on("joinTickets", status => {
console.log(`A client joined to ${status} tickets channel.`);
socket.join(status);
});
socket.on("disconnect", () => {
console.log("Client disconnected");
});

View File

@@ -4,12 +4,14 @@ import Ticket from "../../models/Ticket";
interface Request {
contactId: number;
status?: string;
status: string;
userId: number;
}
const CreateTicketService = async ({
contactId,
status
status,
userId
}: Request): Promise<Ticket> => {
const defaultWhatsapp = await GetDefaultWhatsApp();
@@ -19,7 +21,8 @@ const CreateTicketService = async ({
const { id }: Ticket = await defaultWhatsapp.$create("ticket", {
contactId,
status
status,
userId
});
const ticket = await Ticket.findByPk(id, { include: ["contact"] });

View File

@@ -1,7 +1,7 @@
import Ticket from "../../models/Ticket";
import AppError from "../../errors/AppError";
const DeleteTicketService = async (id: string): Promise<void> => {
const DeleteTicketService = async (id: string): Promise<Ticket> => {
const ticket = await Ticket.findOne({
where: { id }
});
@@ -11,6 +11,8 @@ const DeleteTicketService = async (id: string): Promise<void> => {
}
await ticket.destroy();
return ticket;
};
export default DeleteTicketService;

View File

@@ -12,10 +12,15 @@ interface Request {
ticketId: string;
}
interface Response {
ticket: Ticket;
oldStatus: string;
}
const UpdateTicketService = async ({
ticketData,
ticketId
}: Request): Promise<Ticket> => {
}: Request): Promise<Response> => {
const { status, userId } = ticketData;
const ticket = await Ticket.findOne({
@@ -33,12 +38,14 @@ const UpdateTicketService = async ({
throw new AppError("No ticket found with this ID.", 404);
}
const oldStatus = ticket.status;
await ticket.update({
status,
userId
});
return ticket;
return { ticket, oldStatus };
};
export default UpdateTicketService;

View File

@@ -138,12 +138,15 @@ const handleMessage = async (
}
const io = getIO();
io.to(ticket.id.toString()).to("notification").emit("appMessage", {
action: "create",
message: newMessage,
ticket,
contact
});
io.to(ticket.id.toString())
.to(ticket.status)
.to("notification")
.emit("appMessage", {
action: "create",
message: newMessage,
ticket,
contact
});
};
const isValidMsg = (msg: WbotMessage): boolean => {

View File

@@ -1,4 +1,5 @@
import React, { useState, useEffect } from "react";
import React, { useState, useEffect, useReducer } from "react";
import openSocket from "socket.io-client";
import { makeStyles } from "@material-ui/core/styles";
import List from "@material-ui/core/List";
@@ -69,11 +70,84 @@ const useStyles = makeStyles(theme => ({
},
}));
const reducer = (state, action) => {
if (action.type === "LOAD_TICKETS") {
const newTickets = action.payload;
newTickets.forEach(ticket => {
const ticketIndex = state.findIndex(t => t.id === ticket.id);
if (ticketIndex !== -1) {
state[ticketIndex] = ticket;
if (ticket.unreadMessages > 0) {
state.unshift(state.splice(ticketIndex, 1)[0]);
}
} else {
state.push(ticket);
}
});
return [...state];
}
if (action.type === "RESET_UNREAD") {
const ticketId = action.payload;
const ticketIndex = state.findIndex(t => t.id === ticketId);
if (ticketIndex !== -1) {
state[ticketIndex].unreadMessages = 0;
}
return [...state];
}
if (action.type === "UPDATE_TICKET") {
const ticket = action.payload;
const ticketIndex = state.findIndex(t => t.id === ticket.id);
if (ticketIndex !== -1) {
state[ticketIndex] = ticket;
} else {
state.unshift(ticket);
}
return [...state];
}
if (action.type === "UPDATE_TICKET_MESSAGES_COUNT") {
const ticket = action.payload;
const ticketIndex = state.findIndex(t => t.id === ticket.id);
if (ticketIndex !== -1) {
state[ticketIndex] = ticket;
state.unshift(state.splice(ticketIndex, 1)[0]);
}
return [...state];
}
if (action.type === "DELETE_TICKET") {
const ticketId = action.payload;
const ticketIndex = state.findIndex(t => t.id === ticketId);
if (ticketIndex !== -1) {
state.splice(ticketIndex, 1);
}
return [...state];
}
if (action.type === "RESET") {
return [];
}
};
const TicketsList = ({ status, searchParam, showAll }) => {
const userId = +localStorage.getItem("userId");
const classes = useStyles();
const [pageNumber, setPageNumber] = useState(1);
const [ticketsList, dispatch] = useReducer(reducer, []);
const { tickets, hasMore, loading, dispatch } = useTickets({
useEffect(() => {
dispatch({ type: "RESET" });
setPageNumber(1);
}, [status, searchParam, dispatch, showAll]);
const { tickets, hasMore, loading } = useTickets({
pageNumber,
searchParam,
status,
@@ -81,9 +155,52 @@ const TicketsList = ({ status, searchParam, showAll }) => {
});
useEffect(() => {
dispatch({ type: "RESET" });
setPageNumber(1);
}, [status, searchParam, dispatch, showAll]);
dispatch({
type: "LOAD_TICKETS",
payload: tickets,
});
}, [tickets]);
useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
socket.emit("joinTickets", status);
socket.on("ticket", data => {
if (data.action === "updateUnread") {
dispatch({
type: "RESET_UNREAD",
payload: data.ticketId,
});
}
if (
(data.action === "updateStatus" || data.action === "create") &&
(!data.ticket.userId || data.ticket.userId === userId || showAll)
) {
dispatch({
type: "UPDATE_TICKET",
payload: data.ticket,
});
}
if (data.action === "delete") {
dispatch({ type: "DELETE_TICKET", payload: data.ticketId });
}
});
socket.on("appMessage", data => {
if (data.action === "create") {
dispatch({
type: "UPDATE_TICKET_MESSAGES_COUNT",
payload: data.ticket,
});
}
});
return () => {
socket.disconnect();
};
}, [status, showAll, userId]);
const loadMore = () => {
setPageNumber(prevState => prevState + 1);
@@ -91,7 +208,9 @@ const TicketsList = ({ status, searchParam, showAll }) => {
const handleScroll = e => {
if (!hasMore || loading) return;
const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;
if (scrollHeight - (scrollTop + 100) < clientHeight) {
loadMore();
}
@@ -110,16 +229,16 @@ const TicketsList = ({ status, searchParam, showAll }) => {
{status === "open" && (
<div className={classes.ticketsListHeader}>
{i18n.t("ticketsList.assignedHeader")}
<span className={classes.ticketsCount}>{tickets.length}</span>
<span className={classes.ticketsCount}>{ticketsList.length}</span>
</div>
)}
{status === "pending" && (
<div className={classes.ticketsListHeader}>
{i18n.t("ticketsList.pendingHeader")}
<span className={classes.ticketsCount}>{tickets.length}</span>
<span className={classes.ticketsCount}>{ticketsList.length}</span>
</div>
)}
{tickets.length === 0 && !loading ? (
{ticketsList.length === 0 && !loading ? (
<div className={classes.noTicketsDiv}>
<span className={classes.noTicketsTitle}>
{i18n.t("ticketsList.noTicketsTitle")}
@@ -130,7 +249,7 @@ const TicketsList = ({ status, searchParam, showAll }) => {
</div>
) : (
<>
{tickets.map(ticket => (
{ticketsList.map(ticket => (
<TicketListItem ticket={ticket} key={ticket.id} />
))}
</>

View File

@@ -1,80 +1,8 @@
import { useState, useEffect, useReducer } from "react";
import openSocket from "socket.io-client";
import { useState, useEffect } from "react";
import { toast } from "react-toastify";
import api from "../../services/api";
const reducer = (state, action) => {
if (action.type === "LOAD_TICKETS") {
const newTickets = action.payload;
newTickets.forEach(ticket => {
const ticketIndex = state.findIndex(t => t.id === ticket.id);
if (ticketIndex !== -1) {
state[ticketIndex] = ticket;
if (ticket.unreadMessages > 0) {
state.unshift(state.splice(ticketIndex, 1)[0]);
}
} else {
state.push(ticket);
}
});
return [...state];
}
if (action.type === "UPDATE_TICKETS") {
const { ticket, status, loggedUser, withUnreadMessages } = action.payload;
const ticketIndex = state.findIndex(t => t.id === ticket.id);
if (ticketIndex !== -1) {
if (ticket.status !== state[ticketIndex].status) {
state.splice(ticketIndex, 1);
} else {
state[ticketIndex] = ticket;
state.unshift(state.splice(ticketIndex, 1)[0]);
}
} else if (
ticket.status === status &&
(ticket.userId === loggedUser ||
!ticket.userId ||
ticket.status === "closed")
) {
state.unshift(ticket);
} else if (withUnreadMessages) {
state.unshift(ticket);
}
return [...state];
}
if (action.type === "DELETE_TICKET") {
const ticketId = action.payload;
const ticketIndex = state.findIndex(t => t.id === ticketId);
if (ticketIndex !== -1) {
state.splice(ticketIndex, 1);
}
return [...state];
}
if (action.type === "RESET_UNREAD") {
const { ticketId, withUnreadMessages } = action.payload;
const ticketIndex = state.findIndex(t => t.id === ticketId);
if (ticketIndex !== -1) {
state[ticketIndex].unreadMessages = 0;
if (withUnreadMessages) {
state.splice(ticketIndex, 1);
}
}
return [...state];
}
if (action.type === "RESET") {
return [];
}
};
const useTickets = ({
searchParam,
pageNumber,
@@ -83,10 +11,9 @@ const useTickets = ({
showAll,
withUnreadMessages,
}) => {
const userId = +localStorage.getItem("userId");
const [loading, setLoading] = useState(true);
const [hasMore, setHasMore] = useState(false);
const [tickets, dispatch] = useReducer(reducer, []);
const [tickets, setTickets] = useState([]);
useEffect(() => {
setLoading(true);
@@ -103,10 +30,7 @@ const useTickets = ({
withUnreadMessages,
},
});
dispatch({
type: "LOAD_TICKETS",
payload: data.tickets,
});
setTickets(data.tickets);
setHasMore(data.hasMore);
setLoading(false);
} catch (err) {
@@ -121,57 +45,7 @@ const useTickets = ({
return () => clearTimeout(delayDebounceFn);
}, [searchParam, pageNumber, status, date, showAll, withUnreadMessages]);
useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
socket.emit("joinNotification");
socket.on("ticket", data => {
if (data.action === "updateUnread") {
dispatch({
type: "RESET_UNREAD",
payload: {
ticketId: data.ticketId,
withUnreadMessages: withUnreadMessages,
},
});
}
if (data.action === "updateStatus" || data.action === "create") {
dispatch({
type: "UPDATE_TICKETS",
payload: {
ticket: data.ticket,
status: status,
loggedUser: userId,
},
});
}
if (data.action === "delete") {
dispatch({ type: "DELETE_TICKET", payload: data.ticketId });
}
});
socket.on("appMessage", data => {
if (data.action === "create") {
dispatch({
type: "UPDATE_TICKETS",
payload: {
ticket: data.ticket,
status: status,
withUnreadMessages: withUnreadMessages,
loggedUser: userId,
},
});
}
});
return () => {
socket.disconnect();
};
}, [status, withUnreadMessages, userId]);
return { loading, tickets, hasMore, dispatch };
return { tickets, loading, hasMore };
};
export default useTickets;