Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

task/WG-402: Asset feature image download; Various Fixes #331

Merged
merged 11 commits into from
Feb 18, 2025
7 changes: 5 additions & 2 deletions react/src/AppRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import Callback from '@hazmapper/pages/Callback/Callback';
import StreetviewCallback from '@hazmapper/pages/StreetviewCallback/StreetviewCallback';
import { RootState } from '@hazmapper/redux/store';
import { isTokenValid } from '@hazmapper/utils/authUtils';
import { getBasePath } from './hooks';

interface ProtectedRouteProps {
children: ReactElement;
Expand All @@ -29,6 +30,8 @@ const ProtectedRoute: React.FC<ProtectedRouteProps> = ({ children }) => {
return children;
};

const basePath = getBasePath();

export const appRouter = createBrowserRouter(
[
{
Expand Down Expand Up @@ -73,10 +76,10 @@ export const appRouter = createBrowserRouter(
},
{
path: '*',
element: <Navigate to={'/'} replace={true} />,
element: <Navigate to={basePath} replace={true} />,
},
],
},
],
{ basename: '/' }
{ basename: basePath }
);
15 changes: 11 additions & 4 deletions react/src/components/AssetDetail/AssetButton.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useState } from 'react';
import DOMPurify from 'dompurify';
import { Button } from '@tacc/core-components';
import { Button } from 'antd';
import { Feature, FeatureType } from '@hazmapper/types';
import {
getFeatureType,
Expand Down Expand Up @@ -59,7 +59,15 @@ const AssetButton: React.FC<AssetButtonProps> = ({
return (
<>
{featureType === FeatureType.Image && (
<Button /*TODO add Download*/ type="primary">Download</Button>
<Button
type="primary"
href={featureSource}
download={`feature-${selectedFeature.id}.jpeg`}
target="_blank"
rel="noopener noreferrer"
>
Download
</Button>
)}
{featureType === FeatureType.PointCloud && (
<a href={pointCloudURL} target="_blank" rel="noreferrer">
Expand All @@ -72,11 +80,10 @@ const AssetButton: React.FC<AssetButtonProps> = ({
</Button>
)}
{featureType.includes(selectedFeature.geometry.type) && !isPublicView && (
//TODO
<Button
type="primary"
onClick={() => setIsModalOpen(true)}
isLoading={isImporting}
loading={isImporting}
disabled={isImporting}
>
Add Asset from DesignSafe
Expand Down
2 changes: 1 addition & 1 deletion react/src/components/AssetsPanel/AssetsPanel.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
}

.middleSection {
flex: 0 1 auto;
flex: 1 1 auto;
overflow: auto;
min-height: 10rem;
display: flex;
Expand Down
58 changes: 35 additions & 23 deletions react/src/components/AssetsPanel/AssetsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,34 +29,41 @@ const DownloadFeaturesButton: React.FC<DownloadFeaturesButtonProps> = ({
isPublicView,
disabled,
}) => {
const { isLoading: isDownloading, refetch: triggerDownload } = useFeatures({
const {
data,
isLoading: isDownloading,
refetch,
} = useFeatures({
projectId: project.id,
isPublicView: isPublicView,
assetTypes: [], // Empty array to get all features
options: {
enabled: false, // Only fetch when triggered by user clicking button
gcTime: 0,
staleTime: 0,
onSuccess: (data: FeatureCollection) => {
// Create and trigger download
const blob = new Blob([JSON.stringify(data)], {
type: 'application/json',
});

const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = getFilename(project.name);

document.body.appendChild(link);
link.click();

document.body.removeChild(link);
window.URL.revokeObjectURL(url);
},
},
});

const triggerDownload = () => {
refetch();

// Create and trigger download
const blob = new Blob([JSON.stringify(data)], {
type: 'application/json',
});

const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = getFilename(project.name);

document.body.appendChild(link);
link.click();

document.body.removeChild(link);
window.URL.revokeObjectURL(url);
};

return (
<Button
loading={isDownloading}
Expand Down Expand Up @@ -123,11 +130,16 @@ const AssetsPanel: React.FC<Props> = ({
return (
<>
<Flex vertical className={styles.root}>
<Header className={styles.topSection}>
<Button onClick={() => setIsModalOpen(true)} icon={<PlusOutlined />}>
Import from DesignSafe
</Button>
</Header>
{!isPublicView && (
<Header className={styles.topSection}>
<Button
onClick={() => setIsModalOpen(true)}
icon={<PlusOutlined />}
>
Import from DesignSafe
</Button>
</Header>
)}
<Content className={styles.middleSection}>
<FeatureFileTree
projectId={project.id}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ describe('CreateMapModal', () => {
await waitFor(() => {
expect(
screen.getByText(
'Only letters, numbers, hyphens, and underscores are allowed'
'Only letters, numbers, hyphens, underscores, and periods are allowed'
)
).toBeTruthy();
});
Expand Down
7 changes: 5 additions & 2 deletions react/src/components/CreateMapModal/CreateMapModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@
systemFile: z
.string()
.regex(
/^[A-Za-z0-9-_]+$/,
'Only letters, numbers, hyphens, and underscores are allowed'
/^[A-Za-z0-9-_.]+$/,
'Only letters, numbers, hyphens, underscores, and periods are allowed'
)
.nonempty('File name is required'),
});
Expand All @@ -59,6 +59,7 @@
reset,
watch,
setValue,
trigger,
getValues,
} = methods;

Expand All @@ -84,14 +85,16 @@
const systemFile = getValues('systemFile');
if (systemFile === oldName && systemFile !== systemFilename) {
setValue('systemFile', systemFilename);
trigger('systemFile');
oldSystemFilename.current = systemFilename;
}
}, [getValues, mapName, setValue]);

Check warning on line 91 in react/src/components/CreateMapModal/CreateMapModal.tsx

View workflow job for this annotation

GitHub Actions / React-Linting

React Hook useEffect has a missing dependency: 'trigger'. Either include it or remove the dependency array

const handleClose = () => {
setErrorMessage('');
closeModal();
reset();
oldSystemFilename.current = '';
};

const handleDirectoryChange = (directory: string) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.root {
width: 100%;
flex: 1;
overflow: visible;

:global(.ant-tree .ant-tree-treenode) {
Expand Down
47 changes: 25 additions & 22 deletions react/src/components/LayersPanel/LayersPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -266,28 +266,31 @@ const LayersPanel: React.FC<{
)}
</Flex>
<Flex align="center">
<Button
type="text"
icon={
editLayerField[layerName] ? (
<CheckOutlined />
) : (
<EditFilled />
)
}
title="Rename Layer"
size="small"
disabled={
editLayerField[layerName] &&
getFieldState(layerName).invalid
}
onClick={() =>
setEditLayerField({
...editLayerField,
[layerName]: !editLayerField[layerName],
})
}
/>
{!isPublicView && (
<Button
type="text"
icon={
editLayerField[layerName] ? (
<CheckOutlined />
) : (
<EditFilled />
)
}
title="Rename Layer"
size="small"
disabled={
editLayerField[layerName] &&
getFieldState(layerName).invalid
}
onClick={() =>
setEditLayerField({
...editLayerField,
[layerName]:
!editLayerField[layerName],
})
}
/>
)}
<FormItem
control={control}
name={`tileLayers.${index}.layer.uiOptions.showDescription`}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
display: flex;
overflow: hidden;
gap: 10px; /* space between the map name and DS project name */
align-items: center;
}

.coordinatesDisplay {
Expand Down
31 changes: 18 additions & 13 deletions react/src/components/MapControlBar/MapControlbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { useNavigate } from 'react-router-dom';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faArrowLeft, faLock } from '@fortawesome/free-solid-svg-icons';

import { Button, LoadingSpinner } from '@tacc/core-components';
import { LoadingSpinner } from '@tacc/core-components';

import { Typography } from 'antd';
import { Typography, Button, Tooltip } from 'antd';

const { Text } = Typography;

Expand Down Expand Up @@ -116,17 +116,22 @@ const MapControlbar: React.FC<Props> = ({ activeProject, isPublicView }) => {
</Text>
)}
{canSwitchToPrivateMap && (
// TODO_REACT: Add tooltip "View private map" to this button
<Button
onClick={() => {
const { pathname, search } = window.location;
const newPath = pathname.replace('/project-public/', '/project/');
navigate(`${newPath}${search}`);
}}
type="link"
>
<FontAwesomeIcon icon={faLock} />
</Button>
<Tooltip title="View private map">
<Button
onClick={() => {
const { pathname, search } = window.location;
const newPath = pathname.replace(
'/project-public/',
'/project/'
);
navigate(`${newPath}${search}`);
}}
type="link"
title="View private map"
>
<FontAwesomeIcon icon={faLock} />
</Button>
</Tooltip>
)}
{isFeaturesLoading && (
<div className={styles.loadingData}>
Expand Down
24 changes: 24 additions & 0 deletions react/src/hooks/environment/getBasePath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* Computes the base path for the application based on the current URL.
*/
const getBasePath = (): string => {
// note that path order matters
// as we use startsWith to find a match
const paths: string[] = [
'/hazmapper-react',
'/staging-react',
'/dev-react',
'/exp-react',
'/hazmapper',
'/staging',
'/dev',
'/exp',
];
const currentPath: string = window.location.pathname;
const base: string | undefined = paths.find((path) =>
currentPath.startsWith(path)
);
return base || '/';
};

export default getBasePath;
2 changes: 1 addition & 1 deletion react/src/hooks/environment/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { default as useBasePath } from './useBasePath';
export { default as getBasePath } from './getBasePath';
export { default as useAppConfiguration } from './useAppConfiguration';
4 changes: 2 additions & 2 deletions react/src/hooks/environment/useAppConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
DesignSafePortalEnvironment,
} from '@hazmapper/types';
import { getGeoapiUrl, getDesignsafePortalUrl } from './utils';
import useBasePath from './useBasePath';
import getBasePath from './getBasePath';
import { getLocalAppConfiguration } from './getLocalAppConfiguration';

/**
Expand All @@ -20,7 +20,7 @@ import { getLocalAppConfiguration } from './getLocalAppConfiguration';
* - Production (hazmapper.tacc.utexas.edu without path prefix)
*/
export const useAppConfiguration = (): AppConfiguration => {
const basePath = useBasePath();
const basePath = getBasePath();

const appConfiguration = useMemo(() => {
const hostname = window && window.location && window.location.hostname;
Expand Down
30 changes: 0 additions & 30 deletions react/src/hooks/environment/useBasePath.ts

This file was deleted.

Loading