RoomDialog.tsx 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. import React, { useState, useEffect, useRef } from "react";
  2. import { ToolButton } from "./ToolButton";
  3. import { Island } from "./Island";
  4. import { t } from "../i18n";
  5. import useIsMobile from "../is-mobile";
  6. import { users, clipboard, start, stop } from "./icons";
  7. import { Modal } from "./Modal";
  8. import { copyTextToSystemClipboard } from "../clipboard";
  9. import { AppState } from "../types";
  10. import "./RoomDialog.scss";
  11. function RoomModal({
  12. onCloseRequest,
  13. activeRoomLink,
  14. onRoomCreate,
  15. onRoomDestroy,
  16. }: {
  17. onCloseRequest: () => void;
  18. activeRoomLink: string;
  19. onRoomCreate: () => void;
  20. onRoomDestroy: () => void;
  21. }) {
  22. const roomLinkInput = useRef<HTMLInputElement>(null);
  23. function copyRoomLink() {
  24. copyTextToSystemClipboard(activeRoomLink);
  25. if (roomLinkInput.current) {
  26. roomLinkInput.current.select();
  27. }
  28. }
  29. function selectInput(event: React.MouseEvent<HTMLInputElement>) {
  30. if (event.target !== document.activeElement) {
  31. event.preventDefault();
  32. (event.target as HTMLInputElement).select();
  33. }
  34. }
  35. return (
  36. <div className="RoomDialog-modal">
  37. <Island padding={4}>
  38. <button
  39. className="Modal__close"
  40. onClick={onCloseRequest}
  41. aria-label={t("buttons.close")}
  42. >
  43. </button>
  44. <h2 id="export-title">{t("labels.createRoom")}</h2>
  45. {!activeRoomLink && (
  46. <>
  47. <p>{t("roomDialog.desc_intro")}</p>
  48. <p>{`🔒 ${t("roomDialog.desc_privacy")}`}</p>
  49. <p>{t("roomDialog.desc_start")}</p>
  50. <div className="RoomDialog-sessionStartButtonContainer">
  51. <ToolButton
  52. className="RoomDialog-startSession"
  53. type="button"
  54. icon={start}
  55. title={t("roomDialog.button_startSession")}
  56. aria-label={t("roomDialog.button_startSession")}
  57. showAriaLabel={true}
  58. onClick={onRoomCreate}
  59. />
  60. </div>
  61. </>
  62. )}
  63. {activeRoomLink && (
  64. <>
  65. <p>{t("roomDialog.desc_inProgressIntro")}</p>
  66. <p>{t("roomDialog.desc_shareLink")}</p>
  67. <div className="RoomDialog-linkContainer">
  68. <ToolButton
  69. type="button"
  70. icon={clipboard}
  71. title={t("labels.copy")}
  72. aria-label={t("labels.copy")}
  73. onClick={copyRoomLink}
  74. />
  75. <input
  76. value={activeRoomLink}
  77. readOnly={true}
  78. className="RoomDialog-link"
  79. ref={roomLinkInput}
  80. onPointerDown={selectInput}
  81. />
  82. </div>
  83. <p>{`🔒 ${t("roomDialog.desc_privacy")}`}</p>
  84. <p>
  85. <span role="img" aria-hidden="true">
  86. ⚠️
  87. </span>{" "}
  88. {t("roomDialog.desc_persistenceWarning")}
  89. </p>
  90. <p>{t("roomDialog.desc_exitSession")}</p>
  91. <div className="RoomDialog-sessionStartButtonContainer">
  92. <ToolButton
  93. className="RoomDialog-stopSession"
  94. type="button"
  95. icon={stop}
  96. title={t("roomDialog.button_stopSession")}
  97. aria-label={t("roomDialog.button_stopSession")}
  98. showAriaLabel={true}
  99. onClick={onRoomDestroy}
  100. />
  101. </div>
  102. </>
  103. )}
  104. </Island>
  105. </div>
  106. );
  107. }
  108. export function RoomDialog({
  109. isCollaborating,
  110. collaboratorCount,
  111. onRoomCreate,
  112. onRoomDestroy,
  113. }: {
  114. isCollaborating: AppState["isCollaborating"];
  115. collaboratorCount: AppState["collaboratorCount"];
  116. onRoomCreate: () => void;
  117. onRoomDestroy: () => void;
  118. }) {
  119. const [modalIsShown, setModalIsShown] = useState(false);
  120. const [activeRoomLink, setActiveRoomLink] = useState("");
  121. const triggerButton = useRef<HTMLButtonElement>(null);
  122. const handleClose = React.useCallback(() => {
  123. setModalIsShown(false);
  124. triggerButton.current?.focus();
  125. }, []);
  126. useEffect(() => {
  127. setActiveRoomLink(isCollaborating ? window.location.href : "");
  128. }, [isCollaborating]);
  129. return (
  130. <>
  131. <ToolButton
  132. className={`RoomDialog-modalButton ${
  133. isCollaborating ? "is-collaborating" : ""
  134. }`}
  135. onClick={() => setModalIsShown(true)}
  136. icon={users}
  137. type="button"
  138. title={t("buttons.roomDialog")}
  139. aria-label={t("buttons.roomDialog")}
  140. showAriaLabel={useIsMobile()}
  141. ref={triggerButton}
  142. >
  143. {collaboratorCount > 0 && (
  144. <div className="RoomDialog-modalButton-collaborators">
  145. {collaboratorCount}
  146. </div>
  147. )}
  148. </ToolButton>
  149. {modalIsShown && (
  150. <Modal
  151. maxWidth={800}
  152. labelledBy="room-title"
  153. onCloseRequest={handleClose}
  154. >
  155. <RoomModal
  156. onCloseRequest={handleClose}
  157. activeRoomLink={activeRoomLink}
  158. onRoomCreate={onRoomCreate}
  159. onRoomDestroy={onRoomDestroy}
  160. />
  161. </Modal>
  162. )}
  163. </>
  164. );
  165. }