summaryrefslogtreecommitdiff
path: root/source/features/repo-header-info.tsx
blob: 1942f22af8a9e12e54a0e1c0b3dbc6d1950c2b8e (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 * as pageDetect from 'github-url-detection';
import {LockIcon, RepoForkedIcon, StarIcon} from '@primer/octicons-react';
import React from 'dom-chef';
import {elementExists} from 'select-dom';
import {CachedFunction} from 'webext-storage-cache';

import observe from '../helpers/selector-observer.js';
import features from '../feature-manager.js';
import api from '../github-helpers/api.js';
import GetRepositoryInfo from './repo-header-info.gql';
import {buildRepoURL, cacheByRepo} from '../github-helpers/index.js';
import abbreviateNumber from '../helpers/abbreviate-number.js';

type RepositoryInfo = {
	isFork: boolean;
	isPrivate: boolean;
	stargazerCount: number;
};

const repositoryInfo = new CachedFunction('stargazer-count', {
	async updater(): Promise<RepositoryInfo> {
		const {repository} = await api.v4(GetRepositoryInfo);
		return repository;
	},
	maxAge: {days: 1},
	staleWhileRevalidate: {days: 3},
	cacheKey: cacheByRepo,
});

async function add(repoLink: HTMLAnchorElement): Promise<void> {
	const {isFork, isPrivate, stargazerCount} = await repositoryInfo.get();

	// GitHub may already show this icon natively, so we match its position
	if (isPrivate && !elementExists('.octicon-lock', repoLink)) {
		repoLink.append(
			<LockIcon className="ml-1" width={12} height={12}/>,
		);
	}

	// GitHub may already show this icon natively, so we match its position
	if (isFork && !elementExists('.octicon-repo-forked', repoLink)) {
		repoLink.append(
			<RepoForkedIcon className="ml-1" width={12} height={12}/>,
		);
	}

	if (stargazerCount > 1) {
		repoLink.after(
			<a
				href={buildRepoURL('stargazers')}
				title={`Repository starred by ${stargazerCount.toLocaleString('us')} people`}
				className="d-flex flex-items-center flex-justify-center mr-1 gap-1 color-fg-muted"
			>
				<StarIcon className="ml-1" width={12} height={12}/>
				<span className="f5">{abbreviateNumber(stargazerCount)}</span>
			</a>,
		);
	}
}

function init(signal: AbortSignal): void {
	observe('.AppHeader-context-full li:last-child a.AppHeader-context-item', add, {signal});
}

void features.add(import.meta.url, {
	include: [
		pageDetect.hasRepoHeader,
	],
	init,
});

/*
Test URLs

- Regular: https://github.com/refined-github/refined-github
- Fork: https://github.com/134130/refined-github
- Fork with native icon: https://github.com/refined-github/fork
- Private: https://github.com/refined-github/private
- Private fork: https://github.com/refined-github/fork

*/