summaryrefslogtreecommitdiff
path: root/source/features/comments-time-machine-links.tsx
blob: d0fc99bd156aabe2f5386080d7c6dfe668bb6fb0 (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
import select from 'select-dom';
import React from 'dom-chef';
import features from '../libs/features';
import * as icons from '../libs/icons';
import {getRepoURL} from '../libs/utils';
import {appendBefore} from '../libs/dom-utils';

function addInlineLinks(comment: HTMLElement, timestamp: string): void {
	const links = select.all<HTMLAnchorElement>(`
		[href^="${location.origin}"][href*="/blob/"]:not(.rgh-linkified-code),
		[href^="${location.origin}"][href*="/tree/"]:not(.rgh-linkified-code)
	`, comment);

	for (const link of links) {
		const linkParts = link.pathname.split('/');
		// Skip permalinks
		if (/^[0-9a-f]{40}$/.test(linkParts[4])) {
			continue;
		}

		linkParts[4] = `HEAD@{${timestamp}}`; // Change git ref
		link.after(
			' ',
			<a
				href={linkParts.join('/') + link.hash}
				className="muted-link tooltipped tooltipped-n"
				aria-label="Visit as permalink">
				{icons.clock()}
			</a>
		);
	}
}

function addDropdownLink(comment: HTMLElement, timestamp: string): void {
	const dropdown = select('.show-more-popover', comment);

	// Comment-less reviews don't have a dropdown
	if (!dropdown) {
		return;
	}

	appendBefore(dropdown, '.dropdown-divider',
		<>
			<div className="dropdown-divider" />
			<a
				href={`/${getRepoURL()}/tree/HEAD@{${timestamp}}`}
				className="dropdown-item btn-link"
				role="menuitem"
				title="Browse repository like it appeared on this day">
				View repo at this time
			</a>
		</>
	);
}

function init(): void {
	// PR reviews' main content has nested `.timeline-comment`, but the deepest one doesn't have `relative-time`. These are filtered out with `:not([id^="pullrequestreview"])`
	const comments = select.all(`
		:not(.js-new-comment-form):not([id^="pullrequestreview"]) > .timeline-comment:not(.rgh-time-machine-links),
		.review-comment:not(.rgh-time-machine-links)
	`);

	for (const comment of comments) {
		const timestamp = select('relative-time', comment)!.attributes.datetime.value;

		addDropdownLink(comment, timestamp);
		addInlineLinks(comment, timestamp);
		comment.classList.add('rgh-time-machine-links');
	}
}

features.add({
	id: __featureName__,
	description: 'Adds links to browse the repository and linked files at the time of each comment.',
	screenshot: 'https://user-images.githubusercontent.com/1402241/56450896-68076680-635b-11e9-8b24-ebd11cc4e655.png',
	include: [
		features.hasComments
	],
	load: features.onNewComments,
	init
});