feat: add LinkedIn sharing with pre-filled caption copy on card export (closes #521)#526
Conversation
|
Someone is attempting to deploy a commit to the 007's projects Team on Vercel. A member of the Team first needs to authorize it. |
Thanks for creating a PR for your Issue!
|
| await navigator.clipboard.writeText(captionText); | ||
| copied = true; | ||
| } catch (clipErr) { | ||
| console.warn('Clipboard write failed:', clipErr); | ||
| } | ||
| } | ||
|
|
||
| // Provide UI feedback | ||
| if (this.statusDiv) { | ||
| this.statusDiv.style.display = 'flex'; | ||
| if (copied) { | ||
| this.showSuccessMessage('Caption copied! Opening LinkedIn...'); | ||
| } else { | ||
| // Graceful fallback display | ||
| this.showSuccessMessage('Opening LinkedIn...'); | ||
| } | ||
|
|
||
| // Reset status spinner/text after a delay | ||
| setTimeout(() => { | ||
| this.statusDiv.style.display = 'none'; | ||
| // Reset styling to original states | ||
| this.statusDiv.style.background = ''; | ||
| this.statusDiv.style.color = ''; | ||
| const spinner = this.statusDiv.querySelector('.status-spinner'); | ||
| if (spinner) { | ||
| spinner.textContent = '⏳'; | ||
| spinner.style.animation = ''; | ||
| } | ||
| }, 3000); | ||
| } | ||
|
|
||
| // Open LinkedIn share dialog in a new tab | ||
| const shareUrl = `https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(verificationUrl)}`; | ||
| window.open(shareUrl, '_blank'); |
There was a problem hiding this comment.
window.open blocked by popup blocker after clipboard await
The await navigator.clipboard.writeText(...) at line 303 suspends the async function and releases the browser's user-gesture context; when window.open runs at line 336, browsers (Chrome, Firefox, Safari) no longer consider it user-initiated and silently block the popup.
| await navigator.clipboard.writeText(captionText); | |
| copied = true; | |
| } catch (clipErr) { | |
| console.warn('Clipboard write failed:', clipErr); | |
| } | |
| } | |
| // Provide UI feedback | |
| if (this.statusDiv) { | |
| this.statusDiv.style.display = 'flex'; | |
| if (copied) { | |
| this.showSuccessMessage('Caption copied! Opening LinkedIn...'); | |
| } else { | |
| // Graceful fallback display | |
| this.showSuccessMessage('Opening LinkedIn...'); | |
| } | |
| // Reset status spinner/text after a delay | |
| setTimeout(() => { | |
| this.statusDiv.style.display = 'none'; | |
| // Reset styling to original states | |
| this.statusDiv.style.background = ''; | |
| this.statusDiv.style.color = ''; | |
| const spinner = this.statusDiv.querySelector('.status-spinner'); | |
| if (spinner) { | |
| spinner.textContent = '⏳'; | |
| spinner.style.animation = ''; | |
| } | |
| }, 3000); | |
| } | |
| // Open LinkedIn share dialog in a new tab | |
| const shareUrl = `https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(verificationUrl)}`; | |
| window.open(shareUrl, '_blank'); | |
| // Copy to clipboard first (sync gesture preserved), then open tab | |
| const shareUrl = `https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(verificationUrl)}`; | |
| const tab = window.open(shareUrl, '_blank', 'noopener,noreferrer'); | |
| // Try to copy to clipboard | |
| let copied = false; | |
| if (navigator.clipboard && navigator.clipboard.writeText) { | |
| try { | |
| await navigator.clipboard.writeText(captionText); | |
| copied = true; | |
| } catch (clipErr) { | |
| console.warn('Clipboard write failed:', clipErr); | |
| } | |
| } |
Prompt to fix with AI
Copy this prompt into your AI coding assistant to fix this issue.
In `static/js/achievement-export.js`, inside the `shareLinkedIn()` method (lines 281-342), the `window.open(shareUrl, '_blank')` call at line 336 is placed after `await navigator.clipboard.writeText(captionText)` at line 303. This breaks popup blocking rules: browsers only permit window.open in a synchronous user-gesture handler, and the await suspends the function, releasing that context. Fix by moving the `window.open` call to BEFORE the first `await` (i.e., before line 303), so it executes synchronously within the click handler. Also add `'noopener,noreferrer'` as the third argument to window.open.
Confidence Score: 3/5 - Review RecommendedNot safe to merge without fixes — the LinkedIn sharing feature in Key Findings:
Files requiring special attention
|
Implement dynamic achievement card export combined with a direct LinkedIn sharing option.
I am thrilled to share my achievement! 🎖️ I secured {position} in "{event_name}" organized by "{organizer}". Verify it here: {verification_url}Closes #521