From e8f69507260283215d140eee8122557c0c4cba2c Mon Sep 17 00:00:00 2001 From: Jacob Rief Date: Wed, 25 Sep 2024 16:42:57 +0200 Subject: [PATCH] refactor fetching context for React from ModelAdmin --- client/components/editor/Audio.tsx | 34 ++++-- client/components/editor/Common.tsx | 9 +- client/components/editor/Image.tsx | 7 +- client/components/editor/Video.tsx | 20 ++-- .../{folder => folderitem}/Audio.tsx | 0 .../{folder => folderitem}/Video.tsx | 0 client/components/menu/Audio.tsx | 16 +++ client/esbuild.config.mjs | 3 +- client/finder-admin.scss | 1 + client/finder/ControlButtons.tsx | 90 ++++++++++++++ client/finder/FileAdmin.tsx | 10 +- client/finder/FileDetails.tsx | 111 ++---------------- client/finder/FileUploader.tsx | 9 +- client/finder/FolderAdmin.tsx | 6 +- client/finder/FolderTabs.tsx | 14 +-- client/finder/InodeList.tsx | 5 +- client/finder/Item.tsx | 4 +- client/finder/MenuBar.tsx | 8 +- client/finder/Search.tsx | 6 +- client/finder/UploadProgress.tsx | 14 +-- client/icons/archive.svg | 3 + finder/admin/file.py | 8 +- finder/admin/folder.py | 39 +++--- finder/admin/inode.py | 18 ++- finder/contrib/audio/admin.py | 16 ++- finder/contrib/audio/models.py | 12 +- finder/contrib/common/admin.py | 13 +- finder/contrib/image/admin.py | 10 +- finder/contrib/image/models.py | 4 - finder/contrib/video/admin.py | 16 ++- finder/contrib/video/models.py | 9 -- finder/models/file.py | 12 +- finder/models/inode.py | 27 ----- 33 files changed, 288 insertions(+), 266 deletions(-) rename client/components/{folder => folderitem}/Audio.tsx (100%) rename client/components/{folder => folderitem}/Video.tsx (100%) create mode 100644 client/components/menu/Audio.tsx create mode 100644 client/finder/ControlButtons.tsx create mode 100644 client/icons/archive.svg diff --git a/client/components/editor/Audio.tsx b/client/components/editor/Audio.tsx index aeaa85049..292afab96 100644 --- a/client/components/editor/Audio.tsx +++ b/client/components/editor/Audio.tsx @@ -1,22 +1,23 @@ -import React, {useContext, useState} from 'react'; +import React, {Fragment, useState} from 'react'; import WavesurferPlayer from '@wavesurfer/react'; import RegionsPlugin from 'wavesurfer.js/dist/plugins/regions.js'; -import {FinderSettings} from 'finder/FinderSettings'; import {FileDetails} from 'finder/FileDetails'; import PauseIcon from 'icons/pause.svg'; import PlayIcon from 'icons/play.svg'; export default function Audio(props) { + const {settings} = props; const sampleFields = { start: document.getElementById('id_sample_start') as HTMLInputElement, duration: document.getElementById('id_sample_duration') as HTMLInputElement, }; - const settings = useContext(FinderSettings); const [wavesurfer, setWavesurfer] = useState(null); const [isPlaying, setIsPlaying] = useState(false); const onReady = (ws) => { + document.getElementById('wavesurfer-initializing')?.remove(); + ws.setOptions({height: 128}); setWavesurfer(ws); const wsRegions = ws.registerPlugin(RegionsPlugin.create()); @@ -35,21 +36,36 @@ export default function Audio(props) { }); setIsPlaying(false); - } + }; const onPlayPause = () => { wavesurfer && wavesurfer.playPause(); - } + }; - const controlButtons = [isPlaying ? - : - + const controlButtons = [ + {isPlaying ? + : + + } ]; + const placeholderStyle: React.CSSProperties = { + height: '143px', + padding: '2rem', + fontSize: '18px', + boxSizing: 'border-box', + color: 'rgb(128, 128, 128)', + cursor: 'wait', + }; + return ( - + +
+ {gettext("Initializing audio player, please wait ...")} +
setIsPlaying(true)} onPause={() => setIsPlaying(false)} diff --git a/client/components/editor/Common.tsx b/client/components/editor/Common.tsx index 47e8aba81..7cbc57012 100644 --- a/client/components/editor/Common.tsx +++ b/client/components/editor/Common.tsx @@ -1,14 +1,11 @@ -import React, {useContext} from 'react'; -import {FinderSettings} from 'finder/FinderSettings'; +import React from 'react'; import {FileDetails} from 'finder/FileDetails'; export default function Common(props) { - const settings = useContext(FinderSettings); - return ( - - + + ); } diff --git a/client/components/editor/Image.tsx b/client/components/editor/Image.tsx index 14f1fb653..0eaee33f3 100644 --- a/client/components/editor/Image.tsx +++ b/client/components/editor/Image.tsx @@ -1,11 +1,10 @@ -import React, {useContext, useEffect, useRef, useState} from 'react'; +import React, {useEffect, useRef, useState} from 'react'; import ReactCrop, {Crop} from 'react-image-crop'; -import {FinderSettings} from 'finder/FinderSettings'; import {FileDetails} from 'finder/FileDetails'; export default function Image(props) { - const settings = useContext(FinderSettings); + const {settings} = props; const cropFields = { x: document.getElementById('id_crop_x') as HTMLInputElement, y: document.getElementById('id_crop_y') as HTMLInputElement, @@ -47,7 +46,7 @@ export default function Image(props) { } return ( - + diff --git a/client/components/editor/Video.tsx b/client/components/editor/Video.tsx index 3da96daf9..a4ff234b9 100644 --- a/client/components/editor/Video.tsx +++ b/client/components/editor/Video.tsx @@ -1,6 +1,5 @@ -import React, {useContext, useRef, useState} from 'react'; +import React, {Fragment, useRef, useState} from 'react'; import ReactPlayer from 'react-player/file'; -import {FinderSettings} from 'finder/FinderSettings'; import {FileDetails} from 'finder/FileDetails'; import PauseIcon from 'icons/pause.svg'; import PlayIcon from 'icons/play.svg'; @@ -8,10 +7,10 @@ import CameraIcon from 'icons/camera.svg'; export default function Video(props) { + const {settings} = props; const sampleFields = { sampleStart: document.getElementById('id_sample_start') as HTMLInputElement, }; - const settings = useContext(FinderSettings); const style = { width: '960px', maxWidth: '100%', @@ -39,14 +38,17 @@ export default function Video(props) { sampleFields.sampleStart.value = currentTime; } - const controlButtons = [(isPlaying ? - : - - ), - + const controlButtons = [ + {isPlaying ? + : + + }, + + + , ]; return ( - + archiveSelectedIcons()} className={props.numSelectedInodes ? null : "disabled"} data-tooltip-id="django-finder-tooltip" data-tooltip-content={gettext("Create archive from selection")}> + + + ); +} diff --git a/client/esbuild.config.mjs b/client/esbuild.config.mjs index 1880a18ef..f6d55dfbd 100644 --- a/client/esbuild.config.mjs +++ b/client/esbuild.config.mjs @@ -10,7 +10,8 @@ await build({ 'client/folder-admin.tsx', 'client/file-admin.tsx', 'client/components/editor/*.tsx', - 'client/components/folder/*.tsx', + 'client/components/folderitem/*.tsx', + 'client/components/menu/*.tsx', ], bundle: true, minify: buildOptions.minify, diff --git a/client/finder-admin.scss b/client/finder-admin.scss index 62b386b25..19eb42760 100644 --- a/client/finder-admin.scss +++ b/client/finder-admin.scss @@ -395,6 +395,7 @@ $active-rectangle: rgb(210, 210, 112); right: 0; bottom: 0; left: 0; + z-index: 1; background-color: rgba(255, 255, 255, 0.75); display: flex; flex-direction: column; diff --git a/client/finder/ControlButtons.tsx b/client/finder/ControlButtons.tsx new file mode 100644 index 000000000..b1adb7280 --- /dev/null +++ b/client/finder/ControlButtons.tsx @@ -0,0 +1,90 @@ +import React, {Fragment, useMemo, useRef} from 'react'; +import DownloadIcon from 'icons/download.svg'; +import FullSizeIcon from 'icons/full-size.svg'; +import UploadIcon from 'icons/upload.svg'; + + +function DownloadFileButton(props) { + const {settings} = props; + + return ( + + {gettext("Download")} + + ); +} + + +function ViewOriginalButton(props) { + const {settings} = props; + + function viewOriginal() { + window.open(settings.download_url, '_blank').focus(); + } + + return ( + + ); +} + + +function ReplaceFileButton(props) { + const {settings} = props; + const inputRef = useRef(null); + + function replaceFile() { + inputRef.current.click(); + } + + function handleFileSelect(event) { + const file = event.target.files[0]; + const promise = new Promise((resolve, reject) => { + file.resolve = resolve; + file.reject = reject; + }); + props.setUploadFile(file); + promise.then((response) => { + window.location.reload(); + }).catch((error) => { + alert(error); + }).finally( () => { + props.setUploadFile(null); + }); + } + + return (<> + + + ); +} + + +export function ControlButtons(props) { + //const settings = useContext(FinderSettings); + const {settings} = props; + + const controlButtons = useMemo(() => { + const buttons: Array = []; + if (settings.download_file) { + buttons.push(); + } + if (settings.view_original) { + buttons.push(); + } + if (settings.replace_file) { + buttons.push(); + } + return buttons; + }, []); + + return ( +
+ {props.children} + {controlButtons.map((button, index) => + {button} + )} +
+ ); +} diff --git a/client/finder/FileAdmin.tsx b/client/finder/FileAdmin.tsx index a1f6393bc..7c026700d 100644 --- a/client/finder/FileAdmin.tsx +++ b/client/finder/FileAdmin.tsx @@ -1,12 +1,12 @@ import React, {useContext, useEffect, useMemo, useRef, lazy, Suspense} from 'react'; import {FinderSettings} from './FinderSettings'; -import {FolderTabs} from "./FolderTabs"; +import {FolderTabs} from './FolderTabs'; -export function FileAdmin(props) { +export function FileAdmin() { const settings = useContext(FinderSettings); const FileEditor = useMemo(() => { - const component = `./components/editor/${settings.extension.component}.js`; + const component = `./components/editor/${settings.react_component}.js`; const LazyItem = lazy(() => import(component)); return (props) => ( {gettext("Loading...")}}> @@ -21,9 +21,9 @@ export function FileAdmin(props) { }, []); return (<> - +
- +
); diff --git a/client/finder/FileDetails.tsx b/client/finder/FileDetails.tsx index d78e9bec9..933489795 100644 --- a/client/finder/FileDetails.tsx +++ b/client/finder/FileDetails.tsx @@ -1,106 +1,11 @@ -import React, { - Fragment, - useContext, - useMemo, - useRef, - useState -} from 'react'; +import React, {useMemo, useState} from 'react'; import Select from 'react-select'; +import {ControlButtons} from 'finder/ControlButtons'; import {ProgressBar, ProgressOverlay} from 'finder/UploadProgress'; -import {FinderSettings} from 'finder/FinderSettings'; -import DownloadIcon from 'icons/download.svg'; -import FullSizeIcon from 'icons/full-size.svg'; -import UploadIcon from 'icons/upload.svg'; -function DownloadFileButton(props) { - const settings = useContext(FinderSettings); - - return ( - {gettext("Download")} - ); -} - - -function ViewOriginalButton() { - const settings = useContext(FinderSettings); - - function viewOriginal() { - window.open(settings.download_url, '_blank').focus(); - } - - return ( - - ); -} - - -function ReplaceFileButton(props) { - const {acceptMimeType} = props; - const inputRef = useRef(null); - - function replaceFile() { - inputRef.current.click(); - } - - function handleFileSelect(event) { - const file = event.target.files[0]; - const promise = new Promise((resolve, reject) => { - file.resolve = resolve; - file.reject = reject; - }); - props.setUploadFile(file); - promise.then((response) => { - window.location.reload(); - }).catch((error) => { - alert(error); - }).finally( () => { - props.setUploadFile(null); - }); - } - - return (<> - - - ); -} - - -export function ControlButtons(props) { - const settings = useContext(FinderSettings); - - const controlButtons = useMemo(() => { - const buttons: Array = []; - if (settings.download_url) { - buttons.push(); - } - if (settings.original_url) { - buttons.push(); - } - if (settings.replacing_mime_type) { - buttons.push( - - ); - } - return buttons; - }, []); - - return ( -
- {props.children} - {controlButtons.map((button, index) => - {button} - )} -
- ); -} - - -function SelectLabels() { - const settings = useContext(FinderSettings); +function SelectLabels(props) { + const {settings} = props; const LabelOption = ({innerProps, data}) => (
@@ -165,7 +70,7 @@ function SelectLabels() { export function FileDetails(props) { - const settings = useContext(FinderSettings); + const {settings} = props; const [uploadFile, setUploadFile] = useState>(null); const subtitle = useMemo(() => { const subtitle = document.getElementById('id_subtitle'); @@ -180,12 +85,12 @@ export function FileDetails(props) {

{subtitle}

{props.children} - {props.controlButtons} - + {props.controlButtons} +
{uploadFile && - + } ); diff --git a/client/finder/FileUploader.tsx b/client/finder/FileUploader.tsx index 1da4a0d8f..ff77e917d 100644 --- a/client/finder/FileUploader.tsx +++ b/client/finder/FileUploader.tsx @@ -68,14 +68,13 @@ export const FileUploader = forwardRef((props: any, forwardedRef) => { return (
{props.children} - - {(dragging || uploading.length > 0) && + { + (dragging || uploading.length > 0) && { uploading.map((file, index) => - + ) } - } -
+ }
) }); diff --git a/client/finder/FolderAdmin.tsx b/client/finder/FolderAdmin.tsx index cf92f6c6d..4b8537191 100644 --- a/client/finder/FolderAdmin.tsx +++ b/client/finder/FolderAdmin.tsx @@ -243,6 +243,7 @@ export function FolderAdmin(props) { listRef={columnRefs[settings.folder_id]} menuBarRef={menuBarRef} layout={layout} + settings={settings} /> @@ -271,6 +272,7 @@ export function FolderAdmin(props) { ref={folderId === settings.folder_id ? uploaderRef : null} folderId={folderId} handleUpload={id => columnRefs[id].current.fetchInodes()} + settings={settings} > @@ -333,6 +336,7 @@ export function FolderAdmin(props) { layout={layout} setLayout={setLayout} setSearchResult={setSearchResult} + settings={settings} /> - + {settings.is_trash ? renderTrashArea() : renderWorkArea()}
diff --git a/client/finder/FolderTabs.tsx b/client/finder/FolderTabs.tsx index 360c6725e..75f9e020d 100644 --- a/client/finder/FolderTabs.tsx +++ b/client/finder/FolderTabs.tsx @@ -1,6 +1,5 @@ import {useDroppable} from '@dnd-kit/core'; -import React, {forwardRef, useContext, useImperativeHandle, useState} from 'react'; -import {FinderSettings} from './FinderSettings'; +import React, {forwardRef, useImperativeHandle, useState} from 'react'; import CloseIcon from 'icons/close.svg'; import PinIcon from 'icons/pin.svg'; import RecycleIcon from 'icons/recycle.svg'; @@ -9,8 +8,7 @@ import UpIcon from 'icons/folder-up.svg'; function FolderTab(props) { - const settings = useContext(FinderSettings); - const {folder, isSearchResult} = props; + const {folder, isSearchResult, settings} = props; const { isOver, setNodeRef, @@ -76,8 +74,7 @@ function FolderTab(props) { } export const FolderTabs = forwardRef((props: any, forwardedRef) => { - const settings = useContext(FinderSettings); - const {isSearchResult} = props; + const {isSearchResult, settings} = props; const [favoriteFolders, setFavoriteFolders] = useState(settings.favorite_folders); useImperativeHandle(forwardedRef, () => ({setFavoriteFolders})); @@ -108,9 +105,9 @@ export const FolderTabs = forwardRef((props: any, forwardedRef) => { return (
    {isSearchResult || settings.is_trash ? (<> - + ) : ( - settings.parent_id && () + settings.parent_id && () )} {favoriteFolders.filter( folder => !isSearchResult || folder.is_pinned || folder.id !== settings.folder_id @@ -120,6 +117,7 @@ export const FolderTabs = forwardRef((props: any, forwardedRef) => { folder={folder} togglePin={togglePin} isSearchResult={isSearchResult} + settings={settings} /> )}
diff --git a/client/finder/InodeList.tsx b/client/finder/InodeList.tsx index 17a519a86..efb2e3883 100644 --- a/client/finder/InodeList.tsx +++ b/client/finder/InodeList.tsx @@ -1,18 +1,15 @@ import React, { createRef, forwardRef, - useContext, useEffect, useImperativeHandle, useState, } from 'react'; import {Folder, File, DraggableItem, ListItem} from './Item'; -import {FinderSettings} from './FinderSettings'; export const InodeList = forwardRef((props: any, forwardedRef) => { - const settings = useContext(FinderSettings); - const {folderId, previousFolderId, setCurrentFolder, menuBarRef, layout} = props; + const {folderId, previousFolderId, setCurrentFolder, menuBarRef, layout, settings} = props; const [isLoading, setLoading] = useState(false); const [inodes, setInodes] = useState([]); const [lastSelectedInode, setSelectedInode] = useState(-1); diff --git a/client/finder/Item.tsx b/client/finder/Item.tsx index db17dae5b..eee24869c 100644 --- a/client/finder/Item.tsx +++ b/client/finder/Item.tsx @@ -102,8 +102,8 @@ export function ListItem(props) { const settings = useContext(FinderSettings); const [focusHandler, setFocusHandler] = useState(null); const FigBody = useMemo(() => { - if (props.extension.component) { - const component = `./components/folder/${props.extension.component}.js`; + if (props.react_component) { + const component = `./components/folderitem/${props.react_component}.js`; const LazyItem = lazy(() => import(component)); return (props) => ( diff --git a/client/finder/MenuBar.tsx b/client/finder/MenuBar.tsx index 775cf4c6b..0086b1b3d 100644 --- a/client/finder/MenuBar.tsx +++ b/client/finder/MenuBar.tsx @@ -1,6 +1,5 @@ import React, { useRef, - useContext, forwardRef, useState, useImperativeHandle, @@ -103,8 +102,7 @@ function MenuExtension(props) { export const MenuBar = forwardRef((props: any, forwardedRef) => { - const settings = useContext(FinderSettings); - const {currentFolderId, columnRefs, folderTabsRef, openUploader, downloadFiles, layout, setLayout, setSearchResult} = props; + const {currentFolderId, columnRefs, folderTabsRef, openUploader, downloadFiles, layout, setLayout, setSearchResult, settings} = props; const [numSelectedInodes, setNumSelectedInodes] = useState(0); const [numSelectedFiles, setNumSelectedFiles] = useState(0); const [clipboard, setClipboard] = useClipboard(); @@ -325,7 +323,7 @@ export const MenuBar = forwardRef((props: any, forwardedRef) => {