add multi image upload

This commit is contained in:
SethBurkart123
2024-03-31 20:50:52 +11:00
parent 8d55b878da
commit 93e97d3c21
6 changed files with 153 additions and 16 deletions
+2
View File
@@ -40,6 +40,7 @@
"@sentry/vite-plugin": "^2.16.0", "@sentry/vite-plugin": "^2.16.0",
"@types/color": "^3.0.6", "@types/color": "^3.0.6",
"@types/dompurify": "^3.0.5", "@types/dompurify": "^3.0.5",
"@types/lodash": "^4.17.0",
"@types/node": "^20.11.30", "@types/node": "^20.11.30",
"@types/react": "^18.2.55", "@types/react": "^18.2.55",
"@types/react-dom": "^18.2.19", "@types/react-dom": "^18.2.19",
@@ -55,6 +56,7 @@
"dompurify": "^3.0.8", "dompurify": "^3.0.8",
"framer-motion": "^10.18.0", "framer-motion": "^10.18.0",
"localforage": "^1.10.0", "localforage": "^1.10.0",
"lodash": "^4.17.21",
"million": "latest", "million": "latest",
"motion": "^10.17.0", "motion": "^10.17.0",
"npm": "^10.4.0", "npm": "^10.4.0",
+10
View File
@@ -1,3 +1,4 @@
import { debounce } from 'lodash';
import browser from 'webextension-polyfill' import browser from 'webextension-polyfill'
interface ThemeList { interface ThemeList {
themes: string[]; themes: string[];
@@ -54,3 +55,12 @@ export const deleteTheme = async (themeName: string) => {
} }
}); });
} }
export const sendThemeUpdate = debounce((updatedTheme: CustomTheme) => {
// Send the updated theme to the content script for live preview
browser.runtime.sendMessage({
type: 'currentTab',
info: 'UpdateThemePreview',
body: updatedTheme,
});
}, 100);
+67 -11
View File
@@ -1,9 +1,9 @@
import CodeEditor from '../components/CodeEditor'; import CodeEditor from '../components/CodeEditor';
import { useState } from 'react'; import { useEffect, useState } from 'react';
import ColorPicker from 'react-best-gradient-color-picker'; import ColorPicker from 'react-best-gradient-color-picker';
import { SettingsContextProvider } from '../SettingsContext';
import Accordion from '../components/Accordian'; import Accordion from '../components/Accordian';
import Switch from '../components/Switch'; import Switch from '../components/Switch';
import { sendThemeUpdate } from '../hooks/ThemeManagment';
export default function ThemeCreator() { export default function ThemeCreator() {
const [theme, setTheme] = useState<CustomTheme>({ const [theme, setTheme] = useState<CustomTheme>({
@@ -15,15 +15,56 @@ export default function ThemeCreator() {
CustomImages: [] CustomImages: []
}); });
function saveTheme() { const generateImageId = () => {
console.log(theme); return '_' + Math.random().toString(36).substr(2, 9);
};
const handleImageUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (file) {
const reader = new FileReader();
reader.onload = () => {
const imageUrl = reader.result as string;
const imageId = generateImageId();
const variableName = `--custom-image-${theme.CustomImages.length}`;
const updatedTheme = {
...theme,
CustomImages: [...theme.CustomImages, { id: imageId, url: imageUrl, variableName }],
};
setTheme(updatedTheme);
sendThemeUpdate(updatedTheme);
};
reader.readAsDataURL(file);
} }
};
const handleRemoveImage = (imageId: string) => {
const updatedTheme = {
...theme,
CustomImages: theme.CustomImages.filter((image) => image.id !== imageId),
};
setTheme(updatedTheme);
};
const handleImageVariableChange = (imageId: string, variableName: string) => {
const updatedTheme = {
...theme,
CustomImages: theme.CustomImages.map((image) =>
image.id === imageId ? { ...image, variableName } : image
),
};
setTheme(updatedTheme);
};
function CodeUpdate(value: string) { function CodeUpdate(value: string) {
console.log(value); const updatedTheme = { ...theme, CustomCSS: value };
setTheme((previousTheme) => ({ ...previousTheme, CustomCSS: value })); setTheme(updatedTheme);
} }
useEffect(() => {
sendThemeUpdate(theme);
}, [theme]);
return ( return (
<div className='w-full min-h-[100vh] bg-zinc-100 dark:bg-zinc-800 dark:text-white transition duration-30'> <div className='w-full min-h-[100vh] bg-zinc-100 dark:bg-zinc-800 dark:text-white transition duration-30'>
<div className='flex flex-col p-2'> <div className='flex flex-col p-2'>
@@ -73,9 +114,26 @@ export default function ThemeCreator() {
<div className='h-4'></div> <div className='h-4'></div>
<Accordion defaultOpened title='Custom Images'> <Accordion defaultOpened title='Custom Images'>
child {theme.CustomImages.map((image, index) => (
<div key={image.id}>
<img src={image.url} alt={`Custom Image ${index + 1}`} />
<input
type='text'
value={image.variableName}
onChange={(e) => handleImageVariableChange(image.id, e.target.value)}
placeholder='CSS Variable Name'
/>
<button onClick={() => handleRemoveImage(image.id)}>Remove</button>
</div>
))}
<input
type='file'
accept='image/*'
onChange={handleImageUpload}
/>
</Accordion> </Accordion>
<div className='h-4'></div> <div className='h-4'></div>
<Accordion defaultOpened title='Custom CSS'> <Accordion defaultOpened title='Custom CSS'>
@@ -86,11 +144,9 @@ export default function ThemeCreator() {
callback={CodeUpdate} /> callback={CodeUpdate} />
</Accordion> </Accordion>
<button onClick={saveTheme} className='w-full px-4 py-2 my-4 text-white transition bg-blue-500 rounded dark:text-white'> <button onClick={() => console.log('shared!')} className='w-full px-4 py-2 my-4 text-white transition bg-blue-500 rounded dark:text-white'>
Save Theme Share theme
</button> </button>
<SettingsContextProvider><></></SettingsContextProvider>
</div> </div>
</div> </div>
); );
+7 -1
View File
@@ -4,5 +4,11 @@ type CustomTheme = {
defaultColour: string; defaultColour: string;
CanChangeColour: boolean; CanChangeColour: boolean;
CustomCSS: string; CustomCSS: string;
CustomImages: string[]; CustomImages: CustomImage[];
}
type CustomImage = {
id: string;
url: string;
variableName: string;
} }
+58
View File
@@ -160,3 +160,61 @@ export const disableTheme = async () => {
// Clear the selected theme from localforage // Clear the selected theme from localforage
localforage.removeItem('selectedTheme'); localforage.removeItem('selectedTheme');
}; };
let imageData: CustomImage[] = [];
let previousTheme: CustomTheme = null;
export const UpdateThemePreview = async (updatedTheme: CustomTheme) => {
console.log(updatedTheme)
if (updatedTheme.CustomImages.length !== imageData.length) {
updatedTheme.CustomImages.forEach((image) => {
updateImage(image.id, image.url);
})
}
const { CustomCSS, CustomImages, defaultColour } = updatedTheme;
// Apply custom CSS
let styleElement = document.getElementById('theme-preview-styles');
if (!styleElement) {
styleElement = document.createElement('style');
styleElement.id = 'theme-preview-styles';
document.head.appendChild(styleElement);
}
styleElement.textContent = CustomCSS;
// Apply default color
if (defaultColour !== '') {
browser.storage.local.set({ selectedColor: defaultColour });
}
CustomImages.forEach((image) => {
// @ts-expect-error - not sure why its yelling at me :(
const imageUrl = imageData[image.id];
if (imageUrl) {
document.documentElement.style.setProperty(image.variableName, `url(${imageUrl})`);
}
});
}
export function updateImage(imageId: string, imageDataURI: string) {
// Extract base64 data from the data URI
const base64Index = imageDataURI.indexOf(',') + 1;
const imageBase64 = imageDataURI.substring(base64Index);
// Convert base64 to blob
const byteCharacters = atob(imageBase64);
const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
const blob = new Blob([byteArray], { type: 'image/png' });
// Convert blob to blob URL
const imageUrl = URL.createObjectURL(blob);
// @ts-expect-error - same problem 😭
imageData[imageId] = imageUrl;
}
+6 -1
View File
@@ -1,7 +1,7 @@
import browser from 'webextension-polyfill' import browser from 'webextension-polyfill'
import { MenuOptionsOpen, OpenMenuOptions, OpenWhatsNewPopup, closeSettings } from '../../../SEQTA'; import { MenuOptionsOpen, OpenMenuOptions, OpenWhatsNewPopup, closeSettings } from '../../../SEQTA';
import { deleteTheme, disableTheme, downloadTheme, listThemes, setTheme } from '../../ui/Themes'; import { deleteTheme, disableTheme, downloadTheme, listThemes, setTheme, updateImage, UpdateThemePreview } from '../../ui/Themes';
import { OpenThemeCreator } from '../../ui/ThemeCreator'; import { OpenThemeCreator } from '../../ui/ThemeCreator';
export class MessageHandler { export class MessageHandler {
@@ -44,6 +44,11 @@ export class MessageHandler {
sendResponse({ status: 'success' }); sendResponse({ status: 'success' });
}); });
return true; return true;
case 'UpdateThemePreview':
UpdateThemePreview(request.body).then(() => {
sendResponse({ status: 'success' });
});
break;
case 'OpenChangelog': case 'OpenChangelog':
OpenWhatsNewPopup(); OpenWhatsNewPopup();
closeSettings(); closeSettings();