diff --git a/src/shared/organisms/DataPanel/DataPanel.tsx b/src/shared/organisms/DataPanel/DataPanel.tsx index d7457f3af..82cfaf220 100644 --- a/src/shared/organisms/DataPanel/DataPanel.tsx +++ b/src/shared/organisms/DataPanel/DataPanel.tsx @@ -65,6 +65,7 @@ import { } from '../../molecules/MyDataTable/MyDataTable'; import { distributionMatchesTypes, + getPathSeparator, getSizeOfResourcesToDownload, pathForChildDistributions, pathForTopLevelResources, @@ -153,6 +154,7 @@ export async function downloadArchive({ const resourcesForDownload = resourcesPayload.filter( item => item.localStorageType === 'resource' ); + const slash = getPathSeparator(); const resourcesNotFetched: Error[] = []; const { results } = await PromisePool.withConcurrency(4) @@ -174,7 +176,7 @@ export async function downloadArchive({ item, existingPaths ); - const parentPath = `${path}/${filename}.${extension}`; + const parentPath = `${path}${slash}${filename}.${extension}`; // For resources with distribution(s), we want to download both, the metadata as well as all its distribution(s). // To do that, first prepare the metadata for download. topLevelResources.push({ @@ -207,7 +209,7 @@ export async function downloadArchive({ ...item, // @ts-ignore '@type': childResource['@type'] ?? 'File', - path: `${childPath.path}/${childPath.fileName}`, + path: `${childPath.path}${slash}${childPath.fileName}`, _self: childResource._self ?? item._self, resourceId: childResource['@id'], }); diff --git a/src/shared/utils/__tests__/datapanel.spec.ts b/src/shared/utils/__tests__/datapanel.spec.ts index 0fd642105..bf4f7a3ce 100644 --- a/src/shared/utils/__tests__/datapanel.spec.ts +++ b/src/shared/utils/__tests__/datapanel.spec.ts @@ -12,11 +12,19 @@ import { pathForTopLevelResources, toLocalStorageResources, } from '../datapanel'; +import * as deviceMock from 'react-device-detect'; + +jest.mock('react-device-detect', () => ({ + __esModule: true, + isWindows: false, +})); describe('datapanel utilities', () => { const orgName = 'orgA'; const projectName = 'projectA'; + const getDeviceMock = () => (deviceMock as unknown) as { isWindows: boolean }; + it('serializes resources with no distribution correctly to local storage object', () => { const actualLSResources = toLocalStorageResources( resourceWithoutDistrition, @@ -404,4 +412,24 @@ describe('datapanel utilities', () => { expect(actualPathProps).toEqual(expectPathProps); }); + + it('uses backslashes as separator when user has windows device', () => { + getDeviceMock().isWindows = true; + + const parentPath = `\\${orgName}\\${projectName}\\parentPath`; + const filename = 'awesome-file.asc'; + const mockResource = getMockDistribution(filename); + const actualPathProps = pathForChildDistributions( + mockResource, + parentPath, + new Map() + ); + + const expectPathProps = { + path: `${parentPath}\\awesome-file`, + fileName: filename, + }; + + expect(actualPathProps).toEqual(expectPathProps); + }); }); diff --git a/src/shared/utils/datapanel.ts b/src/shared/utils/datapanel.ts index b34063223..29ca1bbd1 100644 --- a/src/shared/utils/datapanel.ts +++ b/src/shared/utils/datapanel.ts @@ -7,6 +7,7 @@ import isValidUrl from '../../utils/validUrl'; import { ResourceObscured } from 'shared/organisms/DataPanel/DataPanel'; import { getResourceLabel, uuidv4 } from '.'; import { parseURL } from './nexusParse'; +import { isWindows } from 'react-device-detect'; const baseLocalStorageObject = ( resource: Resource, @@ -266,13 +267,20 @@ export function pathForTopLevelResources( }; } +export const getPathSeparator = () => { + if (isWindows) { + return '\\'; + } + return '/'; +}; + export function pathForChildDistributions( distItem: any, parentPath: string, existingPaths: Map ) { const defaultUniqueName = uuidv4().substring(0, 10); // TODO use last part of child self or id - + const slash = getPathSeparator(); const fullFileName = fileNameForDistributionItem(distItem, defaultUniqueName); const fileNameWithoutExtension = fullFileName.slice( 0, @@ -281,7 +289,7 @@ export function pathForChildDistributions( const extension = fullFileName.slice(fullFileName.lastIndexOf('.') + 1); const childDir = fileNameWithoutExtension; // Max Length 20 - const pathToChildFile = `${parentPath}/${childDir}`; // Max Length 60 + 1 + 20 = 80 + const pathToChildFile = `${parentPath}${slash}${childDir}`; // Max Length 60 + 1 + 20 = 80 let uniquePath: string; // TODO de-deuplicate if (existingPaths.has(pathToChildFile)) { const count = existingPaths.get(pathToChildFile)!;