Cursors.js 1.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. import React from "react";
  2. import { useC2C } from "../hooks/useC2C";
  3. import Cursor from "./Cursor";
  4. export const Cursors = ({ users }) => {
  5. const [c2c] = useC2C();
  6. const [cursors, setCursors] = React.useState({});
  7. const usersById = React.useMemo(() => {
  8. return users.reduce((acc, user) => {
  9. acc[user.id] = user;
  10. return acc;
  11. }, {});
  12. }, [users]);
  13. // Prevent race condition when removing user
  14. const currentCursor = React.useMemo(() => {
  15. return users.reduce((acc, user) => {
  16. if (cursors[user.id]) {
  17. acc[user.id] = cursors[user.id];
  18. }
  19. return acc;
  20. }, {});
  21. }, [users, cursors]);
  22. React.useEffect(() => {
  23. setCursors((prevCursors) => {
  24. return users.reduce((acc, user) => {
  25. if (prevCursors[user.id]) {
  26. acc[user.id] = prevCursors[user.id];
  27. }
  28. return acc;
  29. }, {});
  30. });
  31. }, [users]);
  32. React.useEffect(() => {
  33. const unsub = [];
  34. unsub.push(
  35. c2c.subscribe("cursorMove", ({ userId, pos }) => {
  36. //console.log('move', pos);
  37. setCursors((prevCursors) => {
  38. return {
  39. ...prevCursors,
  40. [userId]: pos,
  41. };
  42. });
  43. })
  44. );
  45. unsub.push(
  46. c2c.subscribe("cursorOff", ({ userId }) => {
  47. setCursors((prevCursors) => {
  48. const newCursors = {
  49. ...prevCursors,
  50. };
  51. delete newCursors[userId];
  52. return newCursors;
  53. });
  54. })
  55. );
  56. return () => {
  57. unsub.map((c) => c());
  58. };
  59. }, [c2c]);
  60. return (
  61. <div>
  62. {Object.entries(currentCursor).map(([userId, pos]) => (
  63. <Cursor
  64. key={userId}
  65. pos={pos}
  66. text={usersById[userId].name}
  67. color={usersById[userId].color}
  68. />
  69. ))}
  70. </div>
  71. );
  72. };
  73. export default Cursors;