Skip to content

Commit 6ae1d98

Browse files
committed
feat(contact): add browser detection and mobile-specific handling
- Implement comprehensive browser detection (Chrome, Firefox, Safari, Edge) - Add mobile device detection (iOS, Android, general mobile) - Optimize email opening methods per browser and device type - Mobile: Use location.href (most reliable on mobile devices) - Chrome: Multiple fallback methods for strict security policies - Firefox/Safari/Edge: Direct window.open (works well) - Customize success messages and UI for mobile vs desktop - Mobile-friendly fallback modal with full-width buttons - Shorter timeout delays on mobile devices - Platform-specific messaging and instructions
1 parent d27977e commit 6ae1d98

1 file changed

Lines changed: 99 additions & 23 deletions

File tree

src/components/Contact.tsx

Lines changed: 99 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { motion } from 'framer-motion';
22
import type React from 'react';
3-
import { useState } from 'react';
3+
import { useState, useEffect } from 'react';
44

55
const Contact: React.FC = () => {
66
const [formData, setFormData] = useState({
@@ -12,6 +12,15 @@ const Contact: React.FC = () => {
1212
const [submitStatus, setSubmitStatus] = useState<'idle' | 'success' | 'error'>('idle');
1313
const [showEmailFallback, setShowEmailFallback] = useState(false);
1414
const [emailContent, setEmailContent] = useState('');
15+
const [browserInfo, setBrowserInfo] = useState({
16+
isChrome: false,
17+
isFirefox: false,
18+
isSafari: false,
19+
isEdge: false,
20+
isMobile: false,
21+
isIOS: false,
22+
isAndroid: false
23+
});
1524

1625
const handleSubmit = (e: React.FormEvent) => {
1726
e.preventDefault();
@@ -41,33 +50,66 @@ This message was sent from your portfolio contact form at https://davidagustin.g
4150
// Create mailto link
4251
const mailtoLink = `mailto:davidsyagustin@gmail.com?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`;
4352

44-
// Try multiple methods to open email client (works across all browsers)
53+
// Browser and mobile-specific email opening logic
4554
let emailOpened = false;
4655

47-
try {
48-
// Method 1: Direct window.open
49-
const emailWindow = window.open(mailtoLink, '_blank');
50-
51-
// Method 2: If window.open fails, try location.href
52-
if (!emailWindow || emailWindow.closed || typeof emailWindow.closed === 'undefined') {
56+
if (browserInfo.isMobile) {
57+
// Mobile devices - use location.href (most reliable on mobile)
58+
try {
59+
window.location.href = mailtoLink;
60+
emailOpened = true;
61+
} catch (error) {
62+
console.error('Mobile email opening failed:', error);
63+
}
64+
} else if (browserInfo.isChrome) {
65+
// Chrome desktop - try multiple methods
66+
try {
67+
// Method 1: Try window.open first
68+
const emailWindow = window.open(mailtoLink, '_blank');
69+
70+
// Method 2: If window.open fails, try location.href
71+
if (!emailWindow || emailWindow.closed || typeof emailWindow.closed === 'undefined') {
72+
window.location.href = mailtoLink;
73+
emailOpened = true;
74+
} else {
75+
emailOpened = true;
76+
}
77+
} catch (windowError) {
78+
// Method 3: Fallback to location.href
5379
window.location.href = mailtoLink;
5480
emailOpened = true;
55-
} else {
81+
}
82+
} else if (browserInfo.isFirefox || browserInfo.isSafari || browserInfo.isEdge) {
83+
// Firefox, Safari, Edge - use window.open (works well)
84+
try {
85+
window.open(mailtoLink, '_blank');
86+
emailOpened = true;
87+
} catch (error) {
88+
// Fallback to location.href
89+
window.location.href = mailtoLink;
90+
emailOpened = true;
91+
}
92+
} else {
93+
// Unknown browser - try both methods
94+
try {
95+
const emailWindow = window.open(mailtoLink, '_blank');
96+
if (!emailWindow || emailWindow.closed || typeof emailWindow.closed === 'undefined') {
97+
window.location.href = mailtoLink;
98+
}
99+
emailOpened = true;
100+
} catch (error) {
101+
window.location.href = mailtoLink;
56102
emailOpened = true;
57103
}
58-
} catch (windowError) {
59-
// Method 3: Fallback to location.href
60-
window.location.href = mailtoLink;
61-
emailOpened = true;
62104
}
63105

64-
// If email client didn't open, show fallback
106+
// Show fallback after a delay if email client didn't open
65107
setTimeout(() => {
66108
if (!emailOpened) {
67109
setEmailContent(body);
68110
setShowEmailFallback(true);
69111
}
70-
}, 1000);
112+
}, browserInfo.isMobile ? 500 : 1000); // Shorter delay on mobile
71113

72114
// Show success message
73115
setSubmitStatus('success');
@@ -97,6 +139,24 @@ This message was sent from your portfolio contact form at https://davidagustin.g
97139
}
98140
};
99141

142+
// Browser detection on component mount
143+
useEffect(() => {
144+
const userAgent = navigator.userAgent;
145+
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent);
146+
const isIOS = /iPad|iPhone|iPod/.test(userAgent);
147+
const isAndroid = /Android/.test(userAgent);
148+
149+
setBrowserInfo({
150+
isChrome: /Chrome/.test(userAgent) && !/Edge/.test(userAgent),
151+
isFirefox: /Firefox/.test(userAgent),
152+
isSafari: /Safari/.test(userAgent) && !/Chrome/.test(userAgent),
153+
isEdge: /Edge/.test(userAgent),
154+
isMobile,
155+
isIOS,
156+
isAndroid
157+
});
158+
}, []);
159+
100160
const handleChange = (
101161
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
102162
) => {
@@ -218,7 +278,18 @@ This message was sent from your portfolio contact form at https://davidagustin.g
218278
{/* Status Messages */}
219279
{submitStatus === 'success' && (
220280
<div className="p-4 bg-green-100 border border-green-400 text-green-700 rounded-lg">
221-
✅ Perfect! Your email client should now open with a professionally formatted message ready to send. The email includes your contact information and message in a clean, business-friendly format.
281+
{browserInfo.isMobile ? (
282+
<>
283+
✅ Perfect! Your mobile email app should now open with a professionally formatted message ready to send.
284+
{browserInfo.isIOS && ' If it doesn\'t open automatically, check your default email app settings.'}
285+
{browserInfo.isAndroid && ' If it doesn\'t open automatically, you may need to select your preferred email app.'}
286+
</>
287+
) : (
288+
<>
289+
✅ Perfect! Your email client should now open with a professionally formatted message ready to send.
290+
{browserInfo.isChrome && ' If it doesn\'t open automatically, check your browser\'s popup settings or use the fallback option below.'}
291+
</>
292+
)}
222293
</div>
223294
)}
224295

@@ -231,9 +302,11 @@ This message was sent from your portfolio contact form at https://davidagustin.g
231302
{/* Email Fallback Modal */}
232303
{showEmailFallback && (
233304
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
234-
<div className="bg-white rounded-lg p-6 max-w-2xl w-full max-h-[80vh] overflow-y-auto">
305+
<div className={`bg-white rounded-lg p-6 ${browserInfo.isMobile ? 'w-full max-h-[90vh]' : 'max-w-2xl w-full max-h-[80vh]'} overflow-y-auto`}>
235306
<div className="flex justify-between items-center mb-4">
236-
<h3 className="text-xl font-bold text-gray-800">Email Client Not Found</h3>
307+
<h3 className="text-xl font-bold text-gray-800">
308+
{browserInfo.isMobile ? 'Email App Not Found' : 'Email Client Not Found'}
309+
</h3>
237310
<button
238311
onClick={() => setShowEmailFallback(false)}
239312
className="text-gray-500 hover:text-gray-700 text-2xl"
@@ -242,7 +315,10 @@ This message was sent from your portfolio contact form at https://davidagustin.g
242315
</button>
243316
</div>
244317
<p className="text-gray-600 mb-4">
245-
Your email client didn't open automatically. Here's your formatted message - you can copy and paste it into your email client:
318+
{browserInfo.isMobile
319+
? 'Your email app didn\'t open automatically. Here\'s your formatted message - you can copy and paste it into your preferred email app:'
320+
: 'Your email client didn\'t open automatically. Here\'s your formatted message - you can copy and paste it into your email client:'
321+
}
246322
</p>
247323
<div className="bg-gray-50 p-4 rounded-lg border">
248324
<div className="mb-2">
@@ -258,21 +334,21 @@ This message was sent from your portfolio contact form at https://davidagustin.g
258334
{emailContent}
259335
</pre>
260336
</div>
261-
<div className="mt-4 flex gap-2">
337+
<div className={`mt-4 flex gap-2 ${browserInfo.isMobile ? 'flex-col' : 'flex-row'}`}>
262338
<button
263339
onClick={() => {
264340
navigator.clipboard.writeText(emailContent);
265341
alert('Email content copied to clipboard!');
266342
}}
267-
className="btn btn-primary"
343+
className={`btn btn-primary ${browserInfo.isMobile ? 'w-full' : ''}`}
268344
>
269345
Copy to Clipboard
270346
</button>
271347
<a
272348
href={`mailto:davidsyagustin@gmail.com?subject=${encodeURIComponent(`Portfolio Contact from ${formData.name}`)}&body=${encodeURIComponent(emailContent)}`}
273-
className="btn btn-secondary"
349+
className={`btn btn-secondary ${browserInfo.isMobile ? 'w-full text-center' : ''}`}
274350
>
275-
Try Mailto Link Again
351+
{browserInfo.isMobile ? 'Try Email App Again' : 'Try Mailto Link Again'}
276352
</a>
277353
</div>
278354
</div>

0 commit comments

Comments
 (0)