Skip to content

Commit aeb06f1

Browse files
authored
Merge pull request #35 from necyberteam/upload-issue
Defensive programming around uploading issue
2 parents d00b0e1 + 96e587d commit aeb06f1

File tree

6 files changed

+103
-56
lines changed

6 files changed

+103
-56
lines changed

build/static/js/main.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build/static/js/main.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@snf/access-qa-bot",
3-
"version": "2.4.6",
3+
"version": "2.4.7",
44
"private": false,
55
"homepage": ".",
66
"description": "ACCESS Q&A Bot React Component with support ticket creation, feedback collection, and ProForma integration",

src/components/FileUploadComponent.js

Lines changed: 83 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const FileUploadComponent = ({ onFileUpload }) => {
1414
const [selectedFiles, setSelectedFiles] = useState([]);
1515
const [previewFile, setPreviewFile] = useState(null);
1616
const fileInputRef = useRef(null);
17-
const { captureScreenshot, isCapturing } = useScreenshotCapture();
17+
const { captureScreenshot, isCapturing, isScreenCaptureAvailable } = useScreenshotCapture();
1818

1919
// Helper function to announce messages to screen readers
2020
const announceToScreenReader = (message) => {
@@ -28,7 +28,21 @@ const FileUploadComponent = ({ onFileUpload }) => {
2828
};
2929

3030
const handleFiles = (files) => {
31-
const newFileArray = Array.from(files);
31+
// Defensive check: ensure files is valid and iterable
32+
if (!files || !files.length || files.length === 0) {
33+
console.warn('FileUploadComponent: No valid files provided to handleFiles');
34+
return;
35+
}
36+
37+
let newFileArray;
38+
try {
39+
newFileArray = Array.from(files);
40+
} catch (error) {
41+
console.error('FileUploadComponent: Error converting files to array:', error);
42+
announceToScreenReader('Error processing selected files. Please try again.');
43+
return;
44+
}
45+
3246
const updatedFiles = [...selectedFiles, ...newFileArray];
3347
setSelectedFiles(updatedFiles);
3448

@@ -66,19 +80,36 @@ const FileUploadComponent = ({ onFileUpload }) => {
6680
e.stopPropagation();
6781
setDragActive(false);
6882

69-
if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
70-
handleFiles(e.dataTransfer.files);
83+
try {
84+
if (e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files.length > 0) {
85+
handleFiles(e.dataTransfer.files);
86+
}
87+
} catch (error) {
88+
console.error('FileUploadComponent: Error in handleDrop:', error);
89+
announceToScreenReader('Error processing dropped files. Please try again.');
7190
}
7291
};
7392

7493
const handleFileSelect = (e) => {
75-
if (e.target.files && e.target.files.length > 0) {
76-
handleFiles(e.target.files);
94+
try {
95+
if (e.target && e.target.files && e.target.files.length > 0) {
96+
handleFiles(e.target.files);
97+
}
98+
} catch (error) {
99+
console.error('FileUploadComponent: Error in handleFileSelect:', error);
100+
announceToScreenReader('Error processing selected files. Please try again.');
77101
}
78102
};
79103

80104
const handleButtonClick = () => {
81-
fileInputRef.current.click();
105+
try {
106+
if (fileInputRef.current) {
107+
fileInputRef.current.click();
108+
}
109+
} catch (error) {
110+
console.error('FileUploadComponent: Error in handleButtonClick:', error);
111+
announceToScreenReader('Error opening file selection dialog. Please try again.');
112+
}
82113
};
83114

84115
const handleRemoveFile = (indexToRemove) => {
@@ -202,50 +233,52 @@ const FileUploadComponent = ({ onFileUpload }) => {
202233

203234
return (
204235
<div className="file-upload-container" style={{ padding: '16px', margin: '8px 0' }}>
205-
<div style={{ display: 'flex', justifyContent: 'center', marginBottom: '10px' }}>
206-
<button
207-
onClick={(e) => {
208-
e.preventDefault();
209-
handleScreenshotCapture();
210-
}}
211-
disabled={isCapturing}
212-
aria-describedby="screenshot-help"
213-
aria-label={isCapturing ? 'Taking screenshot, please wait' : 'Take a screenshot to attach'}
214-
style={{
215-
display: 'flex',
216-
alignItems: 'center',
217-
gap: '8px',
218-
backgroundColor: '#107180',
219-
color: 'white',
220-
border: 'none',
221-
borderRadius: '5px',
222-
padding: '8px 12px',
223-
cursor: isCapturing ? 'not-allowed' : 'pointer',
224-
fontSize: '14px',
225-
fontWeight: '500',
226-
boxShadow: '0 1px 3px rgba(0,0,0,0.2)',
227-
}}
228-
>
229-
<svg
230-
xmlns="http://www.w3.org/2000/svg"
231-
width="16"
232-
height="16"
233-
viewBox="0 0 24 24"
234-
fill="none"
235-
stroke="currentColor"
236-
strokeWidth="2"
237-
strokeLinecap="round"
238-
strokeLinejoin="round"
236+
{isScreenCaptureAvailable && (
237+
<div style={{ display: 'flex', justifyContent: 'center', marginBottom: '10px' }}>
238+
<button
239+
onClick={(e) => {
240+
e.preventDefault();
241+
handleScreenshotCapture();
242+
}}
243+
disabled={isCapturing}
244+
aria-describedby="screenshot-help"
245+
aria-label={isCapturing ? 'Taking screenshot, please wait' : 'Take a screenshot to attach'}
246+
style={{
247+
display: 'flex',
248+
alignItems: 'center',
249+
gap: '8px',
250+
backgroundColor: '#107180',
251+
color: 'white',
252+
border: 'none',
253+
borderRadius: '5px',
254+
padding: '8px 12px',
255+
cursor: isCapturing ? 'not-allowed' : 'pointer',
256+
fontSize: '14px',
257+
fontWeight: '500',
258+
boxShadow: '0 1px 3px rgba(0,0,0,0.2)',
259+
}}
239260
>
240-
<path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"></path>
241-
<circle cx="12" cy="13" r="4"></circle>
242-
</svg>
243-
{isCapturing ? 'Taking screenshot...' : 'Take screenshot...'}
244-
</button>
245-
<span id="screenshot-help" className="sr-only">
246-
Captures the current screen and adds it as an attachment
247-
</span>
248-
</div>
261+
<svg
262+
xmlns="http://www.w3.org/2000/svg"
263+
width="16"
264+
height="16"
265+
viewBox="0 0 24 24"
266+
fill="none"
267+
stroke="currentColor"
268+
strokeWidth="2"
269+
strokeLinecap="round"
270+
strokeLinejoin="round"
271+
>
272+
<path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"></path>
273+
<circle cx="12" cy="13" r="4"></circle>
274+
</svg>
275+
{isCapturing ? 'Taking screenshot...' : 'Take screenshot...'}
276+
</button>
277+
<span id="screenshot-help" className="sr-only">
278+
Captures the current screen and adds it as an attachment
279+
</span>
280+
</div>
281+
)}
249282

250283
<div
251284
className={`file-upload-dropzone ${dragActive ? "active" : ""}`}

src/hooks/useScreenshotCapture.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,20 @@ import { useState } from 'react';
77
const useScreenshotCapture = () => {
88
const [isCapturing, setIsCapturing] = useState(false);
99

10+
// Check if screen capture is available
11+
const isScreenCaptureAvailable = () => {
12+
return (
13+
typeof navigator !== 'undefined' &&
14+
navigator.mediaDevices &&
15+
typeof navigator.mediaDevices.getDisplayMedia === 'function'
16+
);
17+
};
18+
1019
const captureScreenshot = async () => {
20+
if (!isScreenCaptureAvailable()) {
21+
throw new Error('Screen capture is not available in this environment');
22+
}
23+
1124
try {
1225
setIsCapturing(true);
1326

@@ -65,7 +78,8 @@ const useScreenshotCapture = () => {
6578

6679
return {
6780
captureScreenshot,
68-
isCapturing
81+
isCapturing,
82+
isScreenCaptureAvailable: isScreenCaptureAvailable()
6983
};
7084
};
7185

0 commit comments

Comments
 (0)