Skip to content

Commit e40af0f

Browse files
authored
fix(export): add clipboard fallback support for unsupported browsers (JhaSourav07#977)
## Description Fixes JhaSourav07#971 Adds graceful clipboard fallback support for environments where `navigator.clipboard` is unavailable. ### Changes - Added fallback clipboard copy support using `document.execCommand('copy')` - Improved browser compatibility for export snippet copying - Prevented copy failures in unsupported or insecure environments ## Pillar - [ ] 🎨 Pillar 1 — New Theme Design - [ ] 📐 Pillar 2 — Geometric SVG Improvement - [ ] 🕐 Pillar 3 — Timezone Logic Optimization - [x] 🛠️ Other (Bug fix, refactoring, docs) ## Visual Preview <!-- Add screenshot/gif here --> ## Checklist before requesting a review: - [x] I have read the `CONTRIBUTING.md` file. - [x] I have tested these changes locally. - [x] I have run `npm run format` and `npm run lint`. - [x] My commits follow the Conventional Commits format. - [x] I have made sure only relevant files were changed.
2 parents 58b8f90 + bb54a78 commit e40af0f

1 file changed

Lines changed: 36 additions & 1 deletion

File tree

app/customize/page.tsx

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,30 @@ export default function CustomizePage(): ReactElement {
153153
const previewSrc = `/api/streak?${queryString}`;
154154
const exportSnippet = getExportSnippet(exportFormat, queryString);
155155

156+
const fallbackCopyToClipboard = (text: string): boolean => {
157+
try {
158+
const textArea = document.createElement('textarea');
159+
160+
textArea.value = text;
161+
textArea.style.position = 'fixed';
162+
textArea.style.opacity = '0';
163+
textArea.style.pointerEvents = 'none';
164+
165+
document.body.appendChild(textArea);
166+
167+
textArea.focus();
168+
textArea.select();
169+
170+
const successful = document.execCommand('copy');
171+
172+
document.body.removeChild(textArea);
173+
174+
return successful;
175+
} catch {
176+
return false;
177+
}
178+
};
179+
156180
const announceCopyStatus = useCallback((message: string): void => {
157181
setCopyStatusMessage('');
158182
window.setTimeout(() => {
@@ -164,8 +188,18 @@ export default function CustomizePage(): ReactElement {
164188
if (!hasUsername) return;
165189

166190
try {
167-
await navigator.clipboard.writeText(exportSnippet);
191+
if (navigator.clipboard && window.isSecureContext) {
192+
await navigator.clipboard.writeText(exportSnippet);
193+
} else {
194+
const copiedSuccessfully = fallbackCopyToClipboard(exportSnippet);
195+
196+
if (!copiedSuccessfully) {
197+
throw new Error('Fallback clipboard copy failed.');
198+
}
199+
}
200+
168201
setCopied(true);
202+
169203
announceCopyStatus(
170204
`${exportFormat === 'markdown' ? 'Markdown' : 'HTML'} snippet copied to clipboard.`
171205
);
@@ -180,6 +214,7 @@ export default function CustomizePage(): ReactElement {
180214
}, 3000);
181215
} catch {
182216
setCopied(false);
217+
183218
announceCopyStatus(
184219
`Unable to copy the ${exportFormat === 'markdown' ? 'Markdown' : 'HTML'} snippet.`
185220
);

0 commit comments

Comments
 (0)