diff --git a/stylesheets/components/InstallScreenProfileNameStep.scss b/stylesheets/components/InstallScreenProfileNameStep.scss index 13fbe0727..a4b0c1d4c 100644 --- a/stylesheets/components/InstallScreenProfileNameStep.scss +++ b/stylesheets/components/InstallScreenProfileNameStep.scss @@ -29,7 +29,7 @@ background: variables.$color-white; border-radius: 16px; padding: 48px; - max-width: 420px; + max-width: 520px; width: 100%; box-shadow: 0 4px 24px rgba(0, 0, 0, 0.15); text-align: center; diff --git a/ts/components/installScreen/InstallScreenCaptchaStep.dom.tsx b/ts/components/installScreen/InstallScreenCaptchaStep.dom.tsx index 4b9fed19b..5eaf7ed26 100644 --- a/ts/components/installScreen/InstallScreenCaptchaStep.dom.tsx +++ b/ts/components/installScreen/InstallScreenCaptchaStep.dom.tsx @@ -2,6 +2,7 @@ // SPDX-License-Identifier: AGPL-3.0-only import React, { type ReactElement, useState, useCallback } from 'react'; +import copyText from 'copy-text-to-clipboard'; import { Button, ButtonVariant } from '../Button.dom.js'; import { TitlebarDragArea } from '../TitlebarDragArea.dom.js'; @@ -30,15 +31,18 @@ export function InstallScreenCaptchaStep({ }: Props): ReactElement { const [isWaitingForCaptcha, setIsWaitingForCaptcha] = useState(false); const [captchaError, setCaptchaError] = useState(null); + const [isCopied, setIsCopied] = useState(false); + + // Shared function to start waiting for captcha and register handler + const startWaitingForCaptcha = useCallback(async () => { + if (isWaitingForCaptcha) { + // Already waiting, don't start another handler + return; + } - const handleOpenCaptcha = useCallback(async () => { setIsWaitingForCaptcha(true); setCaptchaError(null); - // Open the captcha URL in external browser - // This will be intercepted by Electron's will-navigate handler - document.location.href = CAPTCHA_URL; - try { // Wait for the captcha token to come back via the signalcaptcha:// URL const token = await requestCaptcha(); @@ -47,7 +51,25 @@ export function InstallScreenCaptchaStep({ setCaptchaError('Failed to complete captcha. Please try again.'); setIsWaitingForCaptcha(false); } - }, [requestCaptcha, onCaptchaComplete]); + }, [isWaitingForCaptcha, requestCaptcha, onCaptchaComplete]); + + const handleOpenCaptcha = useCallback(async () => { + // Open the captcha URL in external browser + // This will be intercepted by Electron's will-navigate handler + document.location.href = CAPTCHA_URL; + + // Start waiting for the captcha response + await startWaitingForCaptcha(); + }, [startWaitingForCaptcha]); + + const handleCopyUrl = useCallback(async () => { + copyText(CAPTCHA_URL); + setIsCopied(true); + setTimeout(() => setIsCopied(false), 2000); + + // Also start waiting for the captcha response + await startWaitingForCaptcha(); + }, [startWaitingForCaptcha]); return (
@@ -74,7 +96,7 @@ export function InstallScreenCaptchaStep({
- Complete the captcha in your browser, then click "Open + Complete the CAPTCHA in your browser, then click "Open Signal"
@@ -86,7 +108,14 @@ export function InstallScreenCaptchaStep({ variant={ButtonVariant.Primary} disabled={isSubmitting || isWaitingForCaptcha} > - {isWaitingForCaptcha ? 'Waiting for captcha...' : 'Open Captcha'} + {isWaitingForCaptcha ? 'Waiting for CAPTCHA...' : 'Open CAPTCHA'} + +