From 438af68b9fab78647a4531bbd5965625d25d2e84 Mon Sep 17 00:00:00 2001 From: SethBurkart123 Date: Tue, 21 May 2024 21:36:20 +1000 Subject: [PATCH] add basic theme upload functionality --- src/interface/components/ThemeSelector.tsx | 95 ++++++++++++++++++++- src/interface/components/useVisibility.tsx | 34 ++++++++ src/interface/pages/SettingsPage.tsx | 2 + src/interface/pages/SettingsPage/Themes.tsx | 2 +- 4 files changed, 129 insertions(+), 4 deletions(-) create mode 100644 src/interface/components/useVisibility.tsx diff --git a/src/interface/components/ThemeSelector.tsx b/src/interface/components/ThemeSelector.tsx index 09a8b1f0..d604e124 100644 --- a/src/interface/components/ThemeSelector.tsx +++ b/src/interface/components/ThemeSelector.tsx @@ -7,6 +7,11 @@ import { CustomTheme, DownloadedTheme } from '../types/CustomThemes'; import { useSettingsContext } from '../SettingsContext'; import { SettingsState } from '../types/AppProps'; import { debounce } from 'lodash'; +import { InstallTheme } from '../../seqta/ui/themes/downloadTheme'; +import SpinnerIcon from './LoadingSpinner'; +import { toast } from 'react-toastify'; +import 'react-toastify/dist/ReactToastify.css'; +import useVisibility from './useVisibility'; interface ThemeSelectorProps { isEditMode: boolean; @@ -17,7 +22,14 @@ const ThemeSelector: ForwardRefExoticComponent & const [themes, setThemes] = useState[]>([]); const [downloadedThemes, setDownloadedThemes] = useState([]); const [isLoading, setIsLoading] = useState(true); + const [isDragging, setIsDragging] = useState(false); + const [tempTheme, setTempTheme] = useState(null); const { settingsState, setSettingsState } = useSettingsContext(); + const [elementRef, isVisible] = useVisibility({ + root: null, // Use the viewport as the root + rootMargin: '0px', + threshold: 0.1, // 10% of the element needs to be visible + }); const setSelectedTheme = (themeId: string) => { setSettingsState((prevState: SettingsState) => ({ @@ -54,6 +66,19 @@ const ThemeSelector: ForwardRefExoticComponent & }; }, []); + useEffect(() => { + let intervalId: any; + if (isVisible) { + intervalId = setInterval(fetchThemes, 10000); // Fetch themes every 10 seconds + } else { + clearInterval(intervalId); + } + + return () => { + clearInterval(intervalId); + }; + }, [isVisible]); + const fetchThemes = async () => { try { const { themes, selectedTheme } = await listThemes(); @@ -121,14 +146,72 @@ const ThemeSelector: ForwardRefExoticComponent & [settingsState.selectedTheme] ); + const handleDragOver = (e: React.DragEvent) => { + e.preventDefault(); + setIsDragging(true); + }; + + const handleDragLeave = () => { + setIsDragging(false); + }; + + const handleDrop = (e: React.DragEvent) => { + e.preventDefault(); + setIsDragging(false); + const file: File = e.dataTransfer.files[0]; + const reader: FileReader = new FileReader(); + + reader.onload = async (event: ProgressEvent) => { + try { + const result: any = JSON.parse(event.target!.result as string); + try { + setTempTheme(result); + await InstallTheme(result); + await fetchThemes(); + setTempTheme(null); + } catch(error) { + toast.error('Invalid file type. Please upload a valid theme file.'); + setTempTheme(null); + } + } catch (error) { + toast.error('Error parsing file. Please upload a valid JSON theme file.'); + setTempTheme(null); + } + }; + + reader.readAsText(file); + }; + if (isLoading) { return
Loading themes...
; } return ( -
+
+
+
+
+
+ + + + + + + Drop theme here +
+
+
+

Themes

+ {themes.map((theme) => ( & /> ))} - { downloadedThemes.length + themes.length > 0 &&
+ +
+ )} + + {downloadedThemes.length + themes.length > 0 &&
} + >
}