Skip to content
2 changes: 1 addition & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ Thanks for contributing! 🦋🙌
### Editing pull requests

- [](# "sync-pr-commit-title") 🔥 [Uses the PR’s title as the default squash commit title](https://github.com/refined-github/refined-github/issues/276) and [updates the PR’s title to match the commit title, if changed.](https://user-images.githubusercontent.com/1402241/51669708-9a712400-1ff7-11e9-913a-ac1ea1050975.png)
- [](# "update-pr-from-base-branch") [Adds a button to update a PR from the base branch to ensure it builds correctly before merging the PR itself.](https://user-images.githubusercontent.com/1402241/106494023-816d9a00-647f-11eb-8cb1-7c97aa8a5546.png) GitHub only adds it when the base branch is "protected".
- [](# "update-pr-from-base-branch") [Adds an "Update branch" button to every PR.](https://user-images.githubusercontent.com/1402241/234483592-4867cb2e-21cb-436d-9ea0-aedadf834f19.png) GitHub has the same feature, but it must be manually configured with protected branches.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tooltip is actually slightly pushed to the left, not aligned right. I only did it manually here to make it look better.

It can't be centered because it's cut off on sidebar-less views. I tried tooltipped-e but it looked bad too

Screenshot 12

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tooltip was added to clarify that this is a RGH feature, because it blends in way too well otherwise.

- [](# "one-click-review-submission") [Simplifies the PR review form: Approve or reject reviews faster with one-click review-type buttons.](https://user-images.githubusercontent.com/1402241/34326942-529cb7c0-e8f3-11e7-9bee-98b667e18a90.png)
- [](# "pull-request-hotkeys") [Adds keyboard shortcuts to cycle through PR tabs: <kbd>g</kbd> <kbd>←</kbd> and <kbd>g</kbd> <kbd>→</kbd>, or <kbd>g</kbd> <kbd>1</kbd>, <kbd>g</kbd> <kbd>2</kbd>, <kbd>g</kbd> <kbd>3</kbd> and <kbd>g</kbd> <kbd>4</kbd>](https://user-images.githubusercontent.com/16872793/94634958-7e7b5680-029f-11eb-82ea-1f96cd11e4cd.png).
- [](# "pr-branch-auto-delete") [Automatically deletes the branch right after merging a PR, if possible.](https://user-images.githubusercontent.com/1402241/177067141-eabc7494-38a2-45b5-aef9-ac33cc0da370.png)
Expand Down
82 changes: 58 additions & 24 deletions source/features/update-pr-from-base-branch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import React from 'dom-chef';
import select from 'select-dom';

import * as pageDetect from 'github-url-detection';
import delegate from 'delegate-it';
import delegate, {DelegateEvent} from 'delegate-it';

import {CheckIcon} from '@primer/octicons-react';

import features from '../feature-manager';
import observe from '../helpers/selector-observer';
Expand All @@ -11,8 +13,11 @@ import {getBranches} from '../github-helpers/pr-branches';
import getPrInfo from '../github-helpers/get-pr-info';
import {getConversationNumber} from '../github-helpers';
import showToast from '../github-helpers/toast';
import createMergeabilityRow from '../github-widgets/mergeability-row';
import selectHas from '../helpers/select-has';

const selectorForPushablePRNotice = '.merge-pr > .color-fg-muted:first-child';
const canMerge = '.merge-pr > .color-fg-muted:first-child';
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not seeing it in our own repo though 🤷‍♂️

const canNativelyUpdate = '.js-update-branch-form';

async function mergeBranches(): Promise<AnyObject> {
return api.v3(`pulls/${getConversationNumber()!}/update-branch`, {
Expand All @@ -21,14 +26,8 @@ async function mergeBranches(): Promise<AnyObject> {
});
}

async function handler(): Promise<void> {
const {base, head} = getBranches();
if (!confirm(`Merge the ${base.relative} branch into ${head.relative}?`)) {
return;
}

features.unload(import.meta.url);

async function handler({delegateTarget: button}: DelegateEvent<MouseEvent, HTMLButtonElement>): Promise<void> {
button.disabled = true;
await showToast(async () => {
const response = await mergeBranches().catch(error => error);
if (response instanceof Error || !response.ok) {
Expand All @@ -40,29 +39,62 @@ async function handler(): Promise<void> {
message: 'Updating branch…',
doneMessage: 'Branch updated',
});

button.remove();
}

function createButton(): JSX.Element {
return (
<button
type="button"
className="btn btn-sm rgh-update-pr-from-base-branch tooltipped tooltipped-sw"
aria-label="Use Refined GitHub to update the PR from the base branch"
>
Update branch
</button>
);
}

async function addButton(position: Element): Promise<void> {
async function addButton(mergeBar: Element): Promise<void> {
if (!select.exists(canMerge) || select.exists(canNativelyUpdate)) {
return;
}

const {base, head} = getBranches();
const prInfo = await getPrInfo(base.relative, head.relative);
if (!prInfo) {
if (!prInfo?.viewerCanEditFiles || prInfo.mergeable === 'CONFLICTING') {
return;
}

if (prInfo.viewerCanEditFiles && prInfo.mergeable !== 'CONFLICTING') {
position.append(' ', (
<span className="status-meta d-inline-block rgh-update-pr-from-base-branch">
You can <button type="button" className="btn-link">update the base branch</button>.
</span>
));
const mergeabilityRow = selectHas('.branch-action-item:has(.merging-body)')!;
if (mergeabilityRow) {
// The PR is not a draft
mergeabilityRow.prepend(

<div
className="branch-action-btn float-right js-immediate-updates js-needs-timeline-marker-header"
Copy link
Member Author

@fregante fregante Apr 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if these last 2 classes are useful, I just copied the native row and dropped every class and element I understood.

>
{createButton()}
</div>,
);
return;
}

// The PR is still a draft
mergeBar.before(createMergeabilityRow({
action: createButton(),
icon: <CheckIcon/>,
iconClass: 'completeness-indicator-success',
heading: 'This branch has no conflicts with the base branch',
meta: 'Merging can be performed automatically.',
Copy link
Member Author

@fregante fregante Apr 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added this meta line because without it it's a little misaligned, but I'm not totally sure it's a always true statement exactly as GitHub defines it.

Eventually this line will likely contain:

}));
}

async function init(signal: AbortSignal): Promise<false | void> {
await api.expectToken();

delegate('.rgh-update-pr-from-base-branch', 'click', handler, {signal});
observe(selectorForPushablePRNotice, addButton, {signal});
observe('.merge-message', addButton, {signal});
}

void features.add(import.meta.url, {
Expand All @@ -72,10 +104,6 @@ void features.add(import.meta.url, {
exclude: [
pageDetect.isClosedPR,
() => select('.head-ref')!.title === 'This repository has been deleted',

// Native button https://github.blog/changelog/2022-02-03-more-ways-to-keep-your-pull-request-branch-up-to-date/
// TODO: COPY to :has, so it can be hidden dynamically
() => select.exists('.js-update-branch-form'),
],
awaitDomReady: true, // DOM-based exclusions
init,
Expand All @@ -85,7 +113,13 @@ void features.add(import.meta.url, {
Test URLs

PR without conflicts
https://github.com/refined-github/sandbox/pull/11
https://github.com/refined-github/sandbox/pull/60

Draft PR without conflicts
https://github.com/refined-github/sandbox/pull/61

Native "Update branch" button
(pick a conflict-free PR from https://github.com/refined-github/refined-github/pulls?q=is%3Apr+is%3Aopen+sort%3Acreated-asc)

Native "Resolve conflicts" button
https://github.com/refined-github/sandbox/pull/9
Expand Down
38 changes: 38 additions & 0 deletions source/github-widgets/mergeability-row.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from 'dom-chef';

type MergeabilityRowProps = {
action?: JSX.Element;
icon: JSX.Element;
iconClass?: string;
heading: string;
meta?: string;
};

export default function createMergeabilityRow({
action,
icon,
iconClass = '',
heading,
meta,
}: MergeabilityRowProps): JSX.Element {
return (
<div className="branch-action-item">
<div
className="branch-action-btn float-right js-immediate-updates js-needs-timeline-marker-header"
>
{action}
</div>
<div
className={`branch-action-item-icon completeness-indicator ${iconClass}`}
>
{icon}
</div>
<h3 className="h4 status-heading">
{heading}
</h3>
<span className="status-meta">
{meta}
</span>
</div>
);
}