summaryrefslogtreecommitdiff
path: root/source/features/sync-pr-commit-title.tsx
blob: 37ad9a273a59b867c712bcf21886cc05f469c0ec (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import React from 'dom-chef';
import select from 'select-dom';
import delegate from 'delegate-it';
import regexJoin from 'regex-join';
import * as pageDetect from 'github-url-detection';
import * as textFieldEdit from 'text-field-edit';

import features from '../feature-manager';
import onPrMergePanelOpen from '../github-events/on-pr-merge-panel-open';
import {getConversationNumber, userCanLikelyMergePR} from '../github-helpers';
import onCommitTitleUpdate from '../github-events/on-commit-title-update';

const mergeFormSelector = '.is-squashing form:not([hidden])';
const prTitleFieldSelector = '.js-issue-update input[name="issue[title]"]';
const prTitleSubmitSelector = '.js-issue-update button[type="submit"]';

function getCommitTitleField(): HTMLInputElement | undefined {
	return select(`${mergeFormSelector} input#merge_title_field`);
}

function createCommitTitle(): string {
	const prTitle = select(prTitleFieldSelector)!.value.trim();
	return `${prTitle} (#${getConversationNumber()!})`;
}

function needsSubmission(): boolean {
	const inputField = getCommitTitleField();
	if (!inputField || inputField.value === '') {
		return false;
	}

	// Ensure that the required fields are on the page
	if (!select.exists(prTitleFieldSelector + ',' + prTitleSubmitSelector)) {
		features.log.error(import.meta.url, 'Can’t update the PR title');
		return false;
	}

	return createCommitTitle() !== inputField.value;
}

function getUI(): HTMLElement {
	return select(`${mergeFormSelector} .rgh-sync-pr-commit-title-note`) ?? (
		<p className="note rgh-sync-pr-commit-title-note">
			The title of this PR will be updated to match this title. <button type="button" className="btn-link Link--muted text-underline rgh-sync-pr-commit-title">Cancel</button>
		</p>
	);
}

function updateUI(): void {
	if (needsSubmission()) {
		getCommitTitleField()!.after(getUI());
	} else {
		getUI().remove();
	}
}

function updatePRTitle(): void {
	if (!needsSubmission()) {
		return;
	}

	// Remove PR number from commit title
	const prTitle = getCommitTitleField()!.value
		.replace(regexJoin(/\s*\(/, '#' + getConversationNumber()!, /\)$/), '');

	// Fill and submit title-change form
	select(prTitleFieldSelector)!.value = prTitle;
	select(prTitleSubmitSelector)!.click(); // `form.submit()` isn't sent via ajax
}

async function updateCommitTitle(): Promise<void> {
	const field = getCommitTitleField()!;
	if (field) {
		textFieldEdit.set(field, createCommitTitle());
	}
}

function disableSubmission(): void {
	features.unload(import.meta.url);
	getUI().remove();
}

function init(signal: AbortSignal): void {
	onPrMergePanelOpen(updateCommitTitle, signal);
	onCommitTitleUpdate(updateUI, signal);

	delegate('form.js-merge-pull-request', 'submit', updatePRTitle, {signal});
	delegate('.rgh-sync-pr-commit-title', 'click', disableSubmission, {signal});
}

void features.add(import.meta.url, {
	asLongAs: [
		userCanLikelyMergePR,
	],
	include: [
		pageDetect.isPRConversation,
	],
	awaitDomReady: true, // DOM-based filters, feature appears at the end of the page
	init,
});