@@ -7,22 +7,22 @@ import DocumentControlButtons from '../components/DocumentControlButtons'
77import IFrame from '../components/IFrame'
88import { useLocation , useParams , useSearchParams } from 'react-router-dom'
99import { useMessageBanner } from '../data-providers/MessageBannerProvider'
10- import IframeNotFound from './IframePageNotFound'
1110
1211export default function Docs ( ) : JSX . Element {
1312 const params = useParams ( )
1413 const [ searchParams ] = useSearchParams ( )
1514 const location = useLocation ( )
1615 const { showMessage, clearMessages } = useMessageBanner ( )
1716
18- const [ iframePageNotFound , setIframePageNotFound ] = useState < boolean > ( false )
1917 const [ versions , setVersions ] = useState < ProjectDetails [ ] > ( [ ] )
20- const [ loadingFailed , setLoadingFailed ] = useState < boolean > ( false )
18+ const [ displayVersion , setDisplayVersion ] = useState < ProjectDetails | null > ( null )
19+ const [ projectLoading , setProjectLoading ] = useState < boolean > ( true )
20+ const [ notFound , setNotFound ] = useState < boolean > ( false )
2121
22- const project = useRef ( params . project ?? '' )
2322 const page = useRef ( params [ '*' ] ?? '' )
2423 const hash = useRef ( location . hash )
2524
25+ const [ project , setProject ] = useState < string > ( params . project ?? '' )
2626 const [ version , setVersion ] = useState < string > ( params . version ?? 'latest' )
2727 const [ hideUi , setHideUi ] = useState < boolean > ( searchParams . get ( 'hide-ui' ) === '' || searchParams . get ( 'hide-ui' ) === 'true' )
2828 const [ iframeUpdateTrigger , setIframeUpdateTrigger ] = useState < number > ( 0 )
@@ -33,97 +33,83 @@ export default function Docs(): JSX.Element {
3333 // is not needed when only the page or hash changes, because
3434 // the iframe keeps track of that itself.
3535 const iFrameSrc = useMemo ( ( ) => {
36+ if ( ! displayVersion ) {
37+ return ''
38+ }
3639 return ProjectRepository . getProjectDocsURL (
37- project . current ,
38- version ,
40+ project ,
41+ displayVersion . name ,
3942 page . current ,
4043 hash . current
4144 )
4245 // eslint-disable-next-line react-hooks/exhaustive-deps
43- } , [ version , iframeUpdateTrigger ] )
46+ } , [ project , displayVersion , iframeUpdateTrigger ] )
4447
4548 useEffect ( ( ) => {
46- if ( project . current === '' ) {
47- return
48- }
49-
50- void ( async ( ) : Promise < void > => {
49+ setProjectLoading ( true )
50+ const loadProject = async ( ) => {
5151 try {
52- let allVersions = await ProjectRepository . getVersions ( project . current )
53- if ( allVersions . length === 0 ) {
54- setLoadingFailed ( true )
55- return
56- }
57-
52+ let allVersions = await ProjectRepository . getVersions ( project )
5853 allVersions = allVersions . sort ( ( a , b ) =>
5954 ProjectRepository . compareVersions ( a , b )
6055 )
6156 setVersions ( allVersions )
62-
63- const latestVersion =
64- ProjectRepository . getLatestVersion ( allVersions ) . name
65- if ( version === 'latest' ) {
66- if ( latestVersion === 'latest' ) {
67- return
68- }
69- setVersion ( latestVersion )
70- return
71- }
72-
73- // custom version -> check if it exists
74- // if it does. do nothing, as it should be set already
75- const versionsAndTags = allVersions
76- . map ( ( v ) => [ v . name , ...v . tags ] )
77- . flat ( )
78- if ( versionsAndTags . includes ( version ) ) {
79- return
80- }
81-
82- // version does not exist -> fail
83- setLoadingFailed ( true )
84- console . error ( `Version '${ version } ' doesn't exist` )
8557 } catch ( e ) {
8658 console . error ( e )
87- setLoadingFailed ( true )
8859 }
89- } ) ( )
90- // eslint-disable-next-line react-hooks/exhaustive-deps
91- } , [ project ] )
60+ }
61+ loadProject ( ) . finally ( ( ) => {
62+ setProjectLoading ( false )
63+ } )
64+ } , [ project ] ) ;
9265
93- /** Encodes the url for the current page.
94- * @example
95- * getUrl('project', 'version', 'path/to/page.html', '#hash', false) -> '/project/version/path/to/page.html#hash'
96- */
97- const getUrl = (
98- project : string ,
99- version : string ,
100- page : string ,
101- hash : string ,
102- hideUi : boolean
103- ) : string => {
104- return `/${ project } /${ version } /${ encodeURI ( page ) } ${ hash } ${ hideUi ? '?hide-ui' : '' } `
66+ const buildBrowserUrl = ( project : string , version : string , page : string , hash : string , hideUi : boolean ) : string => {
67+ return `/${ project } /${ version } /${ page } ${ hideUi ? '?hide-ui' : '' } ${ hash } `
10568 }
10669
107- const updateUrl = ( newVersion : string , hideUi : boolean ) : void => {
108- const url = getUrl (
109- project . current ,
110- newVersion ,
111- page . current ,
112- hash . current ,
113- hideUi
114- )
115- window . history . pushState ( null , '' , url )
70+ const getShareUrl = ( options : { useLatest : boolean , hideUi : boolean } ) : string => {
71+ return buildBrowserUrl ( project , options . useLatest ? 'latest' : displayVersion ?. name ?? 'latest' , page . current , hash . current , options . hideUi )
11672 }
11773
118- const updateTitle = ( newTitle : string ) : void => {
119- document . title = newTitle
74+ const updateUrl = ( newProject : string , newVersion : string , hideUi : boolean ) : void => {
75+ window . history . pushState ( null , '' , buildBrowserUrl ( newProject , newVersion , page . current , hash . current , hideUi ) )
12076 }
12177
122- // Keep compatibility with encoded page path
12378 useEffect ( ( ) => {
124- updateUrl ( version , hideUi )
125- // eslint-disable-next-line react-hooks/exhaustive-deps
126- } , [ ] )
79+ if ( versions . length === 0 ) {
80+ return
81+ }
82+
83+ if ( version === 'latest' ) {
84+ const latestVersion = ProjectRepository . getLatestVersion ( versions )
85+ setDisplayVersion ( latestVersion )
86+ } else {
87+ const matchingVersion = versions . find ( ( v ) => v . name === version || v . tags . includes ( version ) )
88+ if ( matchingVersion ) {
89+ setDisplayVersion ( matchingVersion )
90+ } else {
91+ setNotFound ( true )
92+ console . error ( `Version '${ version } ' doesn't exist` )
93+ }
94+ }
95+ } , [ versions , version ] )
96+
97+ useEffect ( ( ) => {
98+ const latestVersion = ProjectRepository . getLatestVersion ( versions )
99+ if ( displayVersion === latestVersion ) {
100+ clearMessages ( )
101+ } else {
102+ showMessage ( {
103+ content : 'You are viewing an outdated version of the documentation.' ,
104+ type : 'warning' ,
105+ showMs : null
106+ } )
107+ }
108+ } , [ displayVersion , versions , showMessage , clearMessages ] )
109+
110+ const updateTitle = ( newTitle : string ) : void => {
111+ document . title = newTitle
112+ }
127113
128114 const iFramePageChanged = ( urlPage : string , urlHash : string , title ?: string ) : void => {
129115 if ( title != null && title !== document . title ) {
@@ -134,19 +120,19 @@ export default function Docs(): JSX.Element {
134120 }
135121 page . current = urlPage
136122 hash . current = urlHash
137- updateUrl ( version , hideUi )
123+ updateUrl ( project , version , hideUi )
138124 }
139125
140126 const iFrameHashChanged = ( newHash : string ) : void => {
141127 if ( newHash === hash . current ) {
142128 return
143129 }
144130 hash . current = newHash
145- updateUrl ( version , hideUi )
131+ updateUrl ( project , version , hideUi )
146132 }
147133
148134 const iFrameNotFound = ( ) : void => {
149- setIframePageNotFound ( true )
135+ setNotFound ( true )
150136 }
151137
152138 const iFrameFaviconChanged = ( faviconUrl : string | null ) : void => {
@@ -157,34 +143,18 @@ export default function Docs(): JSX.Element {
157143 favicon . href = faviconUrl
158144 }
159145
160- const onVersionChanged = ( newVersion : string ) : void => {
161- if ( newVersion === version ) {
162- return
163- }
164- setVersion ( newVersion )
165- updateUrl ( newVersion , hideUi )
166- }
167-
168146 useEffect ( ( ) => {
169147 const urlProject = params . project ?? ''
170148 const urlVersion = params . version ?? 'latest'
171149 const urlPage = params [ '*' ] ?? ''
172150 const urlHash = location . hash
173151 const urlHideUi = searchParams . get ( 'hide-ui' ) === '' || searchParams . get ( 'hide-ui' ) === 'true'
174152
175- // update the state to the url params on first loadon
176- if ( urlProject !== project . current ) {
177- setVersions ( [ ] )
178- project . current = urlProject
179- }
180-
181- if ( urlVersion !== version ) {
182- setVersion ( urlVersion )
183- }
184-
185- if ( urlHideUi !== hideUi ) {
186- setHideUi ( urlHideUi )
187- }
153+ // update the state to the url params on first load
154+ setNotFound ( false )
155+ setProject ( urlProject )
156+ setVersion ( urlVersion )
157+ setHideUi ( urlHideUi )
188158
189159 if ( urlPage !== page . current ) {
190160 page . current = urlPage
@@ -194,48 +164,15 @@ export default function Docs(): JSX.Element {
194164 hash . current = urlHash
195165 setIframeUpdateTrigger ( ( v ) => v + 1 )
196166 }
197-
198- setIframePageNotFound ( false )
199167 // eslint-disable-next-line react-hooks/exhaustive-deps
200168 } , [ location ] )
201169
202- useEffect ( ( ) => {
203- // check every time the version changes whether the version
204- // is the latest version and if not, show a banner
205- if ( versions . length === 0 ) {
206- return
207- }
208-
209- const latestVersion = ProjectRepository . getLatestVersion ( versions ) . name
210- if ( version === latestVersion ) {
211- clearMessages ( )
212- return
213- }
214-
215- showMessage ( {
216- content : 'You are viewing an outdated version of the documentation.' ,
217- type : 'warning' ,
218- showMs : null
219- } )
220- // eslint-disable-next-line react-hooks/exhaustive-deps
221- } , [ version , versions ] )
222-
223- if ( loadingFailed || project . current === '' ) {
224- return < NotFound />
225- }
226-
227- if ( iframePageNotFound ) {
228- return (
229- < IframeNotFound
230- project = { project . current }
231- version = { version }
232- hideUi = { hideUi }
233- />
234- )
170+ if ( projectLoading ) {
171+ return < LoadingPage />
235172 }
236173
237- if ( versions . length === 0 ) {
238- return < LoadingPage />
174+ if ( displayVersion == null || notFound ) {
175+ return < NotFound />
239176 }
240177
241178 return (
@@ -250,9 +187,13 @@ export default function Docs(): JSX.Element {
250187 />
251188 { ! hideUi && (
252189 < DocumentControlButtons
253- version = { version }
190+ version = { displayVersion . name }
254191 versions = { versions }
255- onVersionChange = { onVersionChanged }
192+ onVersionChange = { ( newVersion ) => {
193+ updateUrl ( project , newVersion , hideUi )
194+ setVersion ( newVersion )
195+ } }
196+ getShareUrl = { getShareUrl }
256197 />
257198 ) }
258199 </ >
0 commit comments