Skip to content

Commit

Permalink
refactor towards easier extendibility of components
Browse files Browse the repository at this point in the history
  • Loading branch information
jrief committed Sep 24, 2024
1 parent ac050a5 commit b8cbb54
Show file tree
Hide file tree
Showing 13 changed files with 111 additions and 31 deletions.
19 changes: 11 additions & 8 deletions client/finder/FileAdmin.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import React, {useContext, useEffect, useRef, lazy, Suspense} from 'react';
import React, {useContext, useEffect, useMemo, useRef, lazy, Suspense} from 'react';
import {FinderSettings} from './FinderSettings';
import {FolderTabs} from "./FolderTabs";


export function FileAdmin(props) {
const settings = useContext(FinderSettings);
const FileEditor = useMemo(() => {
const component = `./components/editor/${settings.extension.component}.js`;
const LazyItem = lazy(() => import(component));
return (props) => (
<Suspense fallback={<span>{gettext("Loading...")}</span>}>
<LazyItem {...props} />
</Suspense>
);
}, []);
const editorRef = useRef(null);
const component = `./components/editor/${settings.editor_component}.js`;
const FileEditor = settings.editor_component ? lazy(() => import(component)) : null;

useEffect(() => {
editorRef.current.insertAdjacentHTML('afterbegin', settings.mainContent.innerHTML);
Expand All @@ -16,11 +23,7 @@ export function FileAdmin(props) {
return (<>
<FolderTabs />
<div className="detail-editor">
{FileEditor &&
<Suspense fallback={<span>Loading...</span>}>
<FileEditor editorRef={editorRef} />
</Suspense>
}
<FileEditor editorRef={editorRef} extension={settings.extension} />
<div ref={editorRef}></div>
</div>
</>);
Expand Down
2 changes: 0 additions & 2 deletions client/finder/FileDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import {FinderSettings} from 'finder/FinderSettings';
import DownloadIcon from 'icons/download.svg';
import FullSizeIcon from 'icons/full-size.svg';
import UploadIcon from 'icons/upload.svg';
import PauseIcon from "../icons/pause.svg";
import PlayIcon from "../icons/play.svg";


function DownloadFileButton(props) {
Expand Down
18 changes: 9 additions & 9 deletions client/finder/Item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,19 +100,19 @@ export function FigureLabels(props) {

export function ListItem(props) {
const settings = useContext(FinderSettings);
const FigBody = props.folder_component ? useMemo(
() => {
const component = `./components/folder/${props.folder_component}.js`;
const LazyFigure = lazy(() => import(component));
const [focusHandler, setFocusHandler] = useState(null);
const FigBody = useMemo(() => {
if (props.extension.component) {
const component = `./components/folder/${props.extension.component}.js`;
const LazyItem = lazy(() => import(component));
return (props) => (
<Suspense>
<LazyFigure {...props}>{props.children}</LazyFigure>
<LazyItem {...props}>{props.children}</LazyItem>
</Suspense>
);
},
[]
) : StaticFigure;
const [focusHandler, setFocusHandler] = useState(null);
}
return StaticFigure;
},[]);

function handleFocus(event) {
if (!(event.target.contentEditable))
Expand Down
33 changes: 32 additions & 1 deletion client/finder/MenuBar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
import React, {useRef, useContext, forwardRef, useState, useImperativeHandle, useEffect} from 'react';
import React, {
useRef,
useContext,
forwardRef,
useState,
useImperativeHandle,
useEffect,
lazy,
Suspense,
useMemo
} from 'react';
import {useClipboard, useCookie} from './Storage';
import {SearchField} from './Search';
import {FinderSettings} from './FinderSettings';
Expand Down Expand Up @@ -73,6 +83,24 @@ function SortingOptionsItem(props: any) {
}


function MenuExtension(props) {
console.log(props)

const MenuComponent = useMemo(() => {
const component = `./components/menu/${props.extension.component}.js`;
const LazyItem = lazy(() => import(component));
return (props) => (
<Suspense>
<LazyItem {...props} />
</Suspense>
);
},[]);

return (
<MenuComponent {...props} />
)
}


export const MenuBar = forwardRef((props: any, forwardedRef) => {
const settings = useContext(FinderSettings);
Expand Down Expand Up @@ -315,6 +343,9 @@ export const MenuBar = forwardRef((props: any, forwardedRef) => {
<li onClick={addFolder} data-tooltip-id="django-finder-tooltip" data-tooltip-content={gettext("Add new folder")}><AddFolderIcon /></li>
<li className={numSelectedFiles ? null : "disabled"} onClick={downloadSelectedFiles} data-tooltip-id="django-finder-tooltip" data-tooltip-content={gettext("Download selected files")}><DownloadIcon /></li>
<li onClick={openUploader} data-tooltip-id="django-finder-tooltip" data-tooltip-content={gettext("Upload files from local host")}><UploadIcon /></li>
{settings.menu_extensions.filter(extension => extension.component).map((extension, index) => (
<MenuExtension key={index} extension={extension} columnRefs={columnRefs} numSelectedInodes={numSelectedInodes} numSelectedFiles={numSelectedFiles} />
))}
</>)}
</ul>
</nav>
Expand Down
2 changes: 1 addition & 1 deletion finder/admin/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ def get_settings(self, request, inode):
filename=inode.file_name,
file_id=inode.id,
file_mime_type=inode.mime_type,
editor_component=inode.editor_component,
extension=inode.react_editor_extension,
)
if inode.labels.model.objects.exists():
settings['labels'] = [
Expand Down
1 change: 1 addition & 0 deletions finder/admin/folder.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ def get_settings(self, request, inode):
base_url=reverse('admin:finder_foldermodel_changelist', current_app=self.admin_site.name),
ancestors=ancestor_ids,
legends=self._legends,
menu_extensions=[model.react_menu_extension for model in InodeModel.file_models],
)
return settings

Expand Down
2 changes: 1 addition & 1 deletion finder/admin/inode.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,8 @@ def get_inodes(self, sorting=None, **lookup):
'download_url': obj.get_download_url(),
'thumbnail_url': obj.casted.get_thumbnail_url(),
'sample_url': obj.casted.get_sample_url(),
'folder_component': obj.casted.folder_component,
'summary': obj.casted.summary,
'extension': obj.casted.react_folder_extension,
}
if 'labels' in data_fields:
values['labels'] = list(obj.labels.values('id', 'name', 'color'))
Expand Down
15 changes: 14 additions & 1 deletion finder/contrib/audio/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from django.core.files.storage import default_storage
from django.contrib.staticfiles.storage import staticfiles_storage
from django.utils.functional import classproperty

from filer import settings as filer_settings
from finder.models.file import FileModel
Expand All @@ -18,12 +19,24 @@ class AudioFileModel(FileModel):
filer_public_thumbnails = Path(
filer_settings.FILER_STORAGES['public']['thumbnails']['THUMBNAIL_OPTIONS']['base_dir']
)
folder_component = editor_component = 'Audio'

class Meta:
proxy = True
app_label = 'finder'

@classproperty
def react_folder_extension(cls):
return {'component': 'Audio'}

@classproperty
def react_editor_extension(cls):
return {'component': 'Audio'}

@classproperty
def react_menu_extension(cls):
# TODO: move this to ZIPFileModel
return {'component': 'Audio'}

def get_sample_url(self):
sample_start = self.meta_data.get('sample_start', 0)
sample_duration = self.meta_data.get('sample_duration', SAMPLE_DURATION)
Expand Down
2 changes: 0 additions & 2 deletions finder/contrib/common/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
class PDFFileModel(FileModel):
accept_mime_types = ['application/pdf']
fallback_thumbnail_url = staticfiles_storage.url('filer/icons/file-pdf.svg')
editor_component = 'Common'

class Meta:
proxy = True
Expand All @@ -21,7 +20,6 @@ class SpreadsheetModel(FileModel):
'application/vnd.oasis.opendocument.spreadsheet',
]
fallback_thumbnail_url = staticfiles_storage.url('filer/icons/file-spreadsheet.svg')
editor_component = 'Common'

class Meta:
proxy = True
Expand Down
7 changes: 5 additions & 2 deletions finder/contrib/image/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from django.contrib.staticfiles.storage import staticfiles_storage
from django.db import models
from django.utils.functional import cached_property
from django.utils.functional import cached_property, classproperty

from filer import settings as filer_settings

Expand All @@ -17,7 +17,6 @@ class ImageModel(AbstractFileModel):
)
thumbnail_size = 180
fallback_thumbnail_url = staticfiles_storage.url('filer/icons/file-picture.svg')
editor_component = 'Image'

width = models.SmallIntegerField(default=0)
height = models.SmallIntegerField(default=0)
Expand All @@ -29,6 +28,10 @@ class Meta:
def summary(self):
return "{width}×{height}px ({size})".format(size=super().summary, width=self.width, height=self.height)

@classproperty
def react_editor_extension(cls):
return {'component': 'Image'}

def get_thumbnail_path(self, crop_x=None, crop_y=None, crop_size=None):
id = str(self.id)
thumbnail_folder = self.filer_public_thumbnails / f'{id[0:2]}/{id[2:4]}/{id}'
Expand Down
10 changes: 9 additions & 1 deletion finder/contrib/video/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from django.core.files.storage import default_storage
from django.contrib.staticfiles.storage import staticfiles_storage
from django.utils.functional import classproperty

import ffmpeg

Expand All @@ -18,12 +19,19 @@ class VideoFileModel(FileModel):
filer_public_thumbnails = Path(
filer_settings.FILER_STORAGES['public']['thumbnails']['THUMBNAIL_OPTIONS']['base_dir']
)
folder_component = editor_component = 'Video'

class Meta:
proxy = True
app_label = 'finder'

@classproperty
def react_folder_extension(cls):
return {'component': 'Video'}

@classproperty
def react_editor_extension(cls):
return {'component': 'Video'}

def get_sample_url(self):
sample_start = self.meta_data.get('sample_start')
if sample_start is None:
Expand Down
7 changes: 6 additions & 1 deletion finder/models/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from django.core.files.storage import default_storage
from django.db import models
from django.template.defaultfilters import filesizeformat
from django.utils.functional import cached_property
from django.utils.functional import cached_property, classproperty
from django.utils.translation import gettext_lazy as _

from filer import settings as filer_settings
Expand Down Expand Up @@ -167,6 +167,11 @@ def get_thumbnail_url(self):
"""
return self.fallback_thumbnail_url

@classproperty
def react_editor_extension(cls):
# TODO: refactor towards get_editor_context method
return {'component': 'Common'}

@cached_property
def mime_maintype(self):
return self.mime_type.split('/')[0]
Expand Down
24 changes: 22 additions & 2 deletions finder/models/inode.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured, ValidationError
from django.db import models
from django.utils.functional import cached_property
from django.utils.functional import cached_property, classproperty
from django.utils.translation import gettext_lazy as _


Expand Down Expand Up @@ -98,7 +98,6 @@ def filename_validator(value):

class InodeModel(models.Model, metaclass=InodeMetaModel):
is_folder = False
folder_component= editor_component = None
data_fields = ['id', 'name', 'parent', 'created_at', 'last_modified_at']

id = models.UUIDField(
Expand Down Expand Up @@ -166,6 +165,27 @@ def pretty_path(self):
names.append(self.name)
return " / ".join(names)

@classproperty
def react_folder_extension(cls):
"""
Hook to return the React folder component for the given model.
"""
return {'component': None}

@classproperty
def react_editor_extension(cls):
"""
Hook to return the React editor component for the given model.
"""
return {'component': None}

@classproperty
def react_menu_extension(cls):
"""
Hook to return the React menu component for the given model.
"""
return {'component': None}

def get_sample_url(self):
"""
Hook to return a URL for a small sample file. Only used in list views.
Expand Down

0 comments on commit b8cbb54

Please sign in to comment.