import React from 'dom-chef'; import cache from 'webext-storage-cache'; import select from 'select-dom'; import TagIcon from 'octicon/tag.svg'; import elementReady from 'element-ready'; import * as pageDetect from 'github-url-detection'; import features from '.'; import * as api from '../github-helpers/api'; import looseParseInt from '../helpers/loose-parse-int'; import {appendBefore} from '../helpers/dom-utils'; import {createDropdownItem} from './more-dropdown'; import {buildRepoURL, getRepo} from '../github-helpers'; const getCacheKey = (): string => `releases-count:${getRepo()!.nameWithOwner}`; function parseCountFromDom(): number { const releasesCountElement = select('.numbers-summary a[href$="/releases"] .num'); if (releasesCountElement) { return looseParseInt(releasesCountElement); } // In "Repository refresh" layout, look for the releases link in the sidebar const moreReleasesCountElement = select('[href$="/tags"] strong'); if (moreReleasesCountElement) { return looseParseInt(moreReleasesCountElement); } return 0; } async function fetchFromApi(): Promise { const {repository} = await api.v4(` repository() { refs(refPrefix: "refs/tags/") { totalCount } } `); return repository.refs.totalCount; } const getReleaseCount = cache.function(async () => pageDetect.isRepoRoot() ? parseCountFromDom() : fetchFromApi(), { maxAge: {hours: 1}, staleWhileRevalidate: {days: 3}, cacheKey: getCacheKey }); async function init(): Promise { // Always prefer the information in the DOM if (pageDetect.isRepoRoot()) { await cache.delete(getCacheKey()); } const count = await getReleaseCount(); if (count === 0) { return false; } // Wait for the tab bar to be loaded await elementReady([ '.pagehead + *', // Pre "Repository refresh" layout '.UnderlineNav-body + *' ].join()); const repoNavigationBar = select('.js-responsive-underlinenav'); if (repoNavigationBar) { // "Repository refresh" layout const releasesTab = ( Releases {count && {count}} ); select(':scope > ul', repoNavigationBar)!.append(
  • {releasesTab}
  • ); // This re-triggers the overflow listener forcing it to also hide this tab if necessary #3347 repoNavigationBar.replaceWith(repoNavigationBar); // Update "selected" tab mark if (pageDetect.isReleasesOrTags()) { const selected = select('.UnderlineNav-item.selected'); if (selected) { selected.classList.remove('selected'); selected.removeAttribute('aria-current'); } releasesTab.classList.add('selected'); releasesTab.setAttribute('aria-current', 'page'); } appendBefore( select('.js-responsive-underlinenav .dropdown-menu ul')!, '.dropdown-divider', // Won't exist if `more-dropdown` is disabled createDropdownItem('Releases', buildRepoURL('releases'), { 'data-menu-item': 'rgh-releases-item' }) ); // Hide redundant 'Releases' section from repo sidebar if (pageDetect.isRepoRoot()) { const sidebarReleases = await elementReady('.BorderGrid-cell a[href$="/releases"]'); sidebarReleases!.closest('.BorderGrid-row')!.setAttribute('hidden', ''); } return; } const releasesTab = ( Releases {count && {count}} ); appendBefore( // GHE doesn't have `.reponav > ul` select('.reponav > ul') ?? select('.reponav')!, '.reponav-dropdown, [data-selected-links^="repo_settings"]', releasesTab ); // Update "selected" tab mark if (pageDetect.isReleasesOrTags()) { select('.reponav-item.selected')?.classList.remove('js-selected-navigation-item', 'selected'); releasesTab.classList.add('js-selected-navigation-item', 'selected'); releasesTab.dataset.selectedLinks = 'repo_releases'; // Required for ajaxLoad } } void features.add(__filebasename, { shortcuts: { 'g r': 'Go to Releases' }, include: [ pageDetect.isRepo ], awaitDomReady: false, init });