summaryrefslogtreecommitdiff
path: root/source/features/releases-dropdown.tsx
blob: 2d8c078067678f34c20068d0ce181577ca2dd67a (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
import React from 'dom-chef';
import * as pageDetect from 'github-url-detection';
import delegate, {DelegateEvent} from 'delegate-it';
import {CachedFunction} from 'webext-storage-cache';

import api from '../github-helpers/api.js';
import features from '../feature-manager.js';
import {buildRepoURL, cacheByRepo} from '../github-helpers/index.js';
import observe from '../helpers/selector-observer.js';
import GetReleases from './releases-dropdown.gql';

const getReleases = new CachedFunction('releases', {
	async updater(): Promise<string[]> {
		const {repository} = await api.v4(GetReleases);
		return repository.releases.nodes.map(({tagName}: {tagName: string}) => tagName);
	},
	maxAge: {hours: 1},
	staleWhileRevalidate: {days: 4},
	cacheKey: cacheByRepo,
});

// `datalist` selections don't have an `inputType`
async function selectionHandler(event: DelegateEvent<Event, HTMLInputElement>): Promise<void> {
	const field = event.delegateTarget;
	const selectedTag = field.value;
	const releases = await getReleases.get(); // Expected to be in cache
	if (!('inputType' in event) && releases.includes(selectedTag)) {
		location.href = buildRepoURL('releases/tag', encodeURIComponent(selectedTag));
		field.value = ''; // Can't call `preventDefault`, the `input` event is not cancelable
	}
}

async function addList(searchField: HTMLInputElement): Promise<void> {
	const releases = await getReleases.get();
	if (releases.length === 0) {
		return;
	}

	searchField.setAttribute('list', 'rgh-releases-dropdown');
	searchField.after(
		<datalist id="rgh-releases-dropdown">
			{releases.map(tag => <option value={tag}/>)}
		</datalist>,
	);
}

const searchFieldSelector = 'input#release-filter';
async function init(signal: AbortSignal): Promise<void> {
	observe(searchFieldSelector, addList, {signal});
	delegate(searchFieldSelector, 'input', selectionHandler, {signal});
}

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

/*

## Test URLs

https://github.com/refined-github/refined-github/tags
https://github.com/refined-github/sandbox/releases

*/