@@ -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