useMessage.js 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. import React from "react";
  2. import { nanoid } from "nanoid";
  3. import { atom, useRecoilState, useRecoilCallback } from "recoil";
  4. import dayjs from "dayjs";
  5. import { useUsers } from "../users";
  6. import useC2C from "../../components/hooks/useC2C";
  7. export const MessagesAtom = atom({
  8. key: "messages",
  9. default: [],
  10. });
  11. const generateMsg = ({ user: { name, uid, color }, content }) => {
  12. const newMessage = {
  13. type: "message",
  14. user: { name, uid, color },
  15. content,
  16. uid: nanoid(),
  17. timestamp: dayjs().toISOString(),
  18. };
  19. return newMessage;
  20. };
  21. export const parseMessage = (message) => {
  22. try {
  23. return {
  24. ...message,
  25. timestamp: dayjs(message.timestamp),
  26. };
  27. } catch (e) {
  28. console.warn("Discard message as it can't be decoded", e);
  29. }
  30. return null;
  31. };
  32. const noop = () => {};
  33. const useMessage = (onMessage = noop) => {
  34. const [messages, setMessagesState] = useRecoilState(MessagesAtom);
  35. const { c2c, isMaster } = useC2C("board");
  36. const { currentUser } = useUsers();
  37. const getMessage = useRecoilCallback(
  38. ({ snapshot }) => async () => {
  39. const currentMessages = await snapshot.getPromise(MessagesAtom);
  40. return currentMessages;
  41. },
  42. []
  43. );
  44. const setMessages = React.useCallback(
  45. (newMessages) => {
  46. setMessagesState(newMessages.map((m) => parseMessage(m)));
  47. },
  48. [setMessagesState]
  49. );
  50. const initEvents = React.useCallback(
  51. (unsub) => {
  52. unsub.push(
  53. c2c.subscribe("newMessage", (newMessage) => {
  54. setMessagesState((prevMessages) => [
  55. ...prevMessages,
  56. parseMessage(newMessage),
  57. ]);
  58. onMessage(newMessage);
  59. })
  60. );
  61. if (isMaster) {
  62. c2c.register("getMessageHistory", getMessage).then((unregister) => {
  63. unsub.push(unregister);
  64. });
  65. } else {
  66. c2c.call("getMessageHistory").then((messageHistory) => {
  67. setMessages(messageHistory);
  68. });
  69. }
  70. },
  71. [c2c, getMessage, isMaster, onMessage, setMessages, setMessagesState]
  72. );
  73. React.useEffect(() => {
  74. const unsub = [];
  75. initEvents(unsub);
  76. return () => {
  77. unsub.forEach((u) => u());
  78. };
  79. }, [initEvents]);
  80. const sendMessage = React.useCallback(
  81. (messageContent) => {
  82. const newMessage = generateMsg({
  83. user: currentUser,
  84. content: messageContent,
  85. });
  86. if (newMessage) c2c.publish("newMessage", newMessage, true);
  87. },
  88. [c2c, currentUser]
  89. );
  90. return { messages, setMessages, sendMessage };
  91. };
  92. export default useMessage;