Skip to content

Commit 291c927

Browse files
committed
Add namespace hiding within the ObjectInput and remove objects prefixed with namespace in ObjectList
1 parent 5451e0b commit 291c927

File tree

5 files changed

+71
-14
lines changed

5 files changed

+71
-14
lines changed

packages/components/src/AuthenticatedClient/AuthenticatedClient.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,6 @@ function AuthenticatedClient(props: UsePelicanClientOptions) {
3434
namespaceName,
3535
} = usePelicanClient(props);
3636

37-
console.log(shortcuts);
38-
3937
return (
4038
<Box mt={6} {...(uploadRef.current?.dragHandlers ?? {})}>
4139
<Box
@@ -55,6 +53,8 @@ function AuthenticatedClient(props: UsePelicanClientOptions) {
5553
setObjectUrl={setObjectUrl}
5654
onChange={handleRefetchObject}
5755
loading={loading}
56+
federation={federationName}
57+
namespace={namespaceName}
5858
/>
5959
<ClientMetadata
6060
federation={federationName}
@@ -82,6 +82,7 @@ function AuthenticatedClient(props: UsePelicanClientOptions) {
8282
loginRequired={loginRequired}
8383
canLogin={true}
8484
onLoginRequest={handleLogin}
85+
namespace={namespaceName}
8586
/>
8687
</Box>
8788
{shortcuts.length > 0 && (

packages/components/src/ObjectInput/ObjectInput.tsx

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
"use client";
22

3-
import { KeyboardDoubleArrowRight } from "@mui/icons-material";
4-
import { Box, InputAdornment, LinearProgress, TextField } from "@mui/material";
3+
import { KeyboardDoubleArrowRight, Visibility, VisibilityOff } from "@mui/icons-material";
4+
import { Box, IconButton, InputAdornment, LinearProgress, TextField } from "@mui/material";
5+
import { useEffect, useRef, useState } from "react";
56
import { useDebounceCallback } from "usehooks-ts";
67

78
interface ObjectInputProps {
@@ -16,25 +17,54 @@ interface ObjectInputProps {
1617
onChange: (url: string) => void;
1718

1819
loading: boolean;
20+
21+
federation?: string | null;
22+
namespace?: string | null;
1923
}
2024

2125
/**
2226
* The ObjectInput component allows users to input an object URL, handles authentication if required,
2327
* and displays a loading indicator during asynchronous operations.
2428
*/
25-
function ObjectInput({ objectUrl, setObjectUrl, onChange, loading }: ObjectInputProps) {
29+
function ObjectInput({ objectUrl, setObjectUrl, onChange, loading, federation, namespace }: ObjectInputProps) {
2630
const debounced = useDebounceCallback(onChange, 300);
31+
const [showPrefix, setShowPrefix] = useState(false);
32+
33+
// keep track of the last prefix to avoid flicker during loading
34+
const lastPrefixRef = useRef<string | null>(null);
35+
36+
// calculate the prefix to hide
37+
const prefix = federation && namespace ? `pelican://${federation}${namespace}` : null;
38+
39+
// remember the last prefix
40+
useEffect(() => {
41+
if (prefix) {
42+
lastPrefixRef.current = prefix;
43+
}
44+
}, [prefix]);
45+
46+
// Use the current prefix if available, otherwise use the last known prefix
47+
const effectivePrefix = prefix || lastPrefixRef.current;
48+
49+
// Determine what to display in the input
50+
const displayValue =
51+
!showPrefix && effectivePrefix && objectUrl.startsWith(effectivePrefix)
52+
? objectUrl.slice(effectivePrefix.length) || "/"
53+
: objectUrl;
2754

2855
return (
2956
<Box display={"flex"} flexDirection={"column"}>
3057
<Box display={"flex"} alignItems={"center"}>
3158
<TextField
3259
fullWidth
3360
onChange={(e) => {
34-
setObjectUrl(e.target.value);
35-
debounced(e.target.value);
61+
// If prefix is hidden, reconstruct the full URL
62+
const newValue =
63+
!showPrefix && effectivePrefix ? effectivePrefix + e.target.value : e.target.value;
64+
setObjectUrl(newValue);
65+
debounced(newValue);
3666
}}
37-
value={objectUrl}
67+
value={displayValue}
3868
id="pelican-url"
3969
placeholder={"Enter Pelican URL ( pelican://<federation>/<namespace>/* )"}
4070
variant="outlined"
@@ -46,6 +76,18 @@ function ObjectInput({ objectUrl, setObjectUrl, onChange, loading }: ObjectInput
4676
<KeyboardDoubleArrowRight />
4777
</InputAdornment>
4878
),
79+
endAdornment: prefix && (
80+
<InputAdornment position="end">
81+
<IconButton
82+
size="small"
83+
onClick={() => setShowPrefix(!showPrefix)}
84+
edge="end"
85+
title={showPrefix ? "Hide prefix" : "Show full URL"}
86+
>
87+
{showPrefix ? <VisibilityOff /> : <Visibility />}
88+
</IconButton>
89+
</InputAdornment>
90+
),
4991
},
5092
}}
5193
/>

packages/components/src/ObjectView/ObjectView.tsx

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ interface ObjectListProps {
2828
loginRequired: boolean;
2929
canLogin: boolean;
3030
onLoginRequest?: () => void;
31+
/** Namespace prefix to strip from display (e.g., /namespace) */
32+
namespace?: string | null;
3133
}
3234

3335
/**
@@ -41,6 +43,7 @@ function ObjectView({
4143
loginRequired,
4244
canLogin,
4345
onLoginRequest,
46+
namespace,
4447
}: ObjectListProps) {
4548
const [sortColumn, setSortColumn] = useState<SortableColumn>("href");
4649
const [sortDirection, setSortDirection] = useState<SortDirection>("asc");
@@ -171,7 +174,7 @@ function ObjectView({
171174
style={{ cursor: "pointer" }}
172175
>
173176
<TableCell sx={{ px: 2, py: 1 }}>
174-
<ObjectName {...obj} />
177+
<ObjectName {...obj} namespace={namespace} />
175178
</TableCell>
176179
<TableCell sx={{ px: 2, py: 1 }}>
177180
{obj.iscollection ? "" : formatBytes(obj.getcontentlength)}
@@ -234,12 +237,20 @@ function ObjectView({
234237
);
235238
}
236239

237-
function ObjectName(object: ObjectList) {
240+
function ObjectName(props: ObjectList & { namespace?: string | null }) {
241+
const { href, iscollection, getlastmodified, namespace } = props;
242+
243+
// Strip namespace from the display name
244+
let displayName = href;
245+
if (namespace && href.startsWith(namespace)) {
246+
displayName = href.slice(namespace.length) || "/";
247+
}
248+
238249
return (
239250
<Box display="flex" alignItems="center" gap={1}>
240-
{object.iscollection ? (
251+
{iscollection ? (
241252
// Check if this is the parent directory (first item with empty getlastmodified)
242-
object.getlastmodified === "" ? (
253+
getlastmodified === "" ? (
243254
<ArrowUpward color="primary" fontSize="small" />
244255
) : (
245256
<Folder color="primary" fontSize="small" />
@@ -248,7 +259,7 @@ function ObjectName(object: ObjectList) {
248259
<InsertDriveFile color="action" fontSize="small" />
249260
)}
250261
{/* Show ".." for parent directory (synthetic entry with empty getlastmodified) */}
251-
{object.iscollection && object.getlastmodified === "" ? ".." : object.href}
262+
{iscollection && getlastmodified === "" ? ".." : displayName}
252263
</Box>
253264
);
254265
}

packages/components/src/PublicClient/PublicClient.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ function PublicClient(props: UsePelicanClientOptions) {
3535
setObjectUrl={setObjectUrl}
3636
onChange={handleRefetchObject}
3737
loading={loading}
38+
federation={federationName}
39+
namespace={namespaceName}
3840
/>
3941
<ClientMetadata
4042
federation={federationName}
@@ -51,6 +53,7 @@ function PublicClient(props: UsePelicanClientOptions) {
5153
onDownload={handleDownload}
5254
canLogin={false}
5355
loginRequired={loginRequired}
56+
namespace={namespaceName}
5457
/>
5558
</Box>
5659
);

packages/components/src/usePelicanClient/usePelicanClient.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ function usePelicanClient({ objectUrl, setObjectUrl, enableAuth = true }: UsePel
275275

276276
handleRefetchObject(objectUrl);
277277
setInitialFetchDone(true);
278-
}, [authExchangeComplete, initialFetchDone, objectUrl, objectUrl, federations, handleRefetchObject]);
278+
}, [authExchangeComplete, initialFetchDone, objectUrl, federations, handleRefetchObject]);
279279

280280
const handleExplore = useCallback(
281281
(href: string) => {

0 commit comments

Comments
 (0)