-
Notifications
You must be signed in to change notification settings - Fork 17
Add conflict resolution shortcut icon to Mergeable column #266
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -1127,6 +1127,21 @@ <h3 class="text-lg font-semibold text-slate-900 dark:text-slate-100 mb-2">Error< | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, FEEDBACK_DISMISS_DELAY); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function showSuccessToast(message) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Show a temporary global success message (toast) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const toast = document.createElement('div'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| toast.className = 'fixed top-4 right-4 z-[100] flex items-start gap-2 rounded-lg border border-green-200 bg-green-50 px-4 py-3 text-sm text-green-900 shadow-lg dark:border-green-900 dark:bg-green-950/30 dark:text-green-400 max-w-md animate-slide-in'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| toast.innerHTML = ` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <span class="inline-flex h-5 w-5 flex-shrink-0 items-center justify-center rounded-full bg-green-100 text-xs font-bold text-green-700 dark:bg-green-900 dark:text-green-400">✓</span> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div class="leading-5">${escapeHtml(message)}</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| document.body.appendChild(toast); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setTimeout(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| toast.classList.add('opacity-0', 'transition-opacity', 'duration-500'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setTimeout(() => toast.remove(), 500); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, FEEDBACK_DISMISS_DELAY); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function showSectionMessage(message, type) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Show an inline feedback message in the Add PR section on the page | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const section = document.getElementById('prUrlInput')?.closest('section'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -2512,7 +2527,12 @@ <h3 class="text-xl font-semibold text-slate-900 dark:text-slate-100">${emptyTitl | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </td> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <td class="px-2 py-3">${reviewBadge}</td> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <td class="px-2 py-3">${renderReviewerAvatars(pr.reviewers_json)}</td> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <td class="px-2 py-3 text-slate-700 dark:text-slate-300 text-xs truncate">${escapeHtml(mergeableText)}</td> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <td class="px-2 py-3 text-slate-700 dark:text-slate-300 text-xs"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <span class="inline-flex items-center gap-1"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ${escapeHtml(mergeableText)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ${mergeableText === 'Conflicts' ? `<button class="conflict-message-btn inline-flex items-center justify-center rounded text-orange-500 hover:text-orange-700 dark:text-orange-400 dark:hover:text-orange-300 focus:outline-none focus:ring-1 focus:ring-orange-400" data-pr-url="${escapeHtml(pr.pr_url)}" data-author-login="${escapeHtml(pr.author_login)}" title="Open PR and copy a conflict resolution message to clipboard" aria-label="Copy conflict message and open PR"><i class="fas fa-comment-dots"></i></button>` : ''} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </span> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </td> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <td class="px-2 py-3 text-center text-slate-700 dark:text-slate-300 text-xs">${pr.files_changed}</td> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <td class="px-2 py-3 text-center text-slate-700 dark:text-slate-300 text-xs">${pr.commits_count || 0}</td> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <td class="px-2 py-3 text-center text-slate-700 dark:text-slate-300 text-xs"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -2589,6 +2609,26 @@ <h3 class="text-xl font-semibold text-slate-900 dark:text-slate-100">${emptyTitl | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </td> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Add click handler for the conflict message button | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const conflictMessageBtn = row.querySelector('.conflict-message-btn'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (conflictMessageBtn) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| conflictMessageBtn.addEventListener('click', async (e) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| e.stopPropagation(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const prUrl = conflictMessageBtn.dataset.prUrl; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const authorLogin = conflictMessageBtn.dataset.authorLogin; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const authorMention = authorLogin && /^[a-zA-Z0-9_.-]+$/.test(authorLogin) ? `@${authorLogin}` : 'contributor'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const message = `Hi ${authorMention}, this PR has merge conflicts that need to be resolved before it can be merged. Could you please fix the conflicts? Thank you!`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const prUrlWithComment = `${prUrl}?expand=1&body=${encodeURIComponent(message)}`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await navigator.clipboard.writeText(message); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| showSuccessToast('Opening PR with conflict message pre-filled...'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (err) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error('Failed to copy conflict message:', err); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| window.open(prUrlWithComment, '_blank', 'noopener,noreferrer'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+2615
to
+2628
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| conflictMessageBtn.addEventListener('click', async (e) => { | |
| e.stopPropagation(); | |
| const prUrl = conflictMessageBtn.dataset.prUrl; | |
| const authorLogin = conflictMessageBtn.dataset.authorLogin; | |
| const authorMention = authorLogin && /^[a-zA-Z0-9_.-]+$/.test(authorLogin) ? `@${authorLogin}` : 'contributor'; | |
| const message = `Hi ${authorMention}, this PR has merge conflicts that need to be resolved before it can be merged. Could you please fix the conflicts? Thank you!`; | |
| const prUrlWithComment = `${prUrl}?expand=1&body=${encodeURIComponent(message)}`; | |
| try { | |
| await navigator.clipboard.writeText(message); | |
| showSuccessToast('Opening PR with conflict message pre-filled...'); | |
| } catch (err) { | |
| console.error('Failed to copy conflict message:', err); | |
| } | |
| window.open(prUrlWithComment, '_blank', 'noopener,noreferrer'); | |
| conflictMessageBtn.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| const prUrl = conflictMessageBtn.dataset.prUrl; | |
| const authorLogin = conflictMessageBtn.dataset.authorLogin; | |
| const authorMention = authorLogin && /^[a-zA-Z0-9_.-]+$/.test(authorLogin) ? `@${authorLogin}` : 'contributor'; | |
| const message = `Hi ${authorMention}, this PR has merge conflicts that need to be resolved before it can be merged. Could you please fix the conflicts? Thank you!`; | |
| const prUrlWithComment = `${prUrl}?expand=1&body=${encodeURIComponent(message)}`; | |
| // Open the PR in a new tab synchronously in response to the click | |
| window.open(prUrlWithComment, '_blank', 'noopener,noreferrer'); | |
| // Copy the message to the clipboard without delaying the window.open call | |
| navigator.clipboard.writeText(message) | |
| .then(() => { | |
| showSuccessToast('Opening PR with conflict message pre-filled...'); | |
| }) | |
| .catch((err) => { | |
| console.error('Failed to copy conflict message:', err); | |
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The success toast is only shown when
navigator.clipboard.writeTextsucceeds. If clipboard access is denied/unavailable, the PR still opens but no toast is shown, which doesn’t match the described behavior of always confirming the action. Consider showing a success toast for the tab-open action regardless, and separately showing a warning/error toast if clipboard copy fails.