diff --git a/packages/client-core/i18n/en/common.json b/packages/client-core/i18n/en/common.json index 9422cd0f42..0f3da1fd1d 100755 --- a/packages/client-core/i18n/en/common.json +++ b/packages/client-core/i18n/en/common.json @@ -12,7 +12,10 @@ "close": "Close", "submit": "Submit", "search": "Search", - "copyText": "Copy Text" + "copyText": "Copy Text", + "edit": "Edit", + "save": "Save", + "none": "None" }, "tooltip": { "pressKey": "Press {{tip}} to {{message}}" diff --git a/packages/client-core/i18n/en/editor.json b/packages/client-core/i18n/en/editor.json index f1d24868f6..a24de4d860 100755 --- a/packages/client-core/i18n/en/editor.json +++ b/packages/client-core/i18n/en/editor.json @@ -1173,15 +1173,18 @@ "search-placeholder": "Search folders", "generatingThumbnails": "Generating Thumbnails ({{count}} remaining)", "fileProperties": { - "name": "Name:", - "type": "Type:", - "size": "Size:", - "url": "URL:", - "attribution": "Attribution:", - "licensing": "Licensing:", - "tag": "Tag:", - "addTag": "Add Tag", - "save-changes": "Save Changes" + "header": "{{fileName}} Info", + "name": "Name", + "type": "Type", + "size": "Size", + "url": "URL", + "attribution": "Attribution", + "licensing": "Licensing", + "tag": "Tag", + "addTag": "Add New Tag", + "add": "Add", + "save-changes": "Save Changes", + "discard": "Discard" }, "view-mode": { "icons": "View: Icons", diff --git a/packages/ui/src/components/editor/panels/Files/browserGrid/FilePropertiesModal.tsx b/packages/ui/src/components/editor/panels/Files/browserGrid/FilePropertiesModal.tsx new file mode 100644 index 0000000000..3dc1e1bbe2 --- /dev/null +++ b/packages/ui/src/components/editor/panels/Files/browserGrid/FilePropertiesModal.tsx @@ -0,0 +1,241 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +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, { useEffect } from 'react' +import { useTranslation } from 'react-i18next' + +import { PopoverState } from '@etherealengine/client-core/src/common/services/PopoverState' +import { StaticResourceType, fileBrowserPath, staticResourcePath } from '@etherealengine/common/src/schema.type.module' +import { projectResourcesPath } from '@etherealengine/common/src/schemas/media/project-resource.schema' +import { FileDataType } from '@etherealengine/editor/src/components/assets/FileBrowser/FileDataType' +import { EditorState } from '@etherealengine/editor/src/services/EditorServices' +import { getMutableState, useHookstate } from '@etherealengine/hyperflux' +import { useFind, useMutation } from '@etherealengine/spatial/src/common/functions/FeathersHooks' +import { HiPencil, HiPlus, HiXMark } from 'react-icons/hi2' +import { RiSave2Line } from 'react-icons/ri' +import Button from '../../../../../primitives/tailwind/Button' +import Input from '../../../../../primitives/tailwind/Input' +import Modal from '../../../../../primitives/tailwind/Modal' +import Text from '../../../../../primitives/tailwind/Text' + +export default function FilePropertiesModal({ file }: { file: FileDataType }) { + const { t } = useTranslation() + const newFileName = useHookstate(file.name) + const fileService = useMutation(fileBrowserPath) + + const handleSubmit = async () => { + fileService.update(null, { + oldName: file.fullName, + newName: file.isFolder ? newFileName.value : `${newFileName.value}.${file.type}`, + oldPath: file.path, + newPath: file.path, + isCopy: false + }) + PopoverState.hidePopupover() + } + + const staticResource = useFind(staticResourcePath, { + query: { + key: file.key, + project: getMutableState(EditorState).projectName.value! + } + }) + + const staticResourceMutation = useMutation(staticResourcePath) + const projectResourcesMutation = useMutation(projectResourcesPath) + + const resourceProperties = useHookstate({ + id: '', + project: '', + tags: { + input: '', + all: [] as string[] + }, + attribution: { + editing: false, + input: '' + }, + licensing: { + editing: false, + input: '' + } + }) + + useEffect(() => { + if (staticResource.data.length > 0) { + if (staticResource.data.length > 1) console.info('Multiple resources with same key found') + const resources = JSON.parse(JSON.stringify(staticResource.data[0])) as StaticResourceType + if (resources) { + resourceProperties.id.set(resources.id) + resourceProperties.project.set(resources.project ?? '') + resourceProperties.tags.all.set(resources.tags ?? []) + resourceProperties.attribution.input.set(resources.attribution ?? '') + resourceProperties.licensing.input.set(resources.licensing ?? '') + } + } + }, [staticResource.data]) + + const handleAddTag = () => { + const newTags = [...resourceProperties.tags.all.value, resourceProperties.tags.input.value] + staticResourceMutation.patch(resourceProperties.id.value, { + tags: newTags + }) + resourceProperties.tags.input.set('') + resourceProperties.tags.all.set(newTags) + } + + const handleRemoveTag = (removedTag: string) => { + const currentTags = resourceProperties.tags.all.value.filter((tag) => tag !== removedTag) + staticResourceMutation.patch(resourceProperties.id.value, { + tags: currentTags + }) + resourceProperties.tags.all.set(currentTags) + } + + return ( + +
+
+ {t('editor:layout.filebrowser.fileProperties.name')} + {file.name} +
+
+ {t('editor:layout.filebrowser.fileProperties.type')} + {file.type.toUpperCase()} +
+
+ {t('editor:layout.filebrowser.fileProperties.size')} + {file.size} +
+
+ {t('editor:layout.filebrowser.fileProperties.attribution')} + + {resourceProperties.attribution.editing.value ? ( + <> + resourceProperties.attribution.input.set(event.target.value)} + /> +
+
+ {t('editor:layout.filebrowser.fileProperties.licensing')} + + {resourceProperties.licensing.editing.value ? ( + <> + resourceProperties.licensing.input.set(event.target.value)} + /> +
+
+ + {t('editor:layout.filebrowser.fileProperties.addTag')} + +
+ resourceProperties.tags.input.set(event.target.value)} + /> +
+
+ {resourceProperties.tags.all.value.map((tag) => ( + + {tag} handleRemoveTag(tag)} /> + + ))} +
+
+
+
+ ) +} diff --git a/packages/ui/src/components/editor/panels/Files/browserGrid/index.tsx b/packages/ui/src/components/editor/panels/Files/browserGrid/index.tsx index 2875ba65d5..a6a9ac3ce2 100644 --- a/packages/ui/src/components/editor/panels/Files/browserGrid/index.tsx +++ b/packages/ui/src/components/editor/panels/Files/browserGrid/index.tsx @@ -49,6 +49,7 @@ import Button from '../../../../../primitives/tailwind/Button' import { ContextMenu } from '../../../layout/ContextMenu' import { FileIcon } from '../icon' import DeleteFileModal from './DeleteFileModal' +import FilePropertiesModal from './FilePropertiesModal' import RenameFileModal from './RenameFileModal' export const canDropItemOverFolder = (folderName: string) => @@ -193,7 +194,6 @@ type FileBrowserItemType = { disableDnD?: boolean currentContent: MutableRefObject<{ item: FileDataType; isCopy: boolean }> setFileProperties: any - setOpenPropertiesModal: any setOpenCompress: any setOpenConvert: any isFilesLoading: boolean @@ -209,7 +209,6 @@ export function FileBrowserItem({ item, disableDnD, currentContent, - setOpenPropertiesModal, setFileProperties, setOpenCompress, setOpenConvert, @@ -292,13 +291,6 @@ export function FileBrowserItem({ }) } - const viewAssetProperties = () => { - setFileProperties(item) - - setOpenPropertiesModal(true) - handleClose() - } - const viewCompress = () => { setFileProperties(item) setOpenCompress(true) @@ -412,7 +404,12 @@ export function FileBrowserItem({ > {t('editor:layout.assetGrid.deleteAsset')} -