From 791abea0aa91c5a2f5d888c2b0a22847ef67000b Mon Sep 17 00:00:00 2001 From: reglim Date: Mon, 27 Mar 2023 08:36:23 +0200 Subject: [PATCH] Fix: Some urls causing loading loop --- web/src/pages/Docs.tsx | 67 ++++++++++++----- web/src/repositories/ProjectRepository.ts | 23 ++++++ .../repositories/ProjectRepository.test.ts | 72 +++++++++++++++++++ 3 files changed, 143 insertions(+), 19 deletions(-) diff --git a/web/src/pages/Docs.tsx b/web/src/pages/Docs.tsx index 18bf2c2eb..a03291a83 100644 --- a/web/src/pages/Docs.tsx +++ b/web/src/pages/Docs.tsx @@ -15,6 +15,20 @@ import styles from './../style/pages/Docs.module.css' import LoadingPage from './LoadingPage' import NotFound from './NotFound' +const trimSlash = (str: string): string => { + str = str.trim() + + while (str.charAt(0) === '/') { + str = str.substring(1) + } + + while (str.charAt(str.length - 1) === '/') { + str = str.substring(0, str.length - 1) + } + + return str +} + export default function Docs (): JSX.Element { const projectParam = useParams().project ?? '' const versionParam = useParams().version ?? 'latest' @@ -23,7 +37,7 @@ export default function Docs (): JSX.Element { const [project] = useState(projectParam) const [version, setVersion] = useState(versionParam) - const [page, setPage] = useState(pageParam) + const [page, setPage] = useState(trimSlash(pageParam)) const [hideUi, setHideUi] = useState(hideUiParam) const [versions, setVersions] = useState([]) const [loadingFailed, setLoadingFailed] = useState(false) @@ -40,19 +54,7 @@ export default function Docs (): JSX.Element { const getVersionToUse = (allVersions: ProjectDetails[]): string => { if (version === 'latest') { - // latest version -> check if there is a latest tag - const versionWithLatestTag = allVersions.find((v) => - (v.tags ?? []).includes('latest') - ) - - // if there is a latest tag, use it, - // otherwise use the latest version by sorting - const latestVersion = - versionWithLatestTag != null - ? versionWithLatestTag.name - : allVersions[allVersions.length - 1].name - - return latestVersion + return ProjectRepository.getLatestVersion(allVersions).name } // custom version -> check if it exists @@ -70,11 +72,25 @@ export default function Docs (): JSX.Element { // replace if the state is the same or the url contained latest // this makes sure it is possible to go back to the overview page - if (window.location.hash.substring(1) === url || window.location.hash.includes(`${project}/latest`)) { + if (window.location.hash.includes(`${project}/latest`)) { navigate(url, { replace: true }) return } + const oldUrl = window.location.hash.substring(1) + const oldProject = oldUrl.split('/')[1] + const oldVersion = oldUrl.split('/')[2] + const oldPage = oldUrl.split('/')[3].split('?')[0] + const oldHideUi = oldUrl.includes('hide-ui=true') + + const isSameUrl = oldProject === project && oldVersion === version && oldPage === page && oldHideUi === hideUi + + console.log('update url', url, oldUrl, oldProject, oldVersion, oldPage, project, version, page, oldHideUi, hideUi, isSameUrl) + + if (isSameUrl) { + return + } + navigate(url) }, [project, version, page, hideUi]) @@ -88,12 +104,16 @@ export default function Docs (): JSX.Element { return } + // remove query params newPage = newPage.split('?')[0] + newPage = trimSlash(newPage) if (newPage === page) { return } + console.log('update page', newPage, page) + setPage(newPage) }, [location]) @@ -101,13 +121,13 @@ export default function Docs (): JSX.Element { void (async (): Promise => { try { let allVersions = await ProjectRepository.getVersions(project) + allVersions = allVersions.sort((a, b) => ProjectRepository.compareVersions(a, b)) if (allVersions.length === 0) { setLoadingFailed(true) return } - allVersions = allVersions.sort((a, b) => ProjectRepository.compareVersions(a, b)) const versionToUse = getVersionToUse(allVersions) if (versionToUse === '') { @@ -147,13 +167,22 @@ export default function Docs (): JSX.Element { // update the path in the url // @ts-expect-error - ts does not find the location on the iframe const path: string = iFrameRef.current.contentWindow.location.href as string - const page = path.split(`${version}/`)[1] + let newPage = path.split(`${version}/`)[1] + + if (newPage == null) { + return + } + + newPage = newPage.split('?')[0] + newPage = trimSlash(newPage) - if (page == null || page.trim().length < 1) { + if (newPage.length < 1 || newPage === page) { return } - setPage(page) + console.log('update page', newPage, page, path) + + setPage(newPage) // make all links in iframe open in new tab // @ts-expect-error - ts does not find the document on the iframe diff --git a/web/src/repositories/ProjectRepository.ts b/web/src/repositories/ProjectRepository.ts index f4b6d09d4..0b49a42a0 100644 --- a/web/src/repositories/ProjectRepository.ts +++ b/web/src/repositories/ProjectRepository.ts @@ -36,6 +36,28 @@ async function getVersions (projectName: string): Promise { return json.versions } +/** + * Returns the latest version of a project. + * Order of precedence: latest, latest tag, latest version + * @param versions all versions of a project + */ +function getLatestVersion (versions: ProjectDetails[]): ProjectDetails { + const latest = versions.find((v) => v.name.includes('latest')) + if (latest != null) { + return latest + } + + const latestTag = versions.find((v) => v.tags.includes('latest')) + if (latestTag != null) { + return latestTag + } + + const sortedVersions = versions + .sort((a, b) => compareVersions(a, b)) + + return sortedVersions[sortedVersions.length - 1] +} + /** * Returns a SearchResult object containing all projects and versions that contain the search query in their name or tag * @param {Project[]} projects List of all projects @@ -239,6 +261,7 @@ function setFavorite (projectName: string, shouldBeFavorite: boolean): void { const exp = { getVersions, + getLatestVersion, filterHiddenVersions, search, getProjectLogoURL, diff --git a/web/src/tests/repositories/ProjectRepository.test.ts b/web/src/tests/repositories/ProjectRepository.test.ts index ffeefd44d..8e9d5fb19 100644 --- a/web/src/tests/repositories/ProjectRepository.test.ts +++ b/web/src/tests/repositories/ProjectRepository.test.ts @@ -460,3 +460,75 @@ describe('filterHiddenVersions', () => { expect(result).toStrictEqual([]) }) }) + +describe('getLatestVersion', () => { + test('should return latest version by name', () => { + const versions: ProjectDetails[] = [ + { + name: '1.0.0', + hidden: false, + tags: [] + }, + { + name: '2.0.0', + hidden: false, + tags: [] + } + ] + + const latestVersion = ProjectRepository.getLatestVersion(versions) + expect(latestVersion).toStrictEqual(versions[1]) + }) + + test('should return version with latest in name', () => { + const versions: ProjectDetails[] = [ + { + name: '1.0.0', + hidden: false, + tags: [] + }, + { + name: 'latest', + hidden: false, + tags: [] + }] + + const latestVersion = ProjectRepository.getLatestVersion(versions) + expect(latestVersion).toStrictEqual(versions[1]) + }) + + test('should return version with latest tag', () => { + const versions: ProjectDetails[] = [ + { + name: '1.0.0', + hidden: false, + tags: ['latest'] + }, + { + name: '2.0.0', + hidden: false, + tags: [] + }] + + const latestVersion = ProjectRepository.getLatestVersion(versions) + expect(latestVersion).toStrictEqual(versions[0]) + }) + + test('should prefer version with latest in name over latest tag', () => { + const versions: ProjectDetails[] = [ + { + name: 'latest', + hidden: false, + tags: [] + }, + { + name: '1.0.0', + hidden: false, + tags: ['latest'] + } + ] + + const latestVersion = ProjectRepository.getLatestVersion(versions) + expect(latestVersion).toStrictEqual(versions[0]) + }) +})