add editing functionality

This commit is contained in:
SethBurkart123
2024-04-04 10:16:02 +11:00
parent 144addc7d4
commit 0abd8dadab
6 changed files with 132 additions and 33 deletions
+11 -3
View File
@@ -5,8 +5,17 @@ import { less } from '@codemirror/lang-less'
import { useCallback, useEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
import './CodeEditor.css' import './CodeEditor.css'
export default function CodeEditor({ callback, initialState, height, className }: { callback: (value: string) => void, initialState: string, height: string, className?: string}) { export default function CodeEditor({
const [value, setValue] = useState(initialState) className = '',
height = '100%',
value,
setValue
}: {
className?: string;
height?: string;
value: string;
setValue: (value: string) => void;
}) {
const [darkMode, setDarkMode] = useState(false) const [darkMode, setDarkMode] = useState(false)
useEffect(() => { useEffect(() => {
@@ -17,7 +26,6 @@ export default function CodeEditor({ callback, initialState, height, className }
const onChange = useCallback((value: string, _: ViewUpdate) => { const onChange = useCallback((value: string, _: ViewUpdate) => {
setValue(value) setValue(value)
callback(value)
}, []) }, [])
return( return(
+10
View File
@@ -1,5 +1,7 @@
import React from 'react'; import React from 'react';
import { CustomTheme } from '../types/CustomThemes'; import { CustomTheme } from '../types/CustomThemes';
import browser from 'webextension-polyfill';
import { PencilIcon } from '@heroicons/react/24/outline';
type ThemeCoverProps = { type ThemeCoverProps = {
theme: Omit<CustomTheme, 'CustomImages'>; theme: Omit<CustomTheme, 'CustomImages'>;
@@ -40,6 +42,14 @@ export const ThemeCover: React.FC<ThemeCoverProps> = ({
<div className="w-4 h-0.5 bg-white"></div> <div className="w-4 h-0.5 bg-white"></div>
</div> </div>
)} )}
{/* edit button */}
<div
className="absolute top-0 right-0 z-10 flex w-6 h-6 p-2 text-white translate-x-1/2 -translate-y-1/2 bg-blue-600 rounded-full place-items-center"
onClick={() => browser.runtime.sendMessage({ type: 'currentTab', info: 'OpenThemeCreator', body: { themeID: theme.id } })}
>
<PencilIcon className="w-4 h-4" />
</div>
<div className="relative top-0 z-10 flex justify-center w-full h-full overflow-hidden transition dark:text-white rounded-xl group place-items-center bg-zinc-100 dark:bg-zinc-900"> <div className="relative top-0 z-10 flex justify-center w-full h-full overflow-hidden transition dark:text-white rounded-xl group place-items-center bg-zinc-100 dark:bg-zinc-900">
{/* Render theme cover image or placeholder */} {/* Render theme cover image or placeholder */}
{/* {theme.CustomImages.length > 0 ? ( {/* {theme.CustomImages.length > 0 ? (
+57 -10
View File
@@ -5,11 +5,11 @@ import Accordion from '../components/Accordian';
import Switch from '../components/Switch'; import Switch from '../components/Switch';
import { sendThemeUpdate } from '../hooks/ThemeManagment'; import { sendThemeUpdate } from '../hooks/ThemeManagment';
import { PlusIcon, XMarkIcon } from '@heroicons/react/24/outline'; import { PlusIcon, XMarkIcon } from '@heroicons/react/24/outline';
import localforage from 'localforage';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
import { CustomTheme } from '../types/CustomThemes'; import { CustomTheme, CustomThemeBase64 } from '../types/CustomThemes';
import browser from 'webextension-polyfill';
function ThemeCreator({ themeID }: { themeID?: string }) { function ThemeCreator() {
const [theme, setTheme] = useState<CustomTheme>({ const [theme, setTheme] = useState<CustomTheme>({
id: uuidv4(), id: uuidv4(),
name: '', name: '',
@@ -17,18 +17,65 @@ function ThemeCreator({ themeID }: { themeID?: string }) {
defaultColour: '', defaultColour: '',
CanChangeColour: true, CanChangeColour: true,
CustomCSS: '', CustomCSS: '',
CustomImages: [] CustomImages: [],
}); });
useEffect(() => { useEffect(() => {
if (themeID) { const getTheme = async (themeID: string) => {
localforage.getItem(themeID).then((theme) => { const theme = await browser.runtime.sendMessage({
type: 'currentTab',
info: 'GetTheme',
body: {
themeID: themeID,
}
}) as CustomThemeBase64 | undefined;
if (theme) { if (theme) {
setTheme(theme as CustomTheme); // base64toblob to convert it to a blob url
const CustomImages = theme.CustomImages.map((image) => {
const base64Index = image.url.indexOf(',') + 1;
const imageBase64 = image.url.substring(base64Index);
// Convert base64 to blob
const byteCharacters = atob(imageBase64);
const byteNumbers = new Uint8Array(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' });
return {
id: image.id,
blob: blob,
variableName: image.variableName,
};
}); });
setTheme({
...theme,
CustomImages,
});
sendThemeUpdate({
...theme,
CustomImages: CustomImages,
}, false, true);
} }
});
};
// get ThemeID from URL params
const urlParams = new URLSearchParams(window.location.search);
const themeID = urlParams.get('themeID');
console.log('ThemeID:', themeID);
if (themeID) {
getTheme(themeID);
}
}, []);
const generateImageId = () => { const generateImageId = () => {
return '_' + Math.random().toString(36).substr(2, 9); return '_' + Math.random().toString(36).substr(2, 9);
@@ -173,8 +220,8 @@ function ThemeCreator({ themeID }: { themeID?: string }) {
<CodeEditor <CodeEditor
className='mt-2' className='mt-2'
height='300px' height='300px'
initialState={theme.CustomCSS} value={theme.CustomCSS}
callback={CodeUpdate} /> setValue={CodeUpdate} />
</Accordion> </Accordion>
<Divider /> <Divider />
+3 -2
View File
@@ -2,15 +2,16 @@ import browser from "webextension-polyfill";
/** /**
* Open the Theme Creator sidebar, it is an embedded page loaded similar to the extension popup * Open the Theme Creator sidebar, it is an embedded page loaded similar to the extension popup
* @param themeID - The ID of the theme to load in the Theme Creator
* @returns void * @returns void
*/ */
export function OpenThemeCreator() { export function OpenThemeCreator( themeID: string = '' ) {
CloseThemeCreator(); CloseThemeCreator();
const width = '310px'; const width = '310px';
const themeCreatorIframe: HTMLIFrameElement = document.createElement('iframe'); const themeCreatorIframe: HTMLIFrameElement = document.createElement('iframe');
themeCreatorIframe.src = `${browser.runtime.getURL('src/interface/index.html')}#themeCreator`; themeCreatorIframe.src = `${browser.runtime.getURL('src/interface/index.html')}${ themeID != '' ? `?themeID=${themeID}` : '' }#themeCreator`;
themeCreatorIframe.id = 'themeCreatorIframe'; themeCreatorIframe.id = 'themeCreatorIframe';
themeCreatorIframe.setAttribute('allowTransparency', 'true'); themeCreatorIframe.setAttribute('allowTransparency', 'true');
themeCreatorIframe.setAttribute('excludeDarkCheck', 'true'); themeCreatorIframe.setAttribute('excludeDarkCheck', 'true');
+41 -4
View File
@@ -152,7 +152,7 @@ export const UpdateImageData = (imageData2: { id: string; base64: string }) => {
const { id, base64 } = imageData2; const { id, base64 } = imageData2;
if (imageData[id]) { if (imageData[id]) {
imageData[id].url = updateImage({ id, url: base64, variableName: imageData[id].variableName }); imageData[id].url = base64toblob(base64);
const { variableName } = imageData[id]; const { variableName } = imageData[id];
document.documentElement.style.setProperty('--' + variableName, `url(${imageData[id].url})`); document.documentElement.style.setProperty('--' + variableName, `url(${imageData[id].url})`);
} }
@@ -172,10 +172,10 @@ function removeImageFromDocument(variableName: string) {
document.documentElement.style.removeProperty('--' + variableName); document.documentElement.style.removeProperty('--' + variableName);
} }
export function updateImage(image: CustomImageBase64) { export function base64toblob(base64: string) {
// Extract base64 data from the data URI // Extract base64 data from the data URI
const base64Index = image.url.indexOf(',') + 1; const base64Index = base64.indexOf(',') + 1;
const imageBase64 = image.url.substring(base64Index); const imageBase64 = base64.substring(base64Index);
// Convert base64 to blob // Convert base64 to blob
const byteCharacters = atob(imageBase64); const byteCharacters = atob(imageBase64);
@@ -233,6 +233,43 @@ const removeTheme = (theme: CustomTheme) => {
}); });
}; };
const blobToBase64 = (blob: Blob) => {
return new Promise<string>((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
const base64 = reader.result as string;
resolve(base64);
};
reader.onerror = reject;
reader.readAsDataURL(blob);
});
};
export const getTheme = async (themeId: string): Promise<CustomThemeBase64 | null> => {
try {
const theme = await localforage.getItem(themeId) as CustomTheme;
const CustomImages: CustomImageBase64[] = await Promise.all(
theme.CustomImages.map(async (image) => {
const base64 = await blobToBase64(image.blob);
return {
id: image.id,
variableName: image.variableName,
url: base64,
};
})
);
return {
...theme,
CustomImages,
};
} catch (error) {
console.error('Error getting theme:', error);
return null;
}
}
export const setTheme = async (themeId: string) => { export const setTheme = async (themeId: string) => {
try { try {
const enabledTheme = await browser.storage.local.get('selectedTheme') as { selectedTheme: string }; const enabledTheme = await browser.storage.local.get('selectedTheme') as { selectedTheme: string };
+7 -11
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 { UpdateImageData, UpdateThemePreview, deleteTheme, disableTheme, getAvailableThemes, saveTheme, setTheme } from '../../ui/Themes'; import { UpdateImageData, UpdateThemePreview, deleteTheme, disableTheme, getAvailableThemes, getTheme, saveTheme, setTheme } from '../../ui/Themes';
import { CloseThemeCreator, OpenThemeCreator } from '../../ui/ThemeCreator'; import { CloseThemeCreator, OpenThemeCreator } from '../../ui/ThemeCreator';
export class MessageHandler { export class MessageHandler {
@@ -29,16 +29,11 @@ export class MessageHandler {
} }
break; break;
case 'UpdateThemeImageData': case 'GetTheme':
UpdateImageData(request.body); getTheme(request.body.themeID).then((theme) => {
sendResponse({ status: 'success' }); sendResponse(theme);
break;
case 'SaveTheme':
saveTheme(request.body).then(() => {
sendResponse({ status: 'success' });
}); });
break; return true;
case 'SetTheme': case 'SetTheme':
setTheme(request.body.themeID).then(() => { setTheme(request.body.themeID).then(() => {
@@ -71,7 +66,8 @@ export class MessageHandler {
break; break;
case 'OpenThemeCreator': case 'OpenThemeCreator':
OpenThemeCreator(); const themeID = request?.body?.themeID;
OpenThemeCreator( themeID ? themeID : '' );
closeSettings(); closeSettings();
sendResponse({ status: 'success' }); sendResponse({ status: 'success' });
break; break;