From a616c8bf005a6df14734b722185575768888bd21 Mon Sep 17 00:00:00 2001 From: Philipp Beck Date: Sat, 15 Sep 2018 22:23:10 +0200 Subject: [PATCH 01/18] Add more useful 404 page feature --- source/content.js | 7 +++- source/features/not-found-page.js | 55 +++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 source/features/not-found-page.js diff --git a/source/content.js b/source/content.js index 185fb4aa68f3..0be8531cbb82 100644 --- a/source/content.js +++ b/source/content.js @@ -82,6 +82,7 @@ import hideIssueListAutocomplete from './features/hide-issue-list-autocomplete'; import userProfileFollowerBadge from './features/user-profile-follower-badge'; import setDefaultRepositoriesTypeToSources from './features/set-default-repositories-type-to-sources'; import markPrivateOrgs from './features/mark-private-orgs'; +import notFoundPage from './features/not-found-page'; import * as pageDetect from './libs/page-detect'; import {safeElementReady, enableFeature, safeOnAjaxedPages, injectCustomCSS} from './libs/utils'; @@ -93,10 +94,14 @@ window.select = select; async function init() { await safeElementReady('body'); - if (pageDetect.is404() || pageDetect.is500()) { + if (pageDetect.is500()) { return; } + if (pageDetect.is404()) { + enableFeature(notFoundPage); + return; + } if (document.body.classList.contains('logged-out')) { console.warn('%cRefined GitHub%c only works when you’re logged in to GitHub.', 'font-weight: bold', ''); return; diff --git a/source/features/not-found-page.js b/source/features/not-found-page.js new file mode 100644 index 000000000000..290113fed597 --- /dev/null +++ b/source/features/not-found-page.js @@ -0,0 +1,55 @@ +/* +This feature adds more useful 404 (not found) page. +- Display the full URL clickable piece by piece +*/ + +import {h} from 'dom-chef'; +import select from 'select-dom'; +import * as pageDetect from '../libs/page-detect'; + +const flatMap = (arr, fn) => + arr.flatMap ? + arr.flatMap(fn) : + arr.reduce((acc, ...args) => acc.concat(fn(...args)), []); + +const getRepoAnchors = (repoHref, repoPath) => { + const [prefix, ...parts] = repoPath.split('/'); + return parts.map((part, index) => { + const path = [repoHref, prefix, ...parts.slice(0, index + 1)]; + return {part}; + }); +}; + +const getAnchors = () => { + const repoPath = pageDetect.getRepoPath(); + if (!repoPath) { + return false; + } + + const {ownerName, repoName} = pageDetect.getOwnerAndRepo(); + const ownerHref = `/${ownerName}`; + const repoHref = `${ownerHref}/${repoName}`; + + return [ + {ownerName}, + {repoName}, + ...getRepoAnchors(repoHref, repoPath) + ]; +}; + +export default function () { + const anchors = getAnchors(); + if (!anchors) { + return; + } + + // NOTE: We need to append it after the parallax_wrapper because other elements might not be available yet. + return select('#parallax_wrapper').after( +
+

Do. Or do not. There is no try.

+

+ {flatMap(anchors, (e, i) => (i === 0 ? [e] : [' / ', e]))} +

+
+ ); +} From ad2baf4f12c44af348028ca7eb2edfe285b84b19 Mon Sep 17 00:00:00 2001 From: Philipp Beck Date: Sat, 15 Sep 2018 22:24:45 +0200 Subject: [PATCH 02/18] Check if the href of the anchor is reachable --- source/features/not-found-page.js | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/source/features/not-found-page.js b/source/features/not-found-page.js index 290113fed597..0b81af3d4591 100644 --- a/source/features/not-found-page.js +++ b/source/features/not-found-page.js @@ -1,6 +1,7 @@ /* This feature adds more useful 404 (not found) page. - Display the full URL clickable piece by piece +- Strikethrough all anchor that return a 404 status code */ import {h} from 'dom-chef'; @@ -12,6 +13,22 @@ const flatMap = (arr, fn) => arr.flatMap(fn) : arr.reduce((acc, ...args) => acc.concat(fn(...args)), []); +const strikethrough = target => { + const wrapper = ; + for (const child of target.childNodes) { + wrapper.appendChild(child); + } + return target.appendChild(wrapper); +}; + +const checkAnchors = async a => { + const {status} = await fetch(a.href, {method: 'head'}); + if (status === 404) { + strikethrough(a); + } + return a; +}; + const getRepoAnchors = (repoHref, repoPath) => { const [prefix, ...parts] = repoPath.split('/'); return parts.map((part, index) => { @@ -30,11 +47,17 @@ const getAnchors = () => { const ownerHref = `/${ownerName}`; const repoHref = `${ownerHref}/${repoName}`; - return [ + const anchors = [ {ownerName}, {repoName}, ...getRepoAnchors(repoHref, repoPath) ]; + + // NOTE: This will asynchronously check if the anchor href is reachable + // If not it will strikethrough the element content + anchors.forEach(checkAnchors); + + return anchors; }; export default function () { From 83ca869e9c32e4ce848cb14cc75484fa87ae414e Mon Sep 17 00:00:00 2001 From: Philipp Beck Date: Sat, 15 Sep 2018 22:29:22 +0200 Subject: [PATCH 03/18] Replace file links with more useful commit history --- source/features/not-found-page.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/source/features/not-found-page.js b/source/features/not-found-page.js index 0b81af3d4591..9d7494890162 100644 --- a/source/features/not-found-page.js +++ b/source/features/not-found-page.js @@ -2,6 +2,7 @@ This feature adds more useful 404 (not found) page. - Display the full URL clickable piece by piece - Strikethrough all anchor that return a 404 status code +- Replace file links with more useful commit history */ import {h} from 'dom-chef'; @@ -32,7 +33,13 @@ const checkAnchors = async a => { const getRepoAnchors = (repoHref, repoPath) => { const [prefix, ...parts] = repoPath.split('/'); return parts.map((part, index) => { - const path = [repoHref, prefix, ...parts.slice(0, index + 1)]; + const path = [ + repoHref, + // NOTE: Replace the file path with the commit path + // This allows to see the history of the file/path + prefix === 'blob' ? 'commits' : prefix, + ...parts.slice(0, index + 1) + ]; return {part}; }); }; From 6541347b3cca58043900472f46838f2b3eaf763c Mon Sep 17 00:00:00 2001 From: Philipp Beck Date: Tue, 18 Sep 2018 11:49:47 +0200 Subject: [PATCH 04/18] Make feature more general The solution was overly specific, so we changed it to work for all 404s. We ignore some paths that are never reachable (right now `tree` and `blob`). For now it also doesn't change `blob` to `commits` anymore. --- source/content.js | 4 +- source/features/not-found-page.js | 85 ------------------------ source/features/useful-not-found-page.js | 60 +++++++++++++++++ 3 files changed, 62 insertions(+), 87 deletions(-) delete mode 100644 source/features/not-found-page.js create mode 100644 source/features/useful-not-found-page.js diff --git a/source/content.js b/source/content.js index 0be8531cbb82..0c9bbb2f72b7 100644 --- a/source/content.js +++ b/source/content.js @@ -82,7 +82,7 @@ import hideIssueListAutocomplete from './features/hide-issue-list-autocomplete'; import userProfileFollowerBadge from './features/user-profile-follower-badge'; import setDefaultRepositoriesTypeToSources from './features/set-default-repositories-type-to-sources'; import markPrivateOrgs from './features/mark-private-orgs'; -import notFoundPage from './features/not-found-page'; +import usefulNotFoundPage from './features/useful-not-found-page'; import * as pageDetect from './libs/page-detect'; import {safeElementReady, enableFeature, safeOnAjaxedPages, injectCustomCSS} from './libs/utils'; @@ -99,7 +99,7 @@ async function init() { } if (pageDetect.is404()) { - enableFeature(notFoundPage); + enableFeature(usefulNotFoundPage); return; } if (document.body.classList.contains('logged-out')) { diff --git a/source/features/not-found-page.js b/source/features/not-found-page.js deleted file mode 100644 index 9d7494890162..000000000000 --- a/source/features/not-found-page.js +++ /dev/null @@ -1,85 +0,0 @@ -/* -This feature adds more useful 404 (not found) page. -- Display the full URL clickable piece by piece -- Strikethrough all anchor that return a 404 status code -- Replace file links with more useful commit history -*/ - -import {h} from 'dom-chef'; -import select from 'select-dom'; -import * as pageDetect from '../libs/page-detect'; - -const flatMap = (arr, fn) => - arr.flatMap ? - arr.flatMap(fn) : - arr.reduce((acc, ...args) => acc.concat(fn(...args)), []); - -const strikethrough = target => { - const wrapper = ; - for (const child of target.childNodes) { - wrapper.appendChild(child); - } - return target.appendChild(wrapper); -}; - -const checkAnchors = async a => { - const {status} = await fetch(a.href, {method: 'head'}); - if (status === 404) { - strikethrough(a); - } - return a; -}; - -const getRepoAnchors = (repoHref, repoPath) => { - const [prefix, ...parts] = repoPath.split('/'); - return parts.map((part, index) => { - const path = [ - repoHref, - // NOTE: Replace the file path with the commit path - // This allows to see the history of the file/path - prefix === 'blob' ? 'commits' : prefix, - ...parts.slice(0, index + 1) - ]; - return {part}; - }); -}; - -const getAnchors = () => { - const repoPath = pageDetect.getRepoPath(); - if (!repoPath) { - return false; - } - - const {ownerName, repoName} = pageDetect.getOwnerAndRepo(); - const ownerHref = `/${ownerName}`; - const repoHref = `${ownerHref}/${repoName}`; - - const anchors = [ - {ownerName}, - {repoName}, - ...getRepoAnchors(repoHref, repoPath) - ]; - - // NOTE: This will asynchronously check if the anchor href is reachable - // If not it will strikethrough the element content - anchors.forEach(checkAnchors); - - return anchors; -}; - -export default function () { - const anchors = getAnchors(); - if (!anchors) { - return; - } - - // NOTE: We need to append it after the parallax_wrapper because other elements might not be available yet. - return select('#parallax_wrapper').after( -
-

Do. Or do not. There is no try.

-

- {flatMap(anchors, (e, i) => (i === 0 ? [e] : [' / ', e]))} -

-
- ); -} diff --git a/source/features/useful-not-found-page.js b/source/features/useful-not-found-page.js new file mode 100644 index 000000000000..61f8a851c12f --- /dev/null +++ b/source/features/useful-not-found-page.js @@ -0,0 +1,60 @@ +/* +This feature adds more useful 404 (not found) page. +- Display the full URL clickable piece by piece +- Strikethrough all anchor that return a 404 status code +*/ + +import {h} from 'dom-chef'; +import select from 'select-dom'; +import * as pageDetect from '../libs/page-detect'; + +const flatMap = (arr, fn) => + arr.flatMap ? + arr.flatMap(fn) : + arr.reduce((acc, ...args) => acc.concat(fn(...args)), []); + +const strikethrough = target => { + const wrapper = ; + for (const child of target.childNodes) { + wrapper.appendChild(child); + } + return target.appendChild(wrapper); +}; + +const checkAnchor = async anchor => { + const {status} = await fetch(anchor.href, {method: 'head'}); + if (status === 404) { + strikethrough(anchor); + } + return anchor; +}; + +const ignoredPathParts = ['tree', 'blob']; +const buildAnchors = () => + pageDetect + .getCleanPathname() + .split('/') + .filter(part => !ignoredPathParts.includes(part)) + .map((part, index, parts) => { + const pathname = `/${parts.slice(0, index + 1).join('/')}`; + + const anchor = {part}; + // NOTE: Asyncronoulsy check the path and strikethrough if it isn't reachable + checkAnchor(anchor); + return anchor; + }); + +export default function () { + const anchors = buildAnchors(); + if (anchors.length === 0) { + return; + } + + // NOTE: We need to append it after the parallax_wrapper because other elements might not be available yet. + return select('#parallax_wrapper').after( +
+

Do. Or do not. There is no try.

+

{flatMap(anchors, (e, i) => (i === 0 ? [e] : [' / ', e]))}

+
+ ); +} From 719c1639d2502531757929a43c12c282ef751112 Mon Sep 17 00:00:00 2001 From: Federico Brigante Date: Wed, 3 Oct 2018 15:07:35 +0800 Subject: [PATCH 05/18] Various improvements - Fetch the correct URL (it used to skip `tree/blob` entirely) - Fetch from right to left to look nicer - Reduce used elements and drop extra copy - Use plain for loops instead of multiple functional loops and flatteners --- source/features/useful-not-found-page.js | 70 ++++++++++-------------- 1 file changed, 29 insertions(+), 41 deletions(-) diff --git a/source/features/useful-not-found-page.js b/source/features/useful-not-found-page.js index 61f8a851c12f..c2bae44e6d77 100644 --- a/source/features/useful-not-found-page.js +++ b/source/features/useful-not-found-page.js @@ -6,55 +6,43 @@ This feature adds more useful 404 (not found) page. import {h} from 'dom-chef'; import select from 'select-dom'; -import * as pageDetect from '../libs/page-detect'; +import {getCleanPathname} from '../libs/page-detect'; -const flatMap = (arr, fn) => - arr.flatMap ? - arr.flatMap(fn) : - arr.reduce((acc, ...args) => acc.concat(fn(...args)), []); - -const strikethrough = target => { - const wrapper = ; - for (const child of target.childNodes) { - wrapper.appendChild(child); - } - return target.appendChild(wrapper); -}; - -const checkAnchor = async anchor => { +async function checkAnchor(anchor) { const {status} = await fetch(anchor.href, {method: 'head'}); if (status === 404) { - strikethrough(anchor); + anchor.replaceWith( + {anchor.textContent} + ); } - return anchor; -}; - -const ignoredPathParts = ['tree', 'blob']; -const buildAnchors = () => - pageDetect - .getCleanPathname() - .split('/') - .filter(part => !ignoredPathParts.includes(part)) - .map((part, index, parts) => { - const pathname = `/${parts.slice(0, index + 1).join('/')}`; +} - const anchor = {part}; - // NOTE: Asyncronoulsy check the path and strikethrough if it isn't reachable - checkAnchor(anchor); - return anchor; - }); +function parseCurrentURL() { + const parts = getCleanPathname().split('/'); + if (parts[2] === 'blob') { // Blob URLs are never useful + parts[2] = 'tree'; + } + return parts; +} export default function () { - const anchors = buildAnchors(); - if (anchors.length === 0) { - return; + const parts = parseCurrentURL(); + const bar =

; + + for (let i = 0; i < parts.length; i++) { + const part = parts[i]; + if (i === 2 && part === 'tree') { + continue; + } + const pathname = '/' + parts.slice(0, i + 1).join('/'); + bar.append(' / ', {part}); } // NOTE: We need to append it after the parallax_wrapper because other elements might not be available yet. - return select('#parallax_wrapper').after( -
-

Do. Or do not. There is no try.

-

{flatMap(anchors, (e, i) => (i === 0 ? [e] : [' / ', e]))}

-
- ); + select('#parallax_wrapper').after(bar); + + // Check parts from right to left + for (let i = bar.children.length - 1; i >= 0; i--) { + checkAnchor(bar.children[i]); + } } From 67852be481876fea41e06196fa99efe4a20bbe52 Mon Sep 17 00:00:00 2001 From: Federico Brigante Date: Wed, 3 Oct 2018 15:08:41 +0800 Subject: [PATCH 06/18] Add link to commit history --- source/features/useful-not-found-page.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/source/features/useful-not-found-page.js b/source/features/useful-not-found-page.js index c2bae44e6d77..59f6ee5c6824 100644 --- a/source/features/useful-not-found-page.js +++ b/source/features/useful-not-found-page.js @@ -25,6 +25,23 @@ function parseCurrentURL() { return parts; } +async function createAdditionalLink(parts, bar) { + if (parts[2] !== 'tree') { + return; + } + parts[2] = 'commits'; + const url = '/' + parts.join('/'); + const {status} = await fetch(url, {method: 'head'}); + if (status === 404) { + return; + } + bar.after( +

+ See also the file’s {commit history} +

+ ); +} + export default function () { const parts = parseCurrentURL(); const bar =

; @@ -45,4 +62,6 @@ export default function () { for (let i = bar.children.length - 1; i >= 0; i--) { checkAnchor(bar.children[i]); } + + createAdditionalLink(parts, bar); } From af8d1ccf40b111e3d4a26860af9d3af545bdf71c Mon Sep 17 00:00:00 2001 From: Federico Brigante Date: Wed, 3 Oct 2018 16:55:41 +0800 Subject: [PATCH 07/18] Fix cache.getSet It wouldn't return cached values --- source/libs/cache.js | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/source/libs/cache.js b/source/libs/cache.js index f209037dbca8..92d190dbd1da 100644 --- a/source/libs/cache.js +++ b/source/libs/cache.js @@ -1,11 +1,12 @@ -export default async function getSet(key, getter, expiration) { +export async function getSet(key, getter, expiration) { const cache = await get(key); - if (cache === undefined) { - const value = getter(); - if (value !== undefined) { - await set(key, value, expiration); - return value; - } + if (cache !== undefined) { + return cache; + } + const value = await getter(); + if (value !== undefined) { + await set(key, value, expiration); + return value; } } @@ -16,7 +17,7 @@ export async function get(key) { }); // If it's not in the cache, it's best to return "undefined" - if (value === null) { + if (value === null || value === undefined) { return undefined; } return value; @@ -41,14 +42,17 @@ if (!browser.runtime.getBackground) { if (code === 'get-cache') { const [cached] = document.cookie.split('; ') .filter(item => item.startsWith(key + '=')); - if (cached) { const [, value] = cached.split('='); sendResponse(JSON.parse(value)); + console.log('CACHE: found', key, value); } else { sendResponse(); + console.log('CACHE: not found', key); } } else if (code === 'set-cache') { + console.log('CACHE: setting', key, value); + // Store as JSON to preserve data type // otherwise Booleans and Numbers become strings document.cookie = `${key}=${JSON.stringify(value)}; max-age=${expiration ? expiration * 3600 * 24 : ''}`; From 2507a80bde9be0e0bbe7646c4fa3997fb610739d Mon Sep 17 00:00:00 2001 From: Federico Brigante Date: Wed, 3 Oct 2018 16:57:20 +0800 Subject: [PATCH 08/18] Add default branch API getter --- source/libs/get-default-branch.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 source/libs/get-default-branch.js diff --git a/source/libs/get-default-branch.js b/source/libs/get-default-branch.js new file mode 100644 index 000000000000..213823884b07 --- /dev/null +++ b/source/libs/get-default-branch.js @@ -0,0 +1,17 @@ +import * as cache from './cache'; +import * as api from './api'; +import {getOwnerAndRepo} from './page-detect'; + +async function fetchFromApi(user, repo) { + const response = await api.v3(`repos/${user}/${repo}`); + if (response && response.default_branch) { + return response.default_branch; + } +} + +export default function () { + const {ownerName, repoName} = getOwnerAndRepo(); + return cache.getSet(`default-branch:${ownerName}/${repoName}`, + () => fetchFromApi(ownerName, repoName) + ); +} From 4aee8283c3478650bd0c71df18195f9edd25c488 Mon Sep 17 00:00:00 2001 From: Federico Brigante Date: Wed, 3 Oct 2018 16:58:18 +0800 Subject: [PATCH 09/18] Fetch default branch for the branch buttons If not available on page --- source/features/add-branch-buttons.js | 49 +++++---------------------- source/libs/get-default-branch.js | 26 +++++++++++++- 2 files changed, 33 insertions(+), 42 deletions(-) diff --git a/source/features/add-branch-buttons.js b/source/features/add-branch-buttons.js index 2384e09e521f..31e412c51f23 100644 --- a/source/features/add-branch-buttons.js +++ b/source/features/add-branch-buttons.js @@ -2,17 +2,10 @@ import {h} from 'dom-chef'; import select from 'select-dom'; import compareVersions from 'tiny-version-compare'; import * as icons from '../libs/icons'; -import * as cache from '../libs/cache'; import {appendBefore} from '../libs/utils'; import {groupSiblings} from '../libs/group-buttons'; -import {getRepoURL, isRepoRoot, getOwnerAndRepo} from '../libs/page-detect'; - -// This regex should match all of these combinations: -// "This branch is even with master." -// "This branch is 1 commit behind master." -// "This branch is 1 commit ahead of master." -// "This branch is 1 commit ahead, 27 commits behind master." -const branchInfoRegex = /([^ ]+)\.$/; +import getDefaultBranch from '../libs/get-default-branch'; +import {getRepoURL, isRepoRoot} from '../libs/page-detect'; function getTagLink() { const tags = select @@ -45,38 +38,12 @@ function getTagLink() { return link; } -async function getDefaultBranchNameIfDifferent() { - const {ownerName, repoName} = getOwnerAndRepo(); - const cacheKey = `default-branch:${ownerName}/${repoName}`; - - // Return the cached name if it differs from the current one - const cachedName = await cache.get(cacheKey); - if (cachedName) { - const currentBranch = select('[data-hotkey="w"] span').textContent; - return cachedName === currentBranch ? false : cachedName; - } - - // We can find the name in the infobar, available in folder views - const branchInfo = select('.branch-infobar'); - if (!branchInfo) { - return; - } - - // Parse the infobar - const [, branchName] = branchInfo.textContent.trim().match(branchInfoRegex) || []; - if (branchName) { - cache.set(cacheKey, branchName, 1); - return branchName; - } -} - async function getDefaultBranchLink() { - if (select.exists('.repohead h1 .octicon-repo-forked')) { - return; // It's a fork, no "default branch" info available #1132 - } + const defaultBranch = await getDefaultBranch(); + const currentBranch = select('[data-hotkey="w"] span').textContent; - const branchName = await getDefaultBranchNameIfDifferent(); - if (!branchName) { + // Don't show the button if we’re already on the default branch + if (defaultBranch === currentBranch) { return; } @@ -84,7 +51,7 @@ async function getDefaultBranchLink() { if (isRepoRoot()) { url = `/${getRepoURL()}`; } else { - const branchLink = select(`.select-menu-item[data-name='${branchName}']`); + const branchLink = select(`.select-menu-item[data-name='${defaultBranch}']`); if (!branchLink) { return; } @@ -98,7 +65,7 @@ async function getDefaultBranchLink() { aria-label="Visit the default branch"> {icons.branch()} {' '} - {branchName} + {defaultBranch} ); } diff --git a/source/libs/get-default-branch.js b/source/libs/get-default-branch.js index 213823884b07..034d828c25e9 100644 --- a/source/libs/get-default-branch.js +++ b/source/libs/get-default-branch.js @@ -1,7 +1,31 @@ +import select from 'select-dom'; import * as cache from './cache'; import * as api from './api'; import {getOwnerAndRepo} from './page-detect'; +// This regex should match all of these combinations: +// "This branch is even with master." +// "This branch is 1 commit behind master." +// "This branch is 1 commit ahead of master." +// "This branch is 1 commit ahead, 27 commits behind master." +const branchInfoRegex = /([^ ]+)\.$/; + +function parseBranchFromDom() { + if (select.exists('.repohead h1 .octicon-repo-forked')) { + return; // It's a fork, no "default branch" info available #1132 + } + + // We can find the name in the infobar, available in folder views + const branchInfo = select('.branch-infobar'); + if (!branchInfo) { + return; + } + + // Parse the infobar + const [, branchName] = branchInfo.textContent.trim().match(branchInfoRegex) || []; + return branchName; // `string` or undefined +} + async function fetchFromApi(user, repo) { const response = await api.v3(`repos/${user}/${repo}`); if (response && response.default_branch) { @@ -12,6 +36,6 @@ async function fetchFromApi(user, repo) { export default function () { const {ownerName, repoName} = getOwnerAndRepo(); return cache.getSet(`default-branch:${ownerName}/${repoName}`, - () => fetchFromApi(ownerName, repoName) + () => parseBranchFromDom() || fetchFromApi(ownerName, repoName) ); } From 1c223431daaed321fa62f19e1712e7dfb6bed8ac Mon Sep 17 00:00:00 2001 From: Federico Brigante Date: Wed, 3 Oct 2018 17:15:58 +0800 Subject: [PATCH 10/18] Avoid merge conflict --- source/content.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/content.js b/source/content.js index 0c9bbb2f72b7..443de725f34d 100644 --- a/source/content.js +++ b/source/content.js @@ -80,9 +80,9 @@ import hideCommentsFaster from './features/hide-comments-faster'; import linkifyCommitSha from './features/linkify-commit-sha'; import hideIssueListAutocomplete from './features/hide-issue-list-autocomplete'; import userProfileFollowerBadge from './features/user-profile-follower-badge'; +import usefulNotFoundPage from './features/useful-not-found-page'; import setDefaultRepositoriesTypeToSources from './features/set-default-repositories-type-to-sources'; import markPrivateOrgs from './features/mark-private-orgs'; -import usefulNotFoundPage from './features/useful-not-found-page'; import * as pageDetect from './libs/page-detect'; import {safeElementReady, enableFeature, safeOnAjaxedPages, injectCustomCSS} from './libs/utils'; From 869e08bff4dfa0e07add8e11d06a1fe3289d1664 Mon Sep 17 00:00:00 2001 From: Federico Brigante Date: Wed, 3 Oct 2018 17:20:37 +0800 Subject: [PATCH 11/18] Repetition kills you --- source/features/useful-not-found-page.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/source/features/useful-not-found-page.js b/source/features/useful-not-found-page.js index 59f6ee5c6824..c1f11a0a7081 100644 --- a/source/features/useful-not-found-page.js +++ b/source/features/useful-not-found-page.js @@ -8,9 +8,13 @@ import {h} from 'dom-chef'; import select from 'select-dom'; import {getCleanPathname} from '../libs/page-detect'; +async function is404(url) { + const {status} = await fetch(url, {method: 'head'}); + return status === 404; +} + async function checkAnchor(anchor) { - const {status} = await fetch(anchor.href, {method: 'head'}); - if (status === 404) { + if (await is404(anchor.href)) { anchor.replaceWith( {anchor.textContent} ); @@ -25,14 +29,14 @@ function parseCurrentURL() { return parts; } -async function createAdditionalLink(parts, bar) { +async function addCommitHistoryLink(bar) { + const parts = parseCurrentURL(); if (parts[2] !== 'tree') { return; } parts[2] = 'commits'; const url = '/' + parts.join('/'); - const {status} = await fetch(url, {method: 'head'}); - if (status === 404) { + if (await is404(url)) { return; } bar.after( @@ -63,5 +67,5 @@ export default function () { checkAnchor(bar.children[i]); } - createAdditionalLink(parts, bar); + addCommitHistoryLink(bar); } From 0645f9b3b7cd3d163fdaed362a0842c518d607fb Mon Sep 17 00:00:00 2001 From: Federico Brigante Date: Wed, 3 Oct 2018 17:20:56 +0800 Subject: [PATCH 12/18] Add link to files on default branch --- source/features/useful-not-found-page.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/source/features/useful-not-found-page.js b/source/features/useful-not-found-page.js index c1f11a0a7081..ea2211e85faf 100644 --- a/source/features/useful-not-found-page.js +++ b/source/features/useful-not-found-page.js @@ -7,6 +7,7 @@ This feature adds more useful 404 (not found) page. import {h} from 'dom-chef'; import select from 'select-dom'; import {getCleanPathname} from '../libs/page-detect'; +import getDefaultBranch from '../libs/get-default-branch'; async function is404(url) { const {status} = await fetch(url, {method: 'head'}); @@ -46,6 +47,28 @@ async function addCommitHistoryLink(bar) { ); } +async function addDefaultBranchLink(bar) { + const parts = getCleanPathname().split('/'); + const [,,, branch] = parts; + if (!branch) { + return; + } + const defaultBranch = await getDefaultBranch(); + if (!defaultBranch || branch === defaultBranch) { + return; + } + parts[3] = defaultBranch; + const url = '/' + parts.join('/'); + if (await is404(url)) { + return; + } + bar.after( +

+ See also the file on the {default branch} +

+ ); +} + export default function () { const parts = parseCurrentURL(); const bar =

; @@ -68,4 +91,5 @@ export default function () { } addCommitHistoryLink(bar); + addDefaultBranchLink(bar); } From 63548c87402b4ceb02ece01ca585b4b51170df8f Mon Sep 17 00:00:00 2001 From: Federico Brigante Date: Wed, 3 Oct 2018 17:23:29 +0800 Subject: [PATCH 13/18] Avoid / before user name Because this way it matches the repo header --- source/features/useful-not-found-page.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/features/useful-not-found-page.js b/source/features/useful-not-found-page.js index ea2211e85faf..8160b66cb27f 100644 --- a/source/features/useful-not-found-page.js +++ b/source/features/useful-not-found-page.js @@ -79,7 +79,7 @@ export default function () { continue; } const pathname = '/' + parts.slice(0, i + 1).join('/'); - bar.append(' / ', {part}); + bar.append(i ? ' / ' : '', {part}); } // NOTE: We need to append it after the parallax_wrapper because other elements might not be available yet. From 10e5e828a4888034b0b92bc3fd083ea864b7d391 Mon Sep 17 00:00:00 2001 From: Federico Brigante Date: Wed, 3 Oct 2018 17:42:36 +0800 Subject: [PATCH 14/18] Mention in readme --- readme.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 84c5e0da14a2..70eb1647da08 100644 --- a/readme.md +++ b/readme.md @@ -99,7 +99,8 @@ GitHub Enterprise is also supported. More info in the options. - [Use the pull request title as the commit title when merging with `Squash and merge`](https://github.com/sindresorhus/refined-github/issues/276). - [View linked gists inline in comments.](https://user-images.githubusercontent.com/6978877/33911900-c62ee968-df8b-11e7-8685-506ffafc60b4.PNG) - [Avoid opening duplicate issues thanks to the list of possibly-related issues.](https://user-images.githubusercontent.com/29176678/37566899-85953e6e-2abf-11e8-9f0e-52d18c87bbe3.gif) -- [Use the pull request description as the commit message when merging with `Squash and merge`](https://github.com/sindresorhus/refined-github/issues/1322). +- [Use the pull request description as the commit message when merging with `Squash and merge`.](https://github.com/sindresorhus/refined-github/issues/1322). +- [Access related pages on 404 pages.](https://user-images.githubusercontent.com/1402241/46402857-7bdada80-c733-11e8-91a1-856573078ff5.png) ### More actions From a6b7ccbe49c72c50195081b62d7e8a7f1bf0749c Mon Sep 17 00:00:00 2001 From: Federico Brigante Date: Thu, 4 Oct 2018 02:11:33 +0800 Subject: [PATCH 15/18] Skip checking the last part, it's always a 404 --- source/features/useful-not-found-page.js | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/source/features/useful-not-found-page.js b/source/features/useful-not-found-page.js index 8160b66cb27f..f7929411e987 100644 --- a/source/features/useful-not-found-page.js +++ b/source/features/useful-not-found-page.js @@ -14,11 +14,13 @@ async function is404(url) { return status === 404; } +function getStrikeThrough(text) { + return {text}; +} + async function checkAnchor(anchor) { if (await is404(anchor.href)) { - anchor.replaceWith( - {anchor.textContent} - ); + anchor.replaceWith(getStrikeThrough(anchor.textContent)); } } @@ -78,15 +80,20 @@ export default function () { if (i === 2 && part === 'tree') { continue; } - const pathname = '/' + parts.slice(0, i + 1).join('/'); - bar.append(i ? ' / ' : '', {part}); + if (i === parts.length - 1) { + // The last part of the URL is a known 404 + bar.append(' / ', getStrikeThrough(part)); + } else { + const pathname = '/' + parts.slice(0, i + 1).join('/'); + bar.append(i ? ' / ' : '', {part}); + } } // NOTE: We need to append it after the parallax_wrapper because other elements might not be available yet. select('#parallax_wrapper').after(bar); - // Check parts from right to left - for (let i = bar.children.length - 1; i >= 0; i--) { + // Check parts from right to left; skip the last part + for (let i = bar.children.length - 2; i >= 0; i--) { checkAnchor(bar.children[i]); } From dd085c2ab15d215efed18532a523fe46a25d32ca Mon Sep 17 00:00:00 2001 From: Federico Brigante Date: Thu, 4 Oct 2018 02:11:43 +0800 Subject: [PATCH 16/18] Add some comments --- source/features/useful-not-found-page.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source/features/useful-not-found-page.js b/source/features/useful-not-found-page.js index f7929411e987..8f0e2d51f595 100644 --- a/source/features/useful-not-found-page.js +++ b/source/features/useful-not-found-page.js @@ -32,6 +32,7 @@ function parseCurrentURL() { return parts; } +// If the resource was deleted, link to the commit history async function addCommitHistoryLink(bar) { const parts = parseCurrentURL(); if (parts[2] !== 'tree') { @@ -49,6 +50,7 @@ async function addCommitHistoryLink(bar) { ); } +// If the resource exists in the default branch, link it async function addDefaultBranchLink(bar) { const parts = getCleanPathname().split('/'); const [,,, branch] = parts; @@ -59,7 +61,7 @@ async function addDefaultBranchLink(bar) { if (!defaultBranch || branch === defaultBranch) { return; } - parts[3] = defaultBranch; + parts[3] = defaultBranch; // Change branch const url = '/' + parts.join('/'); if (await is404(url)) { return; @@ -78,6 +80,7 @@ export default function () { for (let i = 0; i < parts.length; i++) { const part = parts[i]; if (i === 2 && part === 'tree') { + // /tree/ is not a real part of the URL continue; } if (i === parts.length - 1) { From 6153252899f6834076cada6ad85c2f7483a026e4 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Thu, 4 Oct 2018 14:29:48 +0700 Subject: [PATCH 17/18] Update useful-not-found-page.js --- source/features/useful-not-found-page.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/features/useful-not-found-page.js b/source/features/useful-not-found-page.js index 8f0e2d51f595..5017729570d3 100644 --- a/source/features/useful-not-found-page.js +++ b/source/features/useful-not-found-page.js @@ -75,12 +75,12 @@ async function addDefaultBranchLink(bar) { export default function () { const parts = parseCurrentURL(); - const bar =

; + const bar =

; for (let i = 0; i < parts.length; i++) { const part = parts[i]; if (i === 2 && part === 'tree') { - // /tree/ is not a real part of the URL + // `/tree/` is not a real part of the URL continue; } if (i === parts.length - 1) { From fadea7a05263e732f9dbc1887ba17e8c2c3e070b Mon Sep 17 00:00:00 2001 From: Federico Brigante Date: Thu, 4 Oct 2018 19:42:27 +0800 Subject: [PATCH 18/18] Review changes --- source/features/useful-not-found-page.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/source/features/useful-not-found-page.js b/source/features/useful-not-found-page.js index 5017729570d3..6d32eccc6147 100644 --- a/source/features/useful-not-found-page.js +++ b/source/features/useful-not-found-page.js @@ -50,10 +50,10 @@ async function addCommitHistoryLink(bar) { ); } -// If the resource exists in the default branch, link it +// If the resource exists in the default branch, link to it async function addDefaultBranchLink(bar) { const parts = getCleanPathname().split('/'); - const [,,, branch] = parts; + const branch = parts[3]; if (!branch) { return; } @@ -77,8 +77,7 @@ export default function () { const parts = parseCurrentURL(); const bar =

; - for (let i = 0; i < parts.length; i++) { - const part = parts[i]; + for (const [i, part] of parts.entries()) { if (i === 2 && part === 'tree') { // `/tree/` is not a real part of the URL continue;