mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-06-06 11:44:40 +00:00
add theme transfer
This commit is contained in:
@@ -1,18 +1,21 @@
|
||||
import React, { useState } from 'react';
|
||||
import { CustomTheme } from '../types/CustomThemes';
|
||||
import { CustomTheme, DownloadedTheme } from '../types/CustomThemes';
|
||||
import browser from 'webextension-polyfill';
|
||||
import { ArrowUpOnSquareIcon, PencilIcon } from '@heroicons/react/24/outline';
|
||||
import { sendThemeUpdate } from '../hooks/ThemeManagment';
|
||||
|
||||
type ThemeCoverProps = {
|
||||
theme: Omit<CustomTheme, 'CustomImages'>;
|
||||
theme: Omit<CustomTheme, 'CustomImages'> | DownloadedTheme;
|
||||
isSelected: boolean;
|
||||
isEditMode: boolean;
|
||||
downloaded?: boolean;
|
||||
onThemeSelect: (themeId: string) => void;
|
||||
onThemeDelete: (themeId: string) => void;
|
||||
};
|
||||
|
||||
export const ThemeCover: React.FC<ThemeCoverProps> = ({
|
||||
theme,
|
||||
downloaded,
|
||||
isSelected,
|
||||
isEditMode,
|
||||
onThemeSelect,
|
||||
@@ -21,7 +24,12 @@ export const ThemeCover: React.FC<ThemeCoverProps> = ({
|
||||
const [uploading, setUploading] = useState<boolean>(false);
|
||||
const handleThemeClick = () => {
|
||||
if (isEditMode) return;
|
||||
onThemeSelect(theme.id);
|
||||
if (downloaded) {
|
||||
sendThemeUpdate(theme as DownloadedTheme, true)
|
||||
} else {
|
||||
console.log(theme)
|
||||
onThemeSelect(theme.id);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteClick = (e: React.MouseEvent) => {
|
||||
@@ -54,7 +62,7 @@ export const ThemeCover: React.FC<ThemeCoverProps> = ({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{ isEditMode ? <></> :
|
||||
{ !isEditMode || !downloaded &&
|
||||
<>
|
||||
<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]"
|
||||
@@ -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">
|
||||
{theme.coverImage &&
|
||||
<img
|
||||
src={theme.coverImage as string}
|
||||
src={(typeof theme.coverImage) == 'string' ? theme.coverImage as string : URL.createObjectURL(theme.coverImage as Blob)}
|
||||
alt={theme.name}
|
||||
className="absolute inset-0 z-0 object-cover"
|
||||
/>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
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 Browser from 'webextension-polyfill';
|
||||
import { CustomTheme } from '../types/CustomThemes';
|
||||
import { CustomTheme, DownloadedTheme } from '../types/CustomThemes';
|
||||
import { useSettingsContext } from '../SettingsContext';
|
||||
import { SettingsState } from '../types/AppProps';
|
||||
|
||||
@@ -13,6 +13,7 @@ interface ThemeSelectorProps {
|
||||
|
||||
const ThemeSelector: ForwardRefExoticComponent<Omit<ThemeSelectorProps, "ref"> & RefAttributes<any>> = forwardRef(({ isEditMode = false }, ref) => {
|
||||
const [themes, setThemes] = useState<Omit<CustomTheme, 'CustomImages'>[]>([]);
|
||||
const [downloadedThemes, setDownloadedThemes] = useState<DownloadedTheme[]>([]);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
const { settingsState, setSettingsState } = useSettingsContext();
|
||||
|
||||
@@ -56,6 +57,7 @@ const ThemeSelector: ForwardRefExoticComponent<Omit<ThemeSelectorProps, "ref"> &
|
||||
const { themes, selectedTheme } = await listThemes();
|
||||
|
||||
setThemes(themes);
|
||||
setDownloadedThemes(await getDownloadedThemes());
|
||||
setSelectedTheme(selectedTheme ? selectedTheme : '');
|
||||
} catch (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
|
||||
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"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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) => {
|
||||
// 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> => {
|
||||
// send message to the background script
|
||||
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;
|
||||
|
||||
const imageDataPromises = updatedTheme.CustomImages.map(async (image) => {
|
||||
@@ -97,7 +121,7 @@ export const sendThemeUpdate = (updatedTheme: CustomTheme, saveTheme?: boolean,
|
||||
CustomImages: imageData,
|
||||
};
|
||||
|
||||
if (saveTheme && updatedTheme.coverImage instanceof Blob) {
|
||||
if (saveTheme && updatedTheme.coverImage) {
|
||||
themeData.coverImage = await blobToBase64(updatedTheme.coverImage as Blob);
|
||||
} else {
|
||||
themeData.coverImage = null;
|
||||
|
||||
@@ -12,6 +12,10 @@ export type CustomTheme = {
|
||||
hideThemeName: boolean;
|
||||
}
|
||||
|
||||
export type DownloadedTheme = CustomTheme & {
|
||||
webURL: string;
|
||||
}
|
||||
|
||||
export type CustomImage = {
|
||||
id: string;
|
||||
blob: Blob;
|
||||
|
||||
Reference in New Issue
Block a user