Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/components/common/Recaptcha/Recaptcha.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,15 @@ interface RecaptchaProps {
siteKey: string
className?: string
onChange?: (token: string) => void
onExpire?: () => void
}

export const Recaptcha = forwardRef<RecaptchaHandle, RecaptchaProps>(
function Recaptcha({ siteKey, className, onChange }, ref) {
function Recaptcha({ siteKey, className, onChange, onExpire }, ref) {
const { containerRef, getToken, reset } = useRecaptcha({
siteKey,
onChange,
onExpire,
})

useImperativeHandle(
Expand Down
40 changes: 38 additions & 2 deletions src/hooks/useContactForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
uploadToPresignedUrl,
createServiceRequestWithKeys,
} from '@/utils/upload'
import { captureAppException } from '@/lib/sentry'
import { FetchClientError } from '@/utils/fetchClient'
import {
MAX_TOTAL_UPLOAD_BYTES,
MAX_FILES,
Expand Down Expand Up @@ -116,6 +118,18 @@ export const useContactForm = (options: UseContactFormOptions) => {
setRecaptchaCompleted(true)
}, [])

const handleRecaptchaExpired = useCallback(() => {
setRecaptchaCompleted(false)
setFieldErrors(prev => ({
...prev,
recaptcha: 'reCAPTCHA verification expired. Please verify again.',
}))
captureAppException(new Error('reCAPTCHA verification expired'), {
source: 'app',
tags: { 'recaptcha.event': 'expired' },
})
}, [])

const addFiles = useCallback(
(newFiles: File[]) => {
const nonEmpty = newFiles.filter(file => file.size > 0)
Expand Down Expand Up @@ -390,9 +404,19 @@ export const useContactForm = (options: UseContactFormOptions) => {
status: 'error',
error: 'No upload URL was returned for this file.',
})
return Promise.reject(
new Error('No upload URL was returned for this file.')
const error = new Error(
'No upload URL was returned for this file.'
)
captureAppException(error, {
source: 'support-api',
tags: { 'upload.stage': 'presign_mismatch' },
extra: {
pendingCount: pendingItems.length,
presignedCount: presigned.length,
missingIndex: index,
},
})
return Promise.reject(error)
}
setAttachmentStatus(item.id, {
status: 'uploading',
Expand Down Expand Up @@ -425,6 +449,10 @@ export const useContactForm = (options: UseContactFormOptions) => {
error:
err instanceof Error ? err.message : 'Upload failed',
})
captureAppException(err, {
source: 'support-api',
tags: { 'upload.stage': 'presigned_put' },
})
return Promise.reject(err)
})
})
Expand Down Expand Up @@ -468,6 +496,13 @@ export const useContactForm = (options: UseContactFormOptions) => {
const msg =
rawMessage && rawMessage !== 'Failed to fetch' ? rawMessage : fallback
trackSupportFormFailed(typeOfEnquiry || 'Unknown', msg)
// FetchClientError failures are already captured with request context in fetchClient.ts
if (!(err instanceof FetchClientError)) {
captureAppException(err, {
source: 'support-api',
tags: { 'contact.stage': 'submit' },
})
}
setSubmitError(msg)
} finally {
setIsSubmitting(false)
Expand Down Expand Up @@ -512,6 +547,7 @@ export const useContactForm = (options: UseContactFormOptions) => {
validateTypeOfEnquiry,
validateDescription,
clearRecaptchaError,
handleRecaptchaExpired,
onSubmit,
isFormValid,
}
Expand Down
5 changes: 5 additions & 0 deletions src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -4331,6 +4331,11 @@ p.small {
height: 24px;
}

.connect-magiclink-button-icon {
width: 24px;
height: 24px;
}

.connect-metamask-button-text {
display: flex;
flex-direction: row;
Expand Down
2 changes: 2 additions & 0 deletions src/pages/Contact/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const Contact = ({ isDarkMode }: ContactProps) => {
handleDrop,
handleFileInput,
clearRecaptchaError,
handleRecaptchaExpired,
onSubmit,
} = useContactForm({
getRecaptchaToken: () =>
Expand Down Expand Up @@ -169,6 +170,7 @@ const Contact = ({ isDarkMode }: ContactProps) => {
siteKey={RECAPTCHA_SITE_KEY}
className="flex justify-center"
onChange={clearRecaptchaError}
onExpire={handleRecaptchaExpired}
/>
{fieldErrors.recaptcha && (
<FieldError
Expand Down
Loading