|
1 | 1 | import { useSession } from "next-auth/react"; |
2 | 2 | import posthog from "posthog-js"; |
3 | 3 | import type { FormEvent } from "react"; |
4 | | -import { useMemo, useRef, useState } from "react"; |
| 4 | +import { useCallback, useMemo, useRef, useState } from "react"; |
5 | 5 | import { Controller, useForm } from "react-hook-form"; |
6 | 6 |
|
7 | 7 | import TeamInviteFromOrg from "~/ee/organizations/components/TeamInviteFromOrg"; |
@@ -89,6 +89,7 @@ export default function MemberInvitationModal(props: MemberInvitationModalProps) |
89 | 89 | const [modalImportMode, setModalInputMode] = useState<ModalMode>( |
90 | 90 | canSeeOrganization ? "ORGANIZATION" : "INDIVIDUAL" |
91 | 91 | ); |
| 92 | + const [isCopying, setIsCopying] = useState(false); |
92 | 93 |
|
93 | 94 | const createInviteMutation = trpc.viewer.teams.createInvite.useMutation({ |
94 | 95 | async onSuccess() { |
@@ -193,6 +194,48 @@ export default function MemberInvitationModal(props: MemberInvitationModalProps) |
193 | 194 |
|
194 | 195 | const importRef = useRef<HTMLInputElement | null>(null); |
195 | 196 |
|
| 197 | +const handleCopyInviteLink = useCallback(async () => { |
| 198 | + if (isCopying || createInviteMutation.isPending) return; |
| 199 | + |
| 200 | + setIsCopying(true); |
| 201 | + try { |
| 202 | + // Required for Safari but also works on Chrome |
| 203 | + // Credits to https://wolfgangrittner.dev/how-to-use-clipboard-api-in-firefox/ |
| 204 | + if (typeof ClipboardItem !== "undefined") { |
| 205 | + const inviteLinkClipboardItem = new ClipboardItem({ |
| 206 | + //eslint-disable-next-line no-async-promise-executor |
| 207 | + "text/plain": new Promise((resolve, reject) => { |
| 208 | + // Instead of doing async work and then writing to clipboard, do async work in clipboard API itself |
| 209 | + createInviteMutation.mutateAsync({ |
| 210 | + teamId: props.teamId, |
| 211 | + token: props.token, |
| 212 | + }).then(({ inviteLink }) => { |
| 213 | + resolve(new Blob([inviteLink], { type: "text/plain" })); |
| 214 | + }) |
| 215 | + .catch((err) => { |
| 216 | + reject(err); |
| 217 | + }); |
| 218 | + }), |
| 219 | + }); |
| 220 | + await navigator.clipboard.write([inviteLinkClipboardItem]); |
| 221 | + showToast(t("invite_link_copied"), "success"); |
| 222 | + } else { |
| 223 | + // Fallback for browsers that don't support ClipboardItem e.g. Firefox |
| 224 | + const { inviteLink } = await createInviteMutation.mutateAsync({ |
| 225 | + teamId: props.teamId, |
| 226 | + token: props.token, |
| 227 | + }); |
| 228 | + await navigator.clipboard.writeText(inviteLink); |
| 229 | + showToast(t("invite_link_copied"), "success"); |
| 230 | + } |
| 231 | + } catch (e) { |
| 232 | + showToast(t("something_went_wrong_on_our_end"), "error"); |
| 233 | + console.error(e); |
| 234 | + } finally { |
| 235 | + setIsCopying(false); |
| 236 | + } |
| 237 | + }, [isCopying, createInviteMutation, props.teamId, props.token, t]); |
| 238 | + |
196 | 239 | return ( |
197 | 240 | <Dialog |
198 | 241 | name="inviteModal" |
@@ -412,38 +455,9 @@ export default function MemberInvitationModal(props: MemberInvitationModalProps) |
412 | 455 | type="button" |
413 | 456 | color="minimal" |
414 | 457 | variant="icon" |
415 | | - onClick={async function () { |
416 | | - try { |
417 | | - // Required for Safari but also works on Chrome |
418 | | - // Credits to https://wolfgangrittner.dev/how-to-use-clipboard-api-in-firefox/ |
419 | | - if (typeof ClipboardItem !== "undefined") { |
420 | | - const inviteLinkClipbardItem = new ClipboardItem({ |
421 | | - //eslint-disable-next-line no-async-promise-executor |
422 | | - "text/plain": new Promise(async (resolve) => { |
423 | | - // Instead of doing async work and then writing to clipboard, do async work in clipboard API itself |
424 | | - const { inviteLink } = await createInviteMutation.mutateAsync({ |
425 | | - teamId: props.teamId, |
426 | | - token: props.token, |
427 | | - }); |
428 | | - showToast(t("invite_link_copied"), "success"); |
429 | | - resolve(new Blob([inviteLink], { type: "text/plain" })); |
430 | | - }), |
431 | | - }); |
432 | | - await navigator.clipboard.write([inviteLinkClipbardItem]); |
433 | | - } else { |
434 | | - // Fallback for browsers that don't support ClipboardItem e.g. Firefox |
435 | | - const { inviteLink } = await createInviteMutation.mutateAsync({ |
436 | | - teamId: props.teamId, |
437 | | - token: props.token, |
438 | | - }); |
439 | | - await navigator.clipboard.writeText(inviteLink); |
440 | | - showToast(t("invite_link_copied"), "success"); |
441 | | - } |
442 | | - } catch (e) { |
443 | | - showToast(t("something_went_wrong_on_our_end"), "error"); |
444 | | - console.error(e); |
445 | | - } |
446 | | - }} |
| 458 | + onClick={handleCopyInviteLink} |
| 459 | + loading={isCopying || createInviteMutation.isPending} |
| 460 | + disabled={isCopying || createInviteMutation.isPending} |
447 | 461 | className={classNames("gap-2", props.token && "opacity-50")} |
448 | 462 | StartIcon="link" |
449 | 463 | data-testid="copy-invite-link-button"> |
|
0 commit comments