Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add link to forked repo below the source #2153

Merged
merged 12 commits into from Jul 3, 2019
1 change: 1 addition & 0 deletions source/content.ts
Expand Up @@ -106,6 +106,7 @@ import './features/highest-rated-comment';
import './features/clean-issue-filters';
import './features/minimize-upload-bar';
import './features/cycle-lists-with-keyboard-shortcuts';
import './features/forked-to';

import './features/scrollable-code-and-blockquote.css';
import './features/center-reactions-popup.css';
Expand Down
99 changes: 99 additions & 0 deletions source/features/forked-to.tsx
@@ -0,0 +1,99 @@
import React from 'dom-chef';
import select from 'select-dom';
import cache from 'webext-storage-cache';
import features from '../libs/features';
import {getRepoURL} from '../libs/utils';
import {isRepoWithAccess} from '../libs/page-detect';

const getCacheKey = (repo: string): string => `forked-to:${repo}`;

async function showForks(): Promise<void> {
const cached = await getValidatedCache(getSourceRepo());
const pageHeader = select('.pagehead h1.public')!;
for (const fork of cached.filter(fork => fork !== getRepoURL())) {
pageHeader.append(
<span className="fork-flag rgh-forked">forked to&nbsp;
<a href={`/${fork}`}>{fork}</a>
</span>
);
}
}

function rememberCurrentFork(): void {
if (!isRepoWithAccess()) {
return;
}

const forkedRepo = findForkedRepo();
if (forkedRepo) {
addAndStoreCache(forkedRepo, getRepoURL());
}
}

function watchForkDialog(): void {
const forkDialog = select('details-dialog[src*="/fork"]')!;
select('include-fragment', forkDialog)!.addEventListener('load', () => {
const forks = select.all('.octicon-repo-forked', forkDialog).map(forkElement => {
return forkElement.parentNode!.textContent!.trim();
});
addAndStoreCache(getSourceRepo(), ...forks);
});
}

function findForkedRepo(): string | undefined {
const forkSourceElement = select<HTMLAnchorElement>('.fork-flag:not(.rgh-forked) a');
if (forkSourceElement) {
return forkSourceElement.pathname.slice(1);
}

return undefined;
}

function getSourceRepo(): string {
return findForkedRepo() || getRepoURL();
}

async function validateFork(repo: string): Promise<boolean> {
const response = await fetch(location.origin + '/' + repo, {method: 'HEAD'});
return response.ok;
}

async function getValidatedCache(repo: string): Promise<string[]> {
const cached = await cache.get<string[]>(getCacheKey(repo)) || [];
const validForks = cached.filter(validateFork).sort(undefined);

if (cached.length !== validForks.length) {
await cache.set<string[]>(getCacheKey(repo), validForks, 10);
}

return validForks;
}

async function addAndStoreCache(repo: string, ...forks: string[]): Promise<void> {
const cached = await cache.get<string[]>(getCacheKey(repo)) || [];
for (const fork of forks) {
if (!cached.includes(fork)) {
cached.push(fork);
}
}

await cache.set<string[]>(getCacheKey(repo), cached, 10);
}

async function init(): Promise<void> {
watchForkDialog();

rememberCurrentFork();

showForks();
}

features.add({
id: 'forked-to',
description: 'Add link to forked repo below the source',
jerone marked this conversation as resolved.
Show resolved Hide resolved
include: [
features.isRepo
],
load: features.onAjaxedPages,
init
});
2 changes: 2 additions & 0 deletions source/libs/page-detect.ts
Expand Up @@ -91,6 +91,8 @@ export const isRepoSettings = (): boolean => /^settings/.test(getRepoPath()!);

export const isRepoTree = (): boolean => isRepoRoot() || /^tree\//.test(getRepoPath()!);

export const isRepoWithAccess = (): boolean => isRepo() && select.exists('.reponav-item[href$="/settings"]');

export const isSingleCommit = (): boolean => /^commit\/[0-9a-f]{5,40}/.test(getRepoPath()!);

export const isSingleFile = (): boolean => /^blob\//.test(getRepoPath()!);
Expand Down