Skip to content
This repository has been archived by the owner on Aug 21, 2024. It is now read-only.

feat(projects): move project download button to file browser panel #8100

Merged
merged 9 commits into from
Jun 21, 2023
1 change: 1 addition & 0 deletions packages/client-core/i18n/en/admin.json
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@
"project": {
"addProject": "Add Project",
"updateProject": "Update Project",
"downloadProject": "Download Project",
"rebuild": "Rebuild",
"rebuilding": "Rebuilding",
"updateEngine": "Update Engine",
Expand Down
2 changes: 0 additions & 2 deletions packages/client-core/src/admin/common/variables/projects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ export interface ProjectColumn {
| 'projectVersion'
| 'commitSHA'
| 'commitDate'
| 'download'
| 'update'
| 'invalidate'
| 'view'
Expand All @@ -47,7 +46,6 @@ export const projectsColumns: ProjectColumn[] = [
{ id: 'projectVersion', label: 'Version', minWidth: 65 },
{ id: 'commitSHA', label: 'Commit SHA', minWidth: 100 },
{ id: 'commitDate', label: 'Commit Date', minWidth: 100 },
{ id: 'download', label: 'Download', minWidth: 65 },
{ id: 'update', label: 'Update', minWidth: 65, align: 'center' },
{ id: 'push', label: 'Push to GitHub', minWidth: 65, align: 'center' },
{ id: 'link', label: 'GitHub Repo Link', minWidth: 65, align: 'center' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,8 @@ Ethereal Engine. All Rights Reserved.
import _ from 'lodash'
import React, { useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { saveAs } from 'save-as'

import ConfirmDialog from '@etherealengine/client-core/src/common/components/ConfirmDialog'
import config from '@etherealengine/common/src/config'
import { ProjectInterface } from '@etherealengine/common/src/interfaces/ProjectInterface'
import multiLogger from '@etherealengine/common/src/logger'
import { getMutableState, useHookstate } from '@etherealengine/hyperflux'
Expand All @@ -38,7 +36,6 @@ import Icon from '@etherealengine/ui/src/primitives/mui/Icon'
import IconButton from '@etherealengine/ui/src/primitives/mui/IconButton'
import Tooltip from '@etherealengine/ui/src/primitives/mui/Tooltip'

import { API } from '../../../API'
import { NotificationService } from '../../../common/services/NotificationService'
import { PROJECT_PAGE_LIMIT, ProjectService, ProjectState } from '../../../common/services/ProjectService'
import { AuthState } from '../../../user/services/AuthService'
Expand Down Expand Up @@ -157,15 +154,6 @@ const ProjectTable = ({ className }: Props) => {
})
}

const DownloadProject = async (row: ProjectInterface) => {
setProject(row)
const url = `/projects/${row.name}`

const data = await API.instance.client.service('archiver').get(url)
const blob = await (await fetch(`${config.client.fileServer}/${data}`)).blob()
saveAs(blob, row.name + '.zip')
}

const openInvalidateConfirmation = (row) => {
setProject(row)

Expand Down Expand Up @@ -314,19 +302,6 @@ const ProjectTable = ({ className }: Props) => {
)}
</>
),
download: (
<>
{isAdmin && (
<IconButton
className={styles.iconButton}
name="download"
disabled={!el.repositoryPath}
onClick={() => DownloadProject(el)}
icon={<Icon type="Download" />}
/>
)}
</>
),
link: (
<>
<IconButton
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ Ethereal Engine. All Rights Reserved.
import { Downgraded } from '@hookstate/core'
import React, { useEffect, useRef } from 'react'
import { useTranslation } from 'react-i18next'
import { saveAs } from 'save-as'

import { API } from '@etherealengine/client-core/src/API'
import ConfirmDialog from '@etherealengine/client-core/src/common/components/ConfirmDialog'
import LoadingView from '@etherealengine/client-core/src/common/components/LoadingView'
import {
Expand All @@ -35,7 +37,9 @@ import {
FileBrowserState,
FILES_PAGE_LIMIT
} from '@etherealengine/client-core/src/common/services/FileBrowserService'
import { NotificationService } from '@etherealengine/client-core/src/common/services/NotificationService'
import { uploadToFeathersService } from '@etherealengine/client-core/src/util/upload'
import config from '@etherealengine/common/src/config'
import { processFileName } from '@etherealengine/common/src/utils/processFileName'
import { KTX2EncodeArguments } from '@etherealengine/engine/src/assets/constants/CompressionParms'
import { KTX2EncodeDefaultArguments } from '@etherealengine/engine/src/assets/constants/CompressionParms'
Expand All @@ -50,6 +54,7 @@ import { addActionReceptor, removeActionReceptor } from '@etherealengine/hyperfl

import ArrowBackIcon from '@mui/icons-material/ArrowBack'
import AutorenewIcon from '@mui/icons-material/Autorenew'
import DownloadIcon from '@mui/icons-material/Download'
import Breadcrumbs from '@mui/material/Breadcrumbs'
import Dialog from '@mui/material/Dialog'
import DialogTitle from '@mui/material/DialogTitle'
Expand Down Expand Up @@ -374,6 +379,32 @@ const FileBrowserContentPanel: React.FC<FileBrowserContentPanelProps> = (props)
selectedDirectory.set(newPath)
}

const handleDownloadProject = async () => {
const url = selectedDirectory.value
const data = await API.instance.client
.service('archiver')
.get(url)
.catch((err: Error) => {
NotificationService.dispatchNotify(err.message, { variant: 'warning' })
return null
})
if (!data) return
const blob = await (await fetch(`${config.client.fileServer}/${data}`)).blob()

let fileName = 'download' // default name
if (selectedDirectory.value[selectedDirectory.value.length - 1] === '/') {
fileName = selectedDirectory.value.split('/').at(-2) as string
} else {
fileName = selectedDirectory.value.split('/').at(-1) as string
}

saveAs(blob, fileName + '.zip')
}

const showDownloadButton =
selectedDirectory.value.slice(1).startsWith('projects/') &&
!['projects', 'projects/'].includes(selectedDirectory.value.slice(1))

return (
<div className={styles.fileBrowserRoot}>
<div style={headGrid}>
Expand All @@ -391,12 +422,22 @@ const FileBrowserContentPanel: React.FC<FileBrowserContentPanelProps> = (props)
>
{breadcrumbs}
</Breadcrumbs>
<ToolButton
tooltip={t('editor:layout.filebrowser.refresh')}
icon={AutorenewIcon}
onClick={onRefreshDirectory}
id="refreshDir"
/>
<span>
<ToolButton
tooltip={t('editor:layout.filebrowser.refresh')}
icon={AutorenewIcon}
onClick={onRefreshDirectory}
id="refreshDir"
/>
{showDownloadButton && (
<ToolButton
tooltip={t('admin:components.project.downloadProject')}
onClick={handleDownloadProject}
icon={DownloadIcon}
id="downloadProject"
/>
)}
</span>
</div>

{retrieving && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,18 @@ export class Archiver implements Partial<ServiceMethods<any>> {
async setup(app: Application, path: string) {}

async get(directory: string, params?: UserParams): Promise<string> {
if (directory[0] === '/') directory = directory.slice(1)
if (!directory.startsWith('projects/') || ['projects', 'projects/'].includes(directory)) {
return Promise.reject(new Error('Cannot archive non-project directories'))
}

if (!params) params = {}
if (!params.query) params.query = {}
const { storageProviderName } = params.query

delete params.query.storageProviderName

const storageProvider = getStorageProvider(storageProviderName)
if (directory[0] === '/') directory = directory.slice(1)

let result = await storageProvider.listFolderContent(directory)

Expand Down