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

file browser configurable top limit #9237

Merged
merged 10 commits into from
Nov 20, 2023
41 changes: 26 additions & 15 deletions packages/client-core/src/common/services/FileBrowserService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { defineState, getMutableState } from '@etherealengine/hyperflux'

import { Engine } from '@etherealengine/engine/src/ecs/classes/Engine'
import { FileBrowserContentType, fileBrowserPath } from '@etherealengine/engine/src/schemas/media/file-browser.schema'
import { NotificationService } from './NotificationService'

export const FILES_PAGE_LIMIT = 100

Expand All @@ -55,21 +56,26 @@ export const FileBrowserService = {

fileBrowserState.retrieving.set(true)

const files = (await Engine.instance.api.service(fileBrowserPath).find({
query: {
$skip: skip * FILES_PAGE_LIMIT,
$limit: FILES_PAGE_LIMIT,
directory
}
})) as Paginated<FileBrowserContentType>
fileBrowserState.merge({
files: files.data,
skip: files.skip,
total: files.total,
retrieving: false,
fetched: true,
lastFetched: Date.now()
})
try {
const files = (await Engine.instance.api.service(fileBrowserPath).find({
query: {
$skip: skip * FILES_PAGE_LIMIT,
$limit: FILES_PAGE_LIMIT,
directory
}
})) as Paginated<FileBrowserContentType>
fileBrowserState.merge({
files: files.data,
skip: files.skip,
total: files.total,
retrieving: false,
fetched: true,
lastFetched: Date.now()
})
} catch (err) {
NotificationService.dispatchNotify((err as Error)?.message, { variant: 'error' })
fileBrowserState.retrieving.set(false)
}
},
moveContent: async (oldName: string, newName: string, oldPath: string, newPath: string, isCopy = false) => {
return Engine.instance.api.service(fileBrowserPath).update(null, { oldName, newName, oldPath, newPath, isCopy })
Expand All @@ -83,5 +89,10 @@ export const FileBrowserService = {
resetSkip: () => {
const fileBrowserState = getMutableState(FileBrowserState)
fileBrowserState.skip.set(0)
},
getNestingDirectory: () => {
return Engine.instance.api
.service(fileBrowserPath)
.get('', { query: { getNestingDirectory: true } }) as Promise<string>
}
}
42 changes: 24 additions & 18 deletions packages/editor/src/components/assets/AssetsPreviewPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ Original Code is the Ethereal Engine team.
All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023
Ethereal Engine. All Rights Reserved.
*/
import React, { useImperativeHandle, useState } from 'react'
import React, { useImperativeHandle } from 'react'

import { AssetLoader } from '@etherealengine/engine/src/assets/classes/AssetLoader'
import { AssetType } from '@etherealengine/engine/src/assets/enum/AssetType'
import createReadableTexture from '@etherealengine/engine/src/assets/functions/createReadableTexture'
import { useHookstate } from '@etherealengine/hyperflux'
import { NO_PROXY, useHookstate } from '@etherealengine/hyperflux'

import { AudioPreviewPanel } from './AssetPreviewPanels/AudioPreviewPanel'
import { ImagePreviewPanel } from './AssetPreviewPanels/ImagePreviewPanel'
Expand All @@ -49,20 +49,23 @@ interface Props {
hideHeading?: boolean
}

export type AssetSelectionChangePropsType = {
type ResourceProps = {
resourceUrl: string
name: string
contentType: string
size: string | undefined
}

export type AssetSelectionChangePropsType = ResourceProps & {
contentType: string
}

/**
* Used to see the Preview of the Asset in the FileBrowser Panel
*/
export const AssetsPreviewPanel = React.forwardRef(({ hideHeading }: Props, ref) => {
useImperativeHandle(ref, () => ({ onSelectionChanged }))
const [previewPanel, usePreviewPanel] = useState({
PreviewSource: null as any,
const previewPanel = useHookstate({
PreviewSource: null as ((props: { resourceProps: ResourceProps }) => JSX.Element) | null,
resourceProps: { resourceUrl: '', name: '', size: '' }
})

Expand Down Expand Up @@ -94,7 +97,7 @@ export const AssetsPreviewPanel = React.forwardRef(({ hideHeading }: Props, ref)
PreviewSource: ModelPreviewPanel,
resourceProps: { resourceUrl: props.resourceUrl, name: props.name, size: props.size }
}
usePreviewPanel(modelPreviewPanel)
previewPanel.set(modelPreviewPanel)
break
case 'image/png':
case 'image/jpeg':
Expand All @@ -105,15 +108,15 @@ export const AssetsPreviewPanel = React.forwardRef(({ hideHeading }: Props, ref)
PreviewSource: ImagePreviewPanel,
resourceProps: { resourceUrl: props.resourceUrl, name: props.name, size: props.size }
}
usePreviewPanel(imagePreviewPanel)
previewPanel.set(imagePreviewPanel)
break
case 'ktx2':
case 'image/ktx2':
const compImgPreviewPanel = {
PreviewSource: ImagePreviewPanel,
resourceProps: { resourceUrl: thumbnail.value, name: props.name, size: props.size }
}
usePreviewPanel(compImgPreviewPanel)
previewPanel.set(compImgPreviewPanel)
break

case 'video/mp4':
Expand All @@ -123,7 +126,7 @@ export const AssetsPreviewPanel = React.forwardRef(({ hideHeading }: Props, ref)
PreviewSource: VideoPreviewPanel,
resourceProps: { resourceUrl: props.resourceUrl, name: props.name, size: props.size }
}
usePreviewPanel(videoPreviewPanel)
previewPanel.set(videoPreviewPanel)
break
case 'audio/mpeg':
case 'mpeg':
Expand All @@ -132,44 +135,47 @@ export const AssetsPreviewPanel = React.forwardRef(({ hideHeading }: Props, ref)
PreviewSource: AudioPreviewPanel,
resourceProps: { resourceUrl: props.resourceUrl, name: props.name, size: props.size }
}
usePreviewPanel(audioPreviewPanel)
previewPanel.set(audioPreviewPanel)
break
case 'md':
case 'ts':
case 'js':
const txtPreviewPanel = {
PreviewSource: TxtPreviewPanel,
resourceProps: { resourceUrl: props.resourceUrl, name: props.name, size: props.size }
}
usePreviewPanel(txtPreviewPanel)
previewPanel.set(txtPreviewPanel)
break
case 'json':
const jsonPreviewPanel = {
PreviewSource: JsonPreviewPanel,
resourceProps: { resourceUrl: props.resourceUrl, name: props.name, size: props.size }
}
usePreviewPanel(jsonPreviewPanel)
previewPanel.set(jsonPreviewPanel)
break

default:
const unavailable = {
PreviewSource: PreviewUnavailable,
resourceProps: { resourceUrl: props.resourceUrl, name: props.name, size: props.size }
}
usePreviewPanel(unavailable)
previewPanel.set(unavailable)
break
}
}

const PreviewSource = previewPanel.get(NO_PROXY).PreviewSource

return (
<>
{!hideHeading && (
<div style={assetHeadingStyles as React.CSSProperties}>
{previewPanel.resourceProps.name &&
previewPanel.resourceProps.size &&
`${previewPanel.resourceProps.name} (${previewPanel.resourceProps.size})`}
{previewPanel.resourceProps.name.value &&
previewPanel.resourceProps.size.value &&
`${previewPanel.resourceProps.name.value} (${previewPanel.resourceProps.size.value})`}
</div>
)}
{previewPanel.PreviewSource && <previewPanel.PreviewSource resourceProps={previewPanel.resourceProps} />}
{PreviewSource && <PreviewSource resourceProps={previewPanel.resourceProps.value} />}
</>
)
})
105 changes: 60 additions & 45 deletions packages/editor/src/components/assets/FileBrowserContentPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ import {
ImageConvertDefaultParms,
ImageConvertParms
} from '@etherealengine/engine/src/assets/constants/ImageConvertParms'
import { getMutableState, NO_PROXY, useHookstate, useState } from '@etherealengine/hyperflux'
import { getMutableState, NO_PROXY, useHookstate } from '@etherealengine/hyperflux'

import AccessibilityNewIcon from '@mui/icons-material/AccessibilityNew'
import AddIcon from '@mui/icons-material/Add'
Expand Down Expand Up @@ -150,19 +150,19 @@ const FileBrowserContentPanel: React.FC<FileBrowserContentPanelProps> = (props)
const anchorEl = useHookstate<null | HTMLElement>(null)
const anchorPosition = useHookstate<undefined | PopoverPosition>(undefined)

const isLoading = useState(true)
const selectedDirectory = useState(
`/${props.folderName || 'projects'}/${props.selectedFile ? props.selectedFile + '/' : ''}`
)
const fileProperties = useState<any>(null)
const originalPath = `/${props.folderName || 'projects'}/${props.selectedFile ? props.selectedFile + '/' : ''}`
const selectedDirectory = useHookstate(originalPath)
const nestingDirectory = useHookstate('projects')
const fileProperties = useHookstate<FileType | null>(null)
const isLoading = useHookstate(true)

const openProperties = useState(false)
const openCompress = useState(false)
const openConvert = useState(false)
const convertProperties = useState<ImageConvertParms>(ImageConvertDefaultParms)
const openProperties = useHookstate(false)
const openCompress = useHookstate(false)
const openConvert = useHookstate(false)
const convertProperties = useHookstate<ImageConvertParms>(ImageConvertDefaultParms)

const openConfirm = useState(false)
const contentToDeletePath = useState('')
const openConfirm = useHookstate(false)
const contentToDeletePath = useHookstate('')

const fileState = useHookstate(getMutableState(FileBrowserState))
const filesValue = fileState.files.attach(Downgraded).value
Expand Down Expand Up @@ -191,7 +191,11 @@ const FileBrowserContentPanel: React.FC<FileBrowserContentPanelProps> = (props)

useEffect(() => {
refreshDirectory()
}, [selectedDirectory.value])
}, [selectedDirectory])

useEffect(() => {
FileBrowserService.getNestingDirectory().then((directory) => nestingDirectory.set(directory))
}, [])

const refreshDirectory = async () => {
await FileBrowserService.fetchFiles(selectedDirectory.value, page)
Expand Down Expand Up @@ -267,7 +271,7 @@ const FileBrowserContentPanel: React.FC<FileBrowserContentPanelProps> = (props)
const onBackDirectory = () => {
const pattern = /([^/]+)/g
const result = selectedDirectory.value.match(pattern)
if (!result) return
if (!result || result.length === 1) return
let newPath = '/'
for (let i = 0; i < result.length - 1; i++) {
newPath += result[i] + '/'
Expand Down Expand Up @@ -314,6 +318,7 @@ const FileBrowserContentPanel: React.FC<FileBrowserContentPanelProps> = (props)
const showUploadAndDownloadButtons =
selectedDirectory.value.slice(1).startsWith('projects/') &&
!['projects', 'projects/'].includes(selectedDirectory.value.slice(1))
const showBackButton = selectedDirectory.value !== originalPath

const handleDownloadProject = async () => {
const url = selectedDirectory.value
Expand Down Expand Up @@ -352,33 +357,41 @@ const FileBrowserContentPanel: React.FC<FileBrowserContentPanelProps> = (props)
changeDirectoryByPath(newPath)
}

let nestingDirectoryFiles = nestingDirectory.value.split('/')
let breadcrumbDirectoryFiles = selectedDirectory.value
.slice(1, -1)
.split('/')
.filter((file, idx) => {
if (idx < nestingDirectoryFiles.length && file === nestingDirectoryFiles[idx]) {
return false
}
return true
})

return (
<Breadcrumbs
style={{}}
maxItems={3}
classes={{ separator: styles.separator, li: styles.breadcrumb, ol: styles.breadcrumbList }}
separator="›"
>
{selectedDirectory.value
.slice(1, -1)
.split('/')
.map((file, index, arr) =>
arr.length - 1 == index ? (
<Typography key={file} style={{ fontSize: '0.9rem' }}>
{file}
</Typography>
) : (
<Link
underline="hover"
key={file}
color="#5d646c"
style={{ fontSize: '0.9rem' }}
onClick={() => handleBreadcrumbDirectoryClick(file)}
>
{file}
</Link>
)
)}
{breadcrumbDirectoryFiles.map((file, index, arr) =>
arr.length - 1 == index ? (
<Typography key={file} style={{ fontSize: '0.9rem' }}>
{file}
</Typography>
) : (
<Link
underline="hover"
key={file}
color="#5d646c"
style={{ fontSize: '0.9rem' }}
onClick={() => handleBreadcrumbDirectoryClick(file)}
>
{file}
</Link>
)
)}
</Breadcrumbs>
)
}
Expand Down Expand Up @@ -459,12 +472,14 @@ const FileBrowserContentPanel: React.FC<FileBrowserContentPanelProps> = (props)
flexWrap: 'wrap'
}}
>
<ToolButton
tooltip={t('editor:layout.filebrowser.back')}
icon={ArrowBackIcon}
onClick={onBackDirectory}
id="backDir"
/>
{showBackButton && (
<ToolButton
tooltip={t('editor:layout.filebrowser.back')}
icon={ArrowBackIcon}
onClick={onBackDirectory}
id="backDir"
/>
)}
<ToolButton
tooltip={t('editor:layout.filebrowser.refresh')}
icon={AutorenewIcon}
Expand Down Expand Up @@ -553,7 +568,7 @@ const FileBrowserContentPanel: React.FC<FileBrowserContentPanelProps> = (props)
classes={{ paper: styles.paperDialog }}
>
<DialogTitle style={{ padding: '0', textTransform: 'capitalize' }}>
{`${fileProperties.value?.name} ${fileProperties.value?.type == 'folder' ? 'folder' : 'file'} Properties`}
{`${fileProperties.value.name} ${fileProperties.value.type == 'folder' ? 'folder' : 'file'} Properties`}
</DialogTitle>
<Grid container spacing={1} style={{ width: '100%', margin: '0' }}>
<Grid item xs={4} style={{ paddingLeft: '10px', paddingTop: '10px', width: '100%' }}>
Expand All @@ -571,10 +586,10 @@ const FileBrowserContentPanel: React.FC<FileBrowserContentPanelProps> = (props)
</Typography>
</Grid>
<Grid item xs={8} style={{ paddingLeft: '10px', paddingTop: '10px', width: '100%' }}>
<Typography className={styles.secondaryText}>{fileProperties.value?.name}</Typography>
<Typography className={styles.secondaryText}>{fileProperties.value?.type}</Typography>
<Typography className={styles.secondaryText}>{fileProperties.value?.size}</Typography>
<Typography className={styles.secondaryText}>{fileProperties.value?.url}</Typography>
<Typography className={styles.secondaryText}>{fileProperties.value.name}</Typography>
<Typography className={styles.secondaryText}>{fileProperties.value.type}</Typography>
<Typography className={styles.secondaryText}>{fileProperties.value.size}</Typography>
<Typography className={styles.secondaryText}>{fileProperties.value.url}</Typography>
</Grid>
</Grid>
</Dialog>
Expand Down
7 changes: 6 additions & 1 deletion packages/editor/src/components/assets/styles.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,10 @@

.breadcrumb {
color: var(--textColor);

[class*=MuiLink-root] {
cursor: pointer;
}
}

.pagination {
Expand All @@ -255,4 +259,5 @@
.horizontalCenter {
margin: 0 auto;
display: block;
}
}

Loading