add theme transfer

This commit is contained in:
SethBurkart123
2024-04-22 11:09:57 +10:00
parent 0674f72578
commit 8cd73977bb
5 changed files with 67 additions and 14 deletions
+12 -4
View File
@@ -1,18 +1,21 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { CustomTheme } from '../types/CustomThemes'; import { CustomTheme, DownloadedTheme } from '../types/CustomThemes';
import browser from 'webextension-polyfill'; import browser from 'webextension-polyfill';
import { ArrowUpOnSquareIcon, PencilIcon } from '@heroicons/react/24/outline'; import { ArrowUpOnSquareIcon, PencilIcon } from '@heroicons/react/24/outline';
import { sendThemeUpdate } from '../hooks/ThemeManagment';
type ThemeCoverProps = { type ThemeCoverProps = {
theme: Omit<CustomTheme, 'CustomImages'>; theme: Omit<CustomTheme, 'CustomImages'> | DownloadedTheme;
isSelected: boolean; isSelected: boolean;
isEditMode: boolean; isEditMode: boolean;
downloaded?: boolean;
onThemeSelect: (themeId: string) => void; onThemeSelect: (themeId: string) => void;
onThemeDelete: (themeId: string) => void; onThemeDelete: (themeId: string) => void;
}; };
export const ThemeCover: React.FC<ThemeCoverProps> = ({ export const ThemeCover: React.FC<ThemeCoverProps> = ({
theme, theme,
downloaded,
isSelected, isSelected,
isEditMode, isEditMode,
onThemeSelect, onThemeSelect,
@@ -21,7 +24,12 @@ export const ThemeCover: React.FC<ThemeCoverProps> = ({
const [uploading, setUploading] = useState<boolean>(false); const [uploading, setUploading] = useState<boolean>(false);
const handleThemeClick = () => { const handleThemeClick = () => {
if (isEditMode) return; if (isEditMode) return;
if (downloaded) {
sendThemeUpdate(theme as DownloadedTheme, true)
} else {
console.log(theme)
onThemeSelect(theme.id); onThemeSelect(theme.id);
}
}; };
const handleDeleteClick = (e: React.MouseEvent) => { const handleDeleteClick = (e: React.MouseEvent) => {
@@ -54,7 +62,7 @@ export const ThemeCover: React.FC<ThemeCoverProps> = ({
</div> </div>
)} )}
{ isEditMode ? <></> : { !isEditMode || !downloaded &&
<> <>
<div <div
className="absolute z-20 flex w-8 h-8 p-2 text-white transition-all rounded-full delay-[20ms] opacity-0 top-1 right-2 dark:bg-black/50 place-items-center group-hover:opacity-100 group-hover:top-[1.25rem]" className="absolute z-20 flex w-8 h-8 p-2 text-white transition-all rounded-full delay-[20ms] opacity-0 top-1 right-2 dark:bg-black/50 place-items-center group-hover:opacity-100 group-hover:top-[1.25rem]"
@@ -75,7 +83,7 @@ export const ThemeCover: React.FC<ThemeCoverProps> = ({
<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">
{theme.coverImage && {theme.coverImage &&
<img <img
src={theme.coverImage as string} src={(typeof theme.coverImage) == 'string' ? theme.coverImage as string : URL.createObjectURL(theme.coverImage as Blob)}
alt={theme.name} alt={theme.name}
className="absolute inset-0 z-0 object-cover" className="absolute inset-0 z-0 object-cover"
/> />
+16 -2
View File
@@ -1,8 +1,8 @@
import React, { useEffect, useState, useCallback, forwardRef, useImperativeHandle, ForwardRefExoticComponent, RefAttributes } from 'react'; import React, { useEffect, useState, useCallback, forwardRef, useImperativeHandle, ForwardRefExoticComponent, RefAttributes } from 'react';
import { listThemes, deleteTheme, setTheme, disableTheme } from '../hooks/ThemeManagment'; import { listThemes, deleteTheme, setTheme, disableTheme, getDownloadedThemes } from '../hooks/ThemeManagment';
import { ThemeCover } from './ThemeCover'; import { ThemeCover } from './ThemeCover';
import Browser from 'webextension-polyfill'; import Browser from 'webextension-polyfill';
import { CustomTheme } from '../types/CustomThemes'; import { CustomTheme, DownloadedTheme } from '../types/CustomThemes';
import { useSettingsContext } from '../SettingsContext'; import { useSettingsContext } from '../SettingsContext';
import { SettingsState } from '../types/AppProps'; import { SettingsState } from '../types/AppProps';
@@ -13,6 +13,7 @@ interface ThemeSelectorProps {
const ThemeSelector: ForwardRefExoticComponent<Omit<ThemeSelectorProps, "ref"> & RefAttributes<any>> = forwardRef(({ isEditMode = false }, ref) => { const ThemeSelector: ForwardRefExoticComponent<Omit<ThemeSelectorProps, "ref"> & RefAttributes<any>> = forwardRef(({ isEditMode = false }, ref) => {
const [themes, setThemes] = useState<Omit<CustomTheme, 'CustomImages'>[]>([]); const [themes, setThemes] = useState<Omit<CustomTheme, 'CustomImages'>[]>([]);
const [downloadedThemes, setDownloadedThemes] = useState<DownloadedTheme[]>([]);
const [isLoading, setIsLoading] = useState<boolean>(true); const [isLoading, setIsLoading] = useState<boolean>(true);
const { settingsState, setSettingsState } = useSettingsContext(); const { settingsState, setSettingsState } = useSettingsContext();
@@ -56,6 +57,7 @@ const ThemeSelector: ForwardRefExoticComponent<Omit<ThemeSelectorProps, "ref"> &
const { themes, selectedTheme } = await listThemes(); const { themes, selectedTheme } = await listThemes();
setThemes(themes); setThemes(themes);
setDownloadedThemes(await getDownloadedThemes());
setSelectedTheme(selectedTheme ? selectedTheme : ''); setSelectedTheme(selectedTheme ? selectedTheme : '');
} catch (error) { } catch (error) {
console.error('Error fetching themes:', error); console.error('Error fetching themes:', error);
@@ -118,6 +120,18 @@ const ThemeSelector: ForwardRefExoticComponent<Omit<ThemeSelectorProps, "ref"> &
/> />
))} ))}
{downloadedThemes.map((theme) => (
<ThemeCover
key={theme.id}
downloaded={true}
theme={theme}
isSelected={theme.id === settingsState.selectedTheme}
isEditMode={isEditMode}
onThemeSelect={handleThemeSelect}
onThemeDelete={handleThemeDelete}
/>
))}
<button <button
onClick={() => Browser.tabs.create({ url: Browser.runtime.getURL('src/interface/index.html#store')})} onClick={() => Browser.tabs.create({ url: Browser.runtime.getURL('src/interface/index.html#store')})}
className="flex items-center justify-center w-full transition aspect-theme rounded-xl bg-zinc-100 dark:bg-zinc-900 dark:text-white" className="flex items-center justify-center w-full transition aspect-theme rounded-xl bg-zinc-100 dark:bg-zinc-900 dark:text-white"
+27 -3
View File
@@ -1,5 +1,6 @@
import browser from 'webextension-polyfill' import browser from 'webextension-polyfill'
import { CustomTheme, ThemeList } from '../types/CustomThemes'; import { CustomTheme, DownloadedTheme, ThemeList } from '../types/CustomThemes';
import localforage from 'localforage';
export const downloadTheme = async (themeName: string, themeURL: string) => { export const downloadTheme = async (themeName: string, themeURL: string) => {
// send message to the background script // send message to the background script
@@ -24,6 +25,29 @@ export const setTheme = async (themeID: string) => {
}); });
} }
export const getDownloadedThemes = async (): Promise<DownloadedTheme[]> => {
// send message to the background script
const response: DownloadedTheme[] = await new Promise(async (resolve, reject) => {
try {
let availableThemes = await localforage.getItem('availableThemes') as string[];
availableThemes = Array.from(new Set(availableThemes));
const downloadedThemes: DownloadedTheme[] = [];
for (let i = 0; i < availableThemes.length; i++) {
let themeData = await localforage.getItem(availableThemes[i]) as DownloadedTheme;
downloadedThemes.push(themeData);
}
resolve(downloadedThemes);
} catch(error) {
reject(error);
}
});
return response;
}
export const listThemes = async (): Promise<ThemeList> => { export const listThemes = async (): Promise<ThemeList> => {
// send message to the background script // send message to the background script
const response: ThemeList = await new Promise((resolve, reject) => { const response: ThemeList = await new Promise((resolve, reject) => {
@@ -72,7 +96,7 @@ export const deleteTheme = async (themeID: string) => {
}); });
} }
export const sendThemeUpdate = (updatedTheme: CustomTheme, saveTheme?: boolean, updateImages?: boolean) => { export const sendThemeUpdate = (updatedTheme: CustomTheme | DownloadedTheme, saveTheme?: boolean, updateImages?: boolean) => {
saveTheme = saveTheme || false; saveTheme = saveTheme || false;
const imageDataPromises = updatedTheme.CustomImages.map(async (image) => { const imageDataPromises = updatedTheme.CustomImages.map(async (image) => {
@@ -97,7 +121,7 @@ export const sendThemeUpdate = (updatedTheme: CustomTheme, saveTheme?: boolean,
CustomImages: imageData, CustomImages: imageData,
}; };
if (saveTheme && updatedTheme.coverImage instanceof Blob) { if (saveTheme && updatedTheme.coverImage) {
themeData.coverImage = await blobToBase64(updatedTheme.coverImage as Blob); themeData.coverImage = await blobToBase64(updatedTheme.coverImage as Blob);
} else { } else {
themeData.coverImage = null; themeData.coverImage = null;
+4
View File
@@ -12,6 +12,10 @@ export type CustomTheme = {
hideThemeName: boolean; hideThemeName: boolean;
} }
export type DownloadedTheme = CustomTheme & {
webURL: string;
}
export type CustomImage = { export type CustomImage = {
id: string; id: string;
blob: Blob; blob: Blob;
+7 -4
View File
@@ -15,7 +15,9 @@ const DownloadTheme = async (theme: ThemesResponse & { theme: CustomTheme & { im
images.push({ imageData, imageID }); images.push({ imageData, imageID });
} }
console.log("Original Theme", theme); const coverImage = await fetch(
`https://betterseqta.pockethost.io/api/files/${theme.collectionId}/${theme.id}/${theme.coverImage}`
);
// add to temp storage index // add to temp storage index
let availableThemes = await localforage.getItem('availableThemes') as string[]; let availableThemes = await localforage.getItem('availableThemes') as string[];
@@ -26,13 +28,14 @@ const DownloadTheme = async (theme: ThemesResponse & { theme: CustomTheme & { im
} }
localforage.setItem('availableThemes', availableThemes); localforage.setItem('availableThemes', availableThemes);
// save the theme to the temp storage
localforage.setItem(theme.theme.id, { localforage.setItem(theme.theme.id, {
...theme.theme, ...theme.theme,
images: theme.theme.images.map((image) => { webURL: theme.id,
coverImage: await coverImage.blob(),
CustomImages: theme.theme.images.map((image) => {
return { return {
...image, ...image,
imageData: images.find((i) => i.imageID.split('_')[0] === image.id)?.imageData blob: images.find((i) => i.imageID.split('_')[0] === image.id)?.imageData
} }
}) })
}); });