@@ -8,8 +8,9 @@ import {useCallback, useEffect, useMemo, useState} from "react";
88import Card from "@mui/material/Card" ;
99import CardContent from "@mui/material/CardContent" ;
1010import Divider from "@mui/material/Divider" ;
11+ import LinearProgress from "@mui/material/LinearProgress" ;
1112import { useSessionStorage } from "usehooks-ts" ;
12- import { Lock } from "@mui/icons-material" ;
13+ import { MenuOpen , Download , Lock } from "@mui/icons-material" ;
1314
1415import {
1516 ObjectList ,
@@ -28,7 +29,8 @@ import {
2829 get ,
2930 put ,
3031 permissions
31- } from "@pelicanplatform/web-client" ;
32+ } from "../../../src/index" ;
33+ import { downloadResponse } from "../../../src/util" ;
3234
3335function Page ( ) {
3436
@@ -79,36 +81,50 @@ function Page() {
7981
8082 // UI State
8183 let [ loginRequired , setLoginRequired ] = useState < boolean > ( false ) ;
82- let [ objectUrl , setObjectUrl ] = useState < string > ( "pelican://osg-htc.org/ncar" ) ;
84+ let [ objectUrl , _setObjectUrl ] = useState < string > ( "pelican://osg-htc.org/ncar" ) ;
8385 let [ permissions , setPermissions ] = useState < TokenPermission [ ] | undefined > ( undefined ) ;
8486 let [ object , setObject ] = useState < File | undefined > ( undefined ) ;
8587 let [ objectList , setObjectList ] = useState < ObjectList [ ] | undefined > ( [ ] ) ;
88+ let [ loading , setLoading ] = useState < boolean > ( false ) ;
89+
90+ let { federationHostname, objectPrefix, objectPath} = useMemo ( ( ) => parseObjectUrl ( objectUrl ) , [ objectUrl ] )
91+
92+ const setObjectUrl = useCallback ( async ( url : string ) => {
93+ setLoading ( true ) ;
94+ _setObjectUrl ( url )
95+ await onObjectUrlChange ( url , federations , setFederations , prefixToNamespace , setPrefixToNamespace , setPermissions , setLoginRequired , setObjectList )
96+ setLoading ( false )
97+ } , [ federations , prefixToNamespace ] )
8698
8799 return (
88100 < Box minHeight = { "90vh" } >
89101 < Grid height = { "100%" } justifyContent = { "center" } container gap = { 2 } >
90102 < Grid size = { { xl : 4 , md : 8 , xs : 11 } } display = { "flex" } flexDirection = { "column" } >
91103 < Box mt = { 6 } mx = { "auto" } width = { "100%" } display = { "flex" } flexDirection = { "column" } >
92104 < Box pt = { 2 } >
93- < TextField fullWidth onChange = { async ( e ) => {
94- setObjectUrl ( e . target . value )
95- await onObjectUrlChange ( e . target . value , federations , setFederations , prefixToNamespace , setPrefixToNamespace , setPermissions , setLoginRequired , setObjectList )
96- } } value = { objectUrl } id = "outlined-basic" label = "Object Name" variant = "outlined" />
97- { loginRequired && codeVerifier && (
98- < IconButton
99- onClick = { async ( ) => {
100- const { federationHostname, objectPrefix} = parseObjectUrl ( objectUrl )
105+ < Box display = { 'flex' } flexDirection = { 'column' } >
106+ < Box display = { 'flex' } alignItems = { 'center' } >
107+ < TextField fullWidth onChange = { async ( e ) => {
108+ await setObjectUrl ( e . target . value )
109+ } } value = { objectUrl } id = "outlined-basic" label = "Object Name" variant = "outlined" />
110+ { loginRequired && codeVerifier && (
111+ < IconButton
112+ onClick = { async ( ) => {
113+ const { federationHostname, objectPrefix} = parseObjectUrl ( objectUrl )
101114
102- const federation = federations [ federationHostname ]
103- const namespaceKey = prefixToNamespace [ objectPrefix ]
104- const namespace = federation . namespaces [ namespaceKey . namespace ]
115+ const federation = federations [ federationHostname ]
116+ const namespaceKey = prefixToNamespace [ objectPrefix ]
117+ const namespace = federation . namespaces [ namespaceKey . namespace ]
105118
106- startAuthorizationCodeFlow ( codeVerifier , namespace , federation )
107- } }
108- >
109- < Lock />
110- </ IconButton >
111- ) }
119+ startAuthorizationCodeFlow ( codeVerifier , namespace , federation )
120+ } }
121+ >
122+ < Lock />
123+ </ IconButton >
124+ ) }
125+ </ Box >
126+ { loading && < LinearProgress /> }
127+ </ Box >
112128 < Typography variant = { 'subtitle2' } >
113129 Namespace Permissions: { permissions ? permissions . join ( ", " ) : "Unknown" }
114130 </ Typography >
@@ -133,7 +149,27 @@ function Page() {
133149 objectList ?. map ( ( obj , index ) => (
134150 < Card key = { index } sx = { { mb : 2 } } variant = "outlined" >
135151 < CardContent >
136- < Typography variant = "h6" gutterBottom > { obj . href } </ Typography >
152+ < Box display = { "flex" } justifyContent = { "space-between" } alignItems = { "center" } >
153+ < Typography variant = "h6" gutterBottom > { obj . href } </ Typography >
154+ { obj . iscollection &&
155+ < Button endIcon = { < MenuOpen /> } onClick = { ( ) => setObjectUrl ( `pelican://${ federationHostname } ${ obj . href } /` ) } >
156+ Explore
157+ </ Button >
158+ }
159+ { ! obj . iscollection &&
160+ < Button
161+ endIcon = { < Download /> }
162+ onClick = { async ( ) => {
163+ try {
164+ downloadResponse ( await get ( `pelican://${ federationHostname } ${ obj . href } ` , federations [ federationHostname ] , federations [ federationHostname ] . namespaces ?. [ prefixToNamespace [ objectPrefix ] ?. namespace ] ) )
165+ } catch { }
166+ } }
167+ >
168+ Download
169+ </ Button >
170+ }
171+ </ Box >
172+
137173 < Divider sx = { { my : 1 } } />
138174 < Typography
139175 variant = "body2" > < strong > Type:</ strong > { obj . resourcetype } { obj . iscollection ? " (Collection)" : "" }
@@ -230,23 +266,13 @@ const onObjectUrlChange = async (objectUrl: string, federations: Record<string,
230266 }
231267 } catch { }
232268
233-
234- // Check permissions
235- try {
236- if ( federations [ federationHostname ] . namespaces ?. [ prefixToNamespace [ objectPrefix ] ?. namespace ] ) {
237- const perms = await permissions ( objectUrl , federations [ federationHostname ] . namespaces ?. [ prefixToNamespace [ objectPrefix ] ?. namespace ] )
238- setPermissions ( perms )
239- }
240- } catch { }
241-
242-
243269 // Try to list
244270 try {
245271 try {
246- setObjectList ( await list ( `pelican://${ objectPrefix } ` , federations [ federationHostname ] , federations [ federationHostname ] . namespaces ?. [ prefixToNamespace [ objectPrefix ] ?. namespace ] ) )
272+ setObjectList ( ( await list ( `pelican://${ objectPrefix } ` , federations [ federationHostname ] , federations [ federationHostname ] . namespaces ?. [ prefixToNamespace [ objectPrefix ] ?. namespace ] ) ) . reverse ( ) )
247273 setLoginRequired ( false )
248274 } catch ( e ) {
249- setObjectList ( await list ( `pelican://${ federationHostname } ${ objectPath } ` , federations [ federationHostname ] , federations [ federationHostname ] . namespaces ?. [ prefixToNamespace [ objectPrefix ] ?. namespace ] ) )
275+ setObjectList ( ( await list ( `pelican://${ federationHostname } ${ objectPath } ` , federations [ federationHostname ] , federations [ federationHostname ] . namespaces ?. [ prefixToNamespace [ objectPrefix ] ?. namespace ] ) ) . reverse ( ) )
250276 setLoginRequired ( false )
251277 }
252278 } catch ( e ) {
@@ -255,6 +281,14 @@ const onObjectUrlChange = async (objectUrl: string, federations: Record<string,
255281 setObjectList ( [ ] )
256282 }
257283 }
284+
285+ // Check permissions
286+ try {
287+ if ( federations [ federationHostname ] . namespaces ?. [ prefixToNamespace [ objectPrefix ] ?. namespace ] ) {
288+ const perms = await permissions ( objectUrl , federations [ federationHostname ] . namespaces ?. [ prefixToNamespace [ objectPrefix ] ?. namespace ] )
289+ setPermissions ( perms )
290+ }
291+ } catch { }
258292}
259293
260294export default Page ;
0 commit comments