From 9485ba2f835e8ae994a46332c1db1dc0e6ee8a6a Mon Sep 17 00:00:00 2001 From: Filipe Lopes Date: Thu, 7 May 2026 11:02:44 -0300 Subject: [PATCH 1/2] fix(common): prevent stale changelog responses * Add request tracking to ignore late changelog responses after dialog close or newer request start * Ignore local .env files in version control --- .gitignore | 1 + .../common/ConceptContainerVersionList.jsx | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 167c486c..a3319199 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ src/stories/ dist/ .idea/ .DS_Store +.env diff --git a/src/components/common/ConceptContainerVersionList.jsx b/src/components/common/ConceptContainerVersionList.jsx index 1ea96e96..3c936f14 100644 --- a/src/components/common/ConceptContainerVersionList.jsx +++ b/src/components/common/ConceptContainerVersionList.jsx @@ -118,8 +118,10 @@ const ConceptContainerVersionList = ({ versions, resource, canEdit, onUpdate, fh const [isChangelogFullScreen, setIsChangelogFullScreen] = React.useState(false); const [showChangelogLoadingMessage, setShowChangelogLoadingMessage] = React.useState(false); const changelogLoadingTimer = React.useRef(null); + const changelogRequestId = React.useRef(0); React.useEffect(() => () => { + changelogRequestId.current += 1 if(changelogLoadingTimer.current) clearTimeout(changelogLoadingTimer.current) }, []) @@ -180,6 +182,10 @@ const ConceptContainerVersionList = ({ versions, resource, canEdit, onUpdate, fh if(changelogLoadingTimer.current) clearTimeout(changelogLoadingTimer.current) + const requestId = changelogRequestId.current + 1 + changelogRequestId.current = requestId + const isActiveChangelogRequest = () => requestId === changelogRequestId.current + setChangelogDialog(true) setChangelogVersion(version) setPreviousChangelogVersionURL(previousVersionURL) @@ -202,6 +208,9 @@ const ConceptContainerVersionList = ({ versions, resource, canEdit, onUpdate, fh {inline: true, output: 'markdown', verbosity: 4} ) .then(response => { + if(!isActiveChangelogRequest()) + return + const markdown = get(response, 'data.markdown') || get(response, 'markdown') if(markdown) { @@ -211,8 +220,14 @@ const ConceptContainerVersionList = ({ versions, resource, canEdit, onUpdate, fh setChangelogError(get(response, 'detail') || get(response, 'error') || 'Could not load changelog.') } }) - .catch(() => setChangelogError('Could not load changelog.')) + .catch(() => { + if(isActiveChangelogRequest()) + setChangelogError('Could not load changelog.') + }) .finally(() => { + if(!isActiveChangelogRequest()) + return + if(changelogLoadingTimer.current) clearTimeout(changelogLoadingTimer.current) setIsChangelogLoading(false) @@ -220,6 +235,7 @@ const ConceptContainerVersionList = ({ versions, resource, canEdit, onUpdate, fh } const onChangelogClose = () => { + changelogRequestId.current += 1 if(changelogLoadingTimer.current) clearTimeout(changelogLoadingTimer.current) setChangelogDialog(false) From 0661c68c3e88236978a583808734a6a468837c74 Mon Sep 17 00:00:00 2001 From: Filipe Lopes Date: Thu, 7 May 2026 11:38:48 -0300 Subject: [PATCH 2/2] fix(common): prevent duplicate source changelog links and reset dialog state * Skip changelog generation when normalized previous version URL matches unversioned source URL * Reset loading, markdown, and error state on dialog close to avoid stale UI state --- src/components/common/ConceptContainerVersionList.jsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/components/common/ConceptContainerVersionList.jsx b/src/components/common/ConceptContainerVersionList.jsx index 3c936f14..a0a22688 100644 --- a/src/components/common/ConceptContainerVersionList.jsx +++ b/src/components/common/ConceptContainerVersionList.jsx @@ -100,6 +100,7 @@ const updateVersion = (version, data, verb, successCallback) => getService(versi const getVersionURI = version => get(version, 'uri') || get(version, 'version_url') || get(version, 'url'); const getPreviousVersionURI = version => get(version, 'previous_version_url'); const isHeadVersion = version => (get(version, 'id') || get(version, 'version') || '').toLowerCase() === 'head'; +const normalizeURI = uri => (uri || '').replace(/\/+$/, '/'); const getVersionLabelFromURL = url => { const parts = (url || '').split('/').filter(Boolean) return parts[parts.length - 1] || url @@ -171,7 +172,12 @@ const ConceptContainerVersionList = ({ versions, resource, canEdit, onUpdate, fh if(resource !== 'source' || fhir || isHeadVersion(version)) return null - return getPreviousVersionURI(version) + const previousVersionURL = getPreviousVersionURI(version) + const unversionedSourceURL = get(version, 'url') + if(!previousVersionURL || normalizeURI(previousVersionURL) === normalizeURI(unversionedSourceURL)) + return null + + return previousVersionURL } const onChangelogClick = version => { @@ -240,7 +246,10 @@ const ConceptContainerVersionList = ({ versions, resource, canEdit, onUpdate, fh clearTimeout(changelogLoadingTimer.current) setChangelogDialog(false) setIsChangelogFullScreen(false) + setIsChangelogLoading(false) setShowChangelogLoadingMessage(false) + setChangelogMarkdown('') + setChangelogError('') }