mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-06-05 19:24:39 +00:00
add theme installer page
This commit is contained in:
@@ -67,9 +67,11 @@
|
|||||||
"preact": "^10.20.0",
|
"preact": "^10.20.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-best-gradient-color-picker": "3.0.5",
|
"react-best-gradient-color-picker": "3.0.5",
|
||||||
|
"react-confetti-explosion": "^2.1.2",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-router-dom": "^6.22.0",
|
"react-router-dom": "^6.22.0",
|
||||||
"react-shadow": "^20.4.0",
|
"react-shadow": "^20.4.0",
|
||||||
|
"react-toastify": "^10.0.5",
|
||||||
"rimraf": "^5.0.5",
|
"rimraf": "^5.0.5",
|
||||||
"sortablejs": "^1.15.2",
|
"sortablejs": "^1.15.2",
|
||||||
"swiper": "^11.1.0",
|
"swiper": "^11.1.0",
|
||||||
|
|||||||
+9
-3
@@ -1,5 +1,7 @@
|
|||||||
import * as Sentry from "@sentry/browser";
|
import * as Sentry from "@sentry/browser";
|
||||||
import browser from 'webextension-polyfill'
|
import browser from 'webextension-polyfill'
|
||||||
|
import { onError } from './seqta/utils/onError';
|
||||||
|
import { SettingsState } from "./types/storage";
|
||||||
|
|
||||||
browser.storage.local.get([ "telemetry" ]).then((telemetry) => {
|
browser.storage.local.get([ "telemetry" ]).then((telemetry) => {
|
||||||
if (telemetry.telemetry === true) {
|
if (telemetry.telemetry === true) {
|
||||||
@@ -14,8 +16,6 @@ browser.storage.local.get([ "telemetry" ]).then((telemetry) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
import { onError } from './seqta/utils/onError';
|
|
||||||
import { SettingsState } from "./types/storage";
|
|
||||||
export const openDB = () => {
|
export const openDB = () => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const request = indexedDB.open('MyDatabase', 1);
|
const request = indexedDB.open('MyDatabase', 1);
|
||||||
@@ -84,7 +84,13 @@ function reloadSeqtaPages() {
|
|||||||
result.then(open, onError)
|
result.then(open, onError)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to handle setting permissions
|
browser.tabs.onUpdated.addListener((tabId, _, tab) => {
|
||||||
|
if (tab.url?.includes('share.betterseqta')) {
|
||||||
|
const id = new URL(tab.url).searchParams.get('id');
|
||||||
|
const justCreated = new URL(tab.url).searchParams.get('justCreated');
|
||||||
|
browser.tabs.update(tabId, { url: `${browser.runtime.getURL('src/interface/index.html')}?id=${id}&justCreated=${justCreated}#theme` });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Main message listener
|
// Main message listener
|
||||||
browser.runtime.onMessage.addListener((request: any, _sender: any, sendResponse: any) => {
|
browser.runtime.onMessage.addListener((request: any, _sender: any, sendResponse: any) => {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import { CustomTheme } from '../types/CustomThemes';
|
import { CustomTheme } from '../types/CustomThemes';
|
||||||
import browser from 'webextension-polyfill';
|
import browser from 'webextension-polyfill';
|
||||||
import { ArrowUpOnSquareIcon, PencilIcon, ShareIcon } from '@heroicons/react/24/outline';
|
import { ArrowUpOnSquareIcon, PencilIcon } from '@heroicons/react/24/outline';
|
||||||
|
|
||||||
type ThemeCoverProps = {
|
type ThemeCoverProps = {
|
||||||
theme: Omit<CustomTheme, 'CustomImages'>;
|
theme: Omit<CustomTheme, 'CustomImages'>;
|
||||||
@@ -18,6 +18,7 @@ export const ThemeCover: React.FC<ThemeCoverProps> = ({
|
|||||||
onThemeSelect,
|
onThemeSelect,
|
||||||
onThemeDelete,
|
onThemeDelete,
|
||||||
}) => {
|
}) => {
|
||||||
|
const [uploading, setUploading] = useState<boolean>(false);
|
||||||
const handleThemeClick = () => {
|
const handleThemeClick = () => {
|
||||||
if (isEditMode) return;
|
if (isEditMode) return;
|
||||||
onThemeSelect(theme.id);
|
onThemeSelect(theme.id);
|
||||||
@@ -28,6 +29,15 @@ export const ThemeCover: React.FC<ThemeCoverProps> = ({
|
|||||||
onThemeDelete(theme.id);
|
onThemeDelete(theme.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleShareClick = (event: React.MouseEvent) => {
|
||||||
|
event?.preventDefault();
|
||||||
|
setUploading(true);
|
||||||
|
browser.runtime.sendMessage({ type: 'currentTab', info: 'ShareTheme', body: { themeID: theme.id } }).then((response) => {
|
||||||
|
setUploading(false);
|
||||||
|
browser.tabs.create({ url: `https://share.betterseqta/theme?id=${response.id}&justCreated=true` });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
className={`relative group w-full aspect-theme flex justify-center items-center rounded-xl transition ring dark:ring-white ring-zinc-300 ${
|
className={`relative group w-full aspect-theme flex justify-center items-center rounded-xl transition ring dark:ring-white ring-zinc-300 ${
|
||||||
@@ -55,9 +65,9 @@ export const ThemeCover: React.FC<ThemeCoverProps> = ({
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
className="absolute z-20 flex w-8 h-8 p-2 text-white transition-all rounded-full opacity-0 top-1 right-12 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 opacity-0 top-1 right-12 dark:bg-black/50 place-items-center group-hover:opacity-100 group-hover:top-[1.25rem]"
|
||||||
onClick={(event) => { event?.preventDefault(), browser.runtime.sendMessage({ type: 'currentTab', info: 'ShareTheme', body: { themeID: theme.id } }) }}
|
onClick={handleShareClick}
|
||||||
>
|
>
|
||||||
<ArrowUpOnSquareIcon className="w-4 h-4" />
|
{uploading ? <LoadingSpinner size={16} /> : <ArrowUpOnSquareIcon className="w-4 h-4" />}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
@@ -78,3 +88,8 @@ export const ThemeCover: React.FC<ThemeCoverProps> = ({
|
|||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const LoadingSpinner = ({ size }: { size: number }) => {
|
||||||
|
return <div style={{ width: `${size}px`, height: `${size}px` }} className={`animate-spin rounded-full border-2 border-white border-t-2 border-t-transparent`}></div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,141 @@
|
|||||||
|
"use client";
|
||||||
|
import React from "react";
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
import { cn } from "../utils/cn";
|
||||||
|
|
||||||
|
export const BackgroundBeams = React.memo(
|
||||||
|
({ className }: { className?: string }) => {
|
||||||
|
const paths = [
|
||||||
|
"M-380 -189C-380 -189 -312 216 152 343C616 470 684 875 684 875",
|
||||||
|
"M-373 -197C-373 -197 -305 208 159 335C623 462 691 867 691 867",
|
||||||
|
"M-366 -205C-366 -205 -298 200 166 327C630 454 698 859 698 859",
|
||||||
|
"M-359 -213C-359 -213 -291 192 173 319C637 446 705 851 705 851",
|
||||||
|
"M-352 -221C-352 -221 -284 184 180 311C644 438 712 843 712 843",
|
||||||
|
"M-345 -229C-345 -229 -277 176 187 303C651 430 719 835 719 835",
|
||||||
|
"M-338 -237C-338 -237 -270 168 194 295C658 422 726 827 726 827",
|
||||||
|
"M-331 -245C-331 -245 -263 160 201 287C665 414 733 819 733 819",
|
||||||
|
"M-324 -253C-324 -253 -256 152 208 279C672 406 740 811 740 811",
|
||||||
|
"M-317 -261C-317 -261 -249 144 215 271C679 398 747 803 747 803",
|
||||||
|
"M-310 -269C-310 -269 -242 136 222 263C686 390 754 795 754 795",
|
||||||
|
"M-303 -277C-303 -277 -235 128 229 255C693 382 761 787 761 787",
|
||||||
|
"M-296 -285C-296 -285 -228 120 236 247C700 374 768 779 768 779",
|
||||||
|
"M-289 -293C-289 -293 -221 112 243 239C707 366 775 771 775 771",
|
||||||
|
"M-282 -301C-282 -301 -214 104 250 231C714 358 782 763 782 763",
|
||||||
|
"M-275 -309C-275 -309 -207 96 257 223C721 350 789 755 789 755",
|
||||||
|
"M-268 -317C-268 -317 -200 88 264 215C728 342 796 747 796 747",
|
||||||
|
"M-261 -325C-261 -325 -193 80 271 207C735 334 803 739 803 739",
|
||||||
|
"M-254 -333C-254 -333 -186 72 278 199C742 326 810 731 810 731",
|
||||||
|
"M-247 -341C-247 -341 -179 64 285 191C749 318 817 723 817 723",
|
||||||
|
"M-240 -349C-240 -349 -172 56 292 183C756 310 824 715 824 715",
|
||||||
|
"M-233 -357C-233 -357 -165 48 299 175C763 302 831 707 831 707",
|
||||||
|
"M-226 -365C-226 -365 -158 40 306 167C770 294 838 699 838 699",
|
||||||
|
"M-219 -373C-219 -373 -151 32 313 159C777 286 845 691 845 691",
|
||||||
|
"M-212 -381C-212 -381 -144 24 320 151C784 278 852 683 852 683",
|
||||||
|
"M-205 -389C-205 -389 -137 16 327 143C791 270 859 675 859 675",
|
||||||
|
"M-198 -397C-198 -397 -130 8 334 135C798 262 866 667 866 667",
|
||||||
|
"M-191 -405C-191 -405 -123 0 341 127C805 254 873 659 873 659",
|
||||||
|
"M-184 -413C-184 -413 -116 -8 348 119C812 246 880 651 880 651",
|
||||||
|
"M-177 -421C-177 -421 -109 -16 355 111C819 238 887 643 887 643",
|
||||||
|
"M-170 -429C-170 -429 -102 -24 362 103C826 230 894 635 894 635",
|
||||||
|
"M-163 -437C-163 -437 -95 -32 369 95C833 222 901 627 901 627",
|
||||||
|
"M-156 -445C-156 -445 -88 -40 376 87C840 214 908 619 908 619",
|
||||||
|
"M-149 -453C-149 -453 -81 -48 383 79C847 206 915 611 915 611",
|
||||||
|
"M-142 -461C-142 -461 -74 -56 390 71C854 198 922 603 922 603",
|
||||||
|
"M-135 -469C-135 -469 -67 -64 397 63C861 190 929 595 929 595",
|
||||||
|
"M-128 -477C-128 -477 -60 -72 404 55C868 182 936 587 936 587",
|
||||||
|
"M-121 -485C-121 -485 -53 -80 411 47C875 174 943 579 943 579",
|
||||||
|
"M-114 -493C-114 -493 -46 -88 418 39C882 166 950 571 950 571",
|
||||||
|
"M-107 -501C-107 -501 -39 -96 425 31C889 158 957 563 957 563",
|
||||||
|
"M-100 -509C-100 -509 -32 -104 432 23C896 150 964 555 964 555",
|
||||||
|
"M-93 -517C-93 -517 -25 -112 439 15C903 142 971 547 971 547",
|
||||||
|
"M-86 -525C-86 -525 -18 -120 446 7C910 134 978 539 978 539",
|
||||||
|
"M-79 -533C-79 -533 -11 -128 453 -1C917 126 985 531 985 531",
|
||||||
|
"M-72 -541C-72 -541 -4 -136 460 -9C924 118 992 523 992 523",
|
||||||
|
"M-65 -549C-65 -549 3 -144 467 -17C931 110 999 515 999 515",
|
||||||
|
"M-58 -557C-58 -557 10 -152 474 -25C938 102 1006 507 1006 507",
|
||||||
|
"M-51 -565C-51 -565 17 -160 481 -33C945 94 1013 499 1013 499",
|
||||||
|
"M-44 -573C-44 -573 24 -168 488 -41C952 86 1020 491 1020 491",
|
||||||
|
"M-37 -581C-37 -581 31 -176 495 -49C959 78 1027 483 1027 483",
|
||||||
|
];
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"absolute h-full w-full inset-0 [mask-size:40px] [mask-repeat:no-repeat] flex items-center justify-center",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
className="absolute z-0 w-full h-full pointer-events-none "
|
||||||
|
width="100%"
|
||||||
|
height="100%"
|
||||||
|
viewBox="0 0 696 316"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M-380 -189C-380 -189 -312 216 152 343C616 470 684 875 684 875M-373 -197C-373 -197 -305 208 159 335C623 462 691 867 691 867M-366 -205C-366 -205 -298 200 166 327C630 454 698 859 698 859M-359 -213C-359 -213 -291 192 173 319C637 446 705 851 705 851M-352 -221C-352 -221 -284 184 180 311C644 438 712 843 712 843M-345 -229C-345 -229 -277 176 187 303C651 430 719 835 719 835M-338 -237C-338 -237 -270 168 194 295C658 422 726 827 726 827M-331 -245C-331 -245 -263 160 201 287C665 414 733 819 733 819M-324 -253C-324 -253 -256 152 208 279C672 406 740 811 740 811M-317 -261C-317 -261 -249 144 215 271C679 398 747 803 747 803M-310 -269C-310 -269 -242 136 222 263C686 390 754 795 754 795M-303 -277C-303 -277 -235 128 229 255C693 382 761 787 761 787M-296 -285C-296 -285 -228 120 236 247C700 374 768 779 768 779M-289 -293C-289 -293 -221 112 243 239C707 366 775 771 775 771M-282 -301C-282 -301 -214 104 250 231C714 358 782 763 782 763M-275 -309C-275 -309 -207 96 257 223C721 350 789 755 789 755M-268 -317C-268 -317 -200 88 264 215C728 342 796 747 796 747M-261 -325C-261 -325 -193 80 271 207C735 334 803 739 803 739M-254 -333C-254 -333 -186 72 278 199C742 326 810 731 810 731M-247 -341C-247 -341 -179 64 285 191C749 318 817 723 817 723M-240 -349C-240 -349 -172 56 292 183C756 310 824 715 824 715M-233 -357C-233 -357 -165 48 299 175C763 302 831 707 831 707M-226 -365C-226 -365 -158 40 306 167C770 294 838 699 838 699M-219 -373C-219 -373 -151 32 313 159C777 286 845 691 845 691M-212 -381C-212 -381 -144 24 320 151C784 278 852 683 852 683M-205 -389C-205 -389 -137 16 327 143C791 270 859 675 859 675M-198 -397C-198 -397 -130 8 334 135C798 262 866 667 866 667M-191 -405C-191 -405 -123 0 341 127C805 254 873 659 873 659M-184 -413C-184 -413 -116 -8 348 119C812 246 880 651 880 651M-177 -421C-177 -421 -109 -16 355 111C819 238 887 643 887 643M-170 -429C-170 -429 -102 -24 362 103C826 230 894 635 894 635M-163 -437C-163 -437 -95 -32 369 95C833 222 901 627 901 627M-156 -445C-156 -445 -88 -40 376 87C840 214 908 619 908 619M-149 -453C-149 -453 -81 -48 383 79C847 206 915 611 915 611M-142 -461C-142 -461 -74 -56 390 71C854 198 922 603 922 603M-135 -469C-135 -469 -67 -64 397 63C861 190 929 595 929 595M-128 -477C-128 -477 -60 -72 404 55C868 182 936 587 936 587M-121 -485C-121 -485 -53 -80 411 47C875 174 943 579 943 579M-114 -493C-114 -493 -46 -88 418 39C882 166 950 571 950 571M-107 -501C-107 -501 -39 -96 425 31C889 158 957 563 957 563M-100 -509C-100 -509 -32 -104 432 23C896 150 964 555 964 555M-93 -517C-93 -517 -25 -112 439 15C903 142 971 547 971 547M-86 -525C-86 -525 -18 -120 446 7C910 134 978 539 978 539M-79 -533C-79 -533 -11 -128 453 -1C917 126 985 531 985 531M-72 -541C-72 -541 -4 -136 460 -9C924 118 992 523 992 523M-65 -549C-65 -549 3 -144 467 -17C931 110 999 515 999 515M-58 -557C-58 -557 10 -152 474 -25C938 102 1006 507 1006 507M-51 -565C-51 -565 17 -160 481 -33C945 94 1013 499 1013 499M-44 -573C-44 -573 24 -168 488 -41C952 86 1020 491 1020 491M-37 -581C-37 -581 31 -176 495 -49C959 78 1027 483 1027 483M-30 -589C-30 -589 38 -184 502 -57C966 70 1034 475 1034 475M-23 -597C-23 -597 45 -192 509 -65C973 62 1041 467 1041 467M-16 -605C-16 -605 52 -200 516 -73C980 54 1048 459 1048 459M-9 -613C-9 -613 59 -208 523 -81C987 46 1055 451 1055 451M-2 -621C-2 -621 66 -216 530 -89C994 38 1062 443 1062 443M5 -629C5 -629 73 -224 537 -97C1001 30 1069 435 1069 435M12 -637C12 -637 80 -232 544 -105C1008 22 1076 427 1076 427M19 -645C19 -645 87 -240 551 -113C1015 14 1083 419 1083 419"
|
||||||
|
stroke="url(#paint0_radial_242_278)"
|
||||||
|
strokeOpacity="0.05"
|
||||||
|
strokeWidth="0.5"
|
||||||
|
></path>
|
||||||
|
|
||||||
|
{paths.map((path, index) => (
|
||||||
|
<motion.path
|
||||||
|
key={`path-` + index}
|
||||||
|
d={path}
|
||||||
|
stroke={`url(#linearGradient-${index})`}
|
||||||
|
strokeOpacity="0.4"
|
||||||
|
strokeWidth="0.5"
|
||||||
|
></motion.path>
|
||||||
|
))}
|
||||||
|
<defs>
|
||||||
|
{paths.map((_, index) => (
|
||||||
|
<motion.linearGradient
|
||||||
|
id={`linearGradient-${index}`}
|
||||||
|
key={`gradient-${index}`}
|
||||||
|
initial={{
|
||||||
|
x1: "0%",
|
||||||
|
x2: "0%",
|
||||||
|
y1: "0%",
|
||||||
|
y2: "0%",
|
||||||
|
}}
|
||||||
|
animate={{
|
||||||
|
x1: ["0%", "100%"],
|
||||||
|
x2: ["0%", "95%"],
|
||||||
|
y1: ["0%", "100%"],
|
||||||
|
y2: ["0%", `${93 + Math.random() * 8}%`],
|
||||||
|
}}
|
||||||
|
transition={{
|
||||||
|
duration: Math.random() * 10 + 10,
|
||||||
|
ease: "easeInOut",
|
||||||
|
repeat: Infinity,
|
||||||
|
delay: Math.random() * 10,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<stop stopColor="#18CCFC" stopOpacity="0"></stop>
|
||||||
|
<stop stopColor="#18CCFC"></stop>
|
||||||
|
<stop offset="32.5%" stopColor="#6344F5"></stop>
|
||||||
|
<stop offset="100%" stopColor="#AE48FF" stopOpacity="0"></stop>
|
||||||
|
</motion.linearGradient>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<radialGradient
|
||||||
|
id="paint0_radial_242_278"
|
||||||
|
cx="0"
|
||||||
|
cy="0"
|
||||||
|
r="1"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="translate(352 34) rotate(90) scale(555 1560.62)"
|
||||||
|
>
|
||||||
|
<stop offset="0.0666667" stopColor="var(--neutral-300)"></stop>
|
||||||
|
<stop offset="0.243243" stopColor="var(--neutral-300)"></stop>
|
||||||
|
<stop offset="0.43594" stopColor="white" stopOpacity="0"></stop>
|
||||||
|
</radialGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
BackgroundBeams.displayName = "BackgroundBeams";
|
||||||
@@ -9,6 +9,7 @@ import font from '../resources/fonts/IconFamily.woff'
|
|||||||
import * as Sentry from "@sentry/react";
|
import * as Sentry from "@sentry/react";
|
||||||
import ThemeCreator from './pages/ThemeCreator';
|
import ThemeCreator from './pages/ThemeCreator';
|
||||||
import Store from './pages/Store';
|
import Store from './pages/Store';
|
||||||
|
import Theme from './pages/Theme';
|
||||||
|
|
||||||
browser.storage.local.get().then(({ telemetry, DarkMode }) => {
|
browser.storage.local.get().then(({ telemetry, DarkMode }) => {
|
||||||
if (DarkMode) document.documentElement.classList.add('dark');
|
if (DarkMode) document.documentElement.classList.add('dark');
|
||||||
@@ -45,6 +46,7 @@ root.render(
|
|||||||
<Route path="/settings/embedded" element={<SettingsPage standalone={false} />} />
|
<Route path="/settings/embedded" element={<SettingsPage standalone={false} />} />
|
||||||
<Route path="/store" element={<Store />} />
|
<Route path="/store" element={<Store />} />
|
||||||
<Route path="/themeCreator" element={<ThemeCreator />} />
|
<Route path="/themeCreator" element={<ThemeCreator />} />
|
||||||
|
<Route path="/theme" element={<Theme />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</HashRouter>
|
</HashRouter>
|
||||||
</React.StrictMode>,
|
</React.StrictMode>,
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import PocketBase from 'pocketbase';
|
||||||
|
import { ThemesRecord } from "../types/pocketbase-types";
|
||||||
|
import ConfettiExplosion from 'react-confetti-explosion';
|
||||||
|
import { BackgroundBeams } from "../components/backgroundBeams";
|
||||||
|
import { LinkIcon } from "@heroicons/react/24/outline";
|
||||||
|
import { ToastContainer, toast } from 'react-toastify';
|
||||||
|
import browser from 'webextension-polyfill';
|
||||||
|
import 'react-toastify/dist/ReactToastify.css';
|
||||||
|
|
||||||
|
const pb = new PocketBase('https://betterseqta.pockethost.io');
|
||||||
|
|
||||||
|
const Theme = () => {
|
||||||
|
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||||
|
const [theme, setTheme] = useState<ThemesRecord | null>(null);
|
||||||
|
const [themeID, setThemeID] = useState<string>('');
|
||||||
|
const [justCreated, setJustCreated] = useState(false);
|
||||||
|
const [displayConfetti, setDisplayConfetti] = useState(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
setDisplayConfetti(false);
|
||||||
|
}, 5000);
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const themeID = urlParams.get('id');
|
||||||
|
const justCreated = urlParams.get('justCreated');
|
||||||
|
if (themeID) {
|
||||||
|
setThemeID(themeID);
|
||||||
|
}
|
||||||
|
|
||||||
|
const getTheme = async (themeID: string) => {
|
||||||
|
const theme = await pb.collection<ThemesRecord>('themes').getOne(themeID);
|
||||||
|
console.log(theme);
|
||||||
|
setIsLoading(false);
|
||||||
|
setTheme(theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (themeID && themeID !== 'null') {
|
||||||
|
getTheme(themeID);
|
||||||
|
setJustCreated(justCreated === 'true');
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{isLoading && (
|
||||||
|
<div className="flex items-center justify-center min-h-screen">
|
||||||
|
<div className="w-32 h-32 border-t-2 border-b-2 border-blue-500 rounded-full animate-spin"></div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{theme && (
|
||||||
|
<div className="flex flex-col items-center justify-center min-h-screen dark:text-white">
|
||||||
|
{ justCreated ? (
|
||||||
|
<>
|
||||||
|
{
|
||||||
|
displayConfetti && (
|
||||||
|
<ConfettiExplosion
|
||||||
|
className="absolute"
|
||||||
|
colors={['#FF0000', '#FF7F50', '#DE3163', '#40E0D0', '#6495ED']}
|
||||||
|
duration={5000}
|
||||||
|
width={1500}
|
||||||
|
force={1}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<div className="text-center">
|
||||||
|
<h2 className="mb-2 text-2xl font-bold">Theme Created Successfully!</h2>
|
||||||
|
<p className="mb-8">Share the install link below with your friends to install the theme!</p>
|
||||||
|
<div
|
||||||
|
className="flex gap-2 py-2 pl-3 pr-2 text-white backdrop-blur-sm rounded-3xl ring-white/20 ring-1 bg-zinc-950/50"
|
||||||
|
>
|
||||||
|
<span className="my-auto">
|
||||||
|
{`https://share.betterseqta/theme?id=${themeID}`}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className="flex gap-1 px-3 py-2 text-white transition cursor-pointer rounded-2xl ring-white/20 ring-1 bg-zinc-950/20 hover:bg-black/85"
|
||||||
|
onClick={() => {
|
||||||
|
navigator.clipboard.writeText(`https://share.betterseqta/theme?id=${themeID}`);
|
||||||
|
toast("Link copied to clipboard!");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<LinkIcon className="w-4 h-4" />
|
||||||
|
Copy Link
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<div className="text-center">
|
||||||
|
<h2 className="mb-2 text-2xl font-bold">{theme.name}</h2>
|
||||||
|
<p className="mb-8">{theme.description}</p>
|
||||||
|
<button
|
||||||
|
className="flex justify-center w-full gap-1 px-3 py-2 text-white transition cursor-pointer rounded-2xl ring-white/20 hover:ring-white/10 ring-1 bg-zinc-950/20 hover:bg-zinc-950/40"
|
||||||
|
onClick={() => browser.runtime.sendMessage({ type: 'currentTab', info: 'DownloadTheme', body: { themeID: themeID } }) }
|
||||||
|
>
|
||||||
|
Install Theme
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<ToastContainer theme="dark" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<BackgroundBeams className='-z-10' />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Theme;
|
||||||
|
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
/**
|
||||||
|
* This file was @generated using pocketbase-typegen
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type PocketBase from 'pocketbase'
|
||||||
|
import type { RecordService } from 'pocketbase'
|
||||||
|
|
||||||
|
export enum Collections {
|
||||||
|
PublishedThemes = "publishedThemes",
|
||||||
|
Themes = "themes",
|
||||||
|
Users = "users",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alias types for improved usability
|
||||||
|
export type IsoDateString = string
|
||||||
|
export type RecordIdString = string
|
||||||
|
export type HTMLString = string
|
||||||
|
|
||||||
|
// System fields
|
||||||
|
export type BaseSystemFields<T = never> = {
|
||||||
|
id: RecordIdString
|
||||||
|
created: IsoDateString
|
||||||
|
updated: IsoDateString
|
||||||
|
collectionId: string
|
||||||
|
collectionName: Collections
|
||||||
|
expand?: T
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AuthSystemFields<T = never> = {
|
||||||
|
email: string
|
||||||
|
emailVisibility: boolean
|
||||||
|
username: string
|
||||||
|
verified: boolean
|
||||||
|
} & BaseSystemFields<T>
|
||||||
|
|
||||||
|
// Record types for each collection
|
||||||
|
|
||||||
|
export type PublishedThemesRecord = {
|
||||||
|
coverImage?: string
|
||||||
|
description?: string
|
||||||
|
downloads?: string
|
||||||
|
marqueeImage?: string
|
||||||
|
name: string
|
||||||
|
themeURL?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ThemesRecord<Ttheme = unknown> = {
|
||||||
|
coverImage?: string
|
||||||
|
description?: string
|
||||||
|
downloads?: string
|
||||||
|
images?: string[]
|
||||||
|
name: string
|
||||||
|
submitted?: boolean
|
||||||
|
theme?: null | Ttheme
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UsersRecord = {
|
||||||
|
avatar?: string
|
||||||
|
name?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response types include system fields and match responses from the PocketBase API
|
||||||
|
export type PublishedThemesResponse<Texpand = unknown> = Required<PublishedThemesRecord> & BaseSystemFields<Texpand>
|
||||||
|
export type ThemesResponse<Ttheme = unknown, Texpand = unknown> = Required<ThemesRecord<Ttheme>> & BaseSystemFields<Texpand>
|
||||||
|
export type UsersResponse<Texpand = unknown> = Required<UsersRecord> & AuthSystemFields<Texpand>
|
||||||
|
|
||||||
|
// Types containing all Records and Responses, useful for creating typing helper functions
|
||||||
|
|
||||||
|
export type CollectionRecords = {
|
||||||
|
publishedThemes: PublishedThemesRecord
|
||||||
|
themes: ThemesRecord
|
||||||
|
users: UsersRecord
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CollectionResponses = {
|
||||||
|
publishedThemes: PublishedThemesResponse
|
||||||
|
themes: ThemesResponse
|
||||||
|
users: UsersResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type for usage with type asserted PocketBase instance
|
||||||
|
// https://github.com/pocketbase/js-sdk#specify-typescript-definitions
|
||||||
|
|
||||||
|
export type TypedPocketBase = PocketBase & {
|
||||||
|
collection(idOrName: 'publishedThemes'): RecordService<PublishedThemesResponse>
|
||||||
|
collection(idOrName: 'themes'): RecordService<ThemesResponse>
|
||||||
|
collection(idOrName: 'users'): RecordService<UsersResponse>
|
||||||
|
}
|
||||||
+17
-1
@@ -1,3 +1,8 @@
|
|||||||
|
const colors = require("tailwindcss/colors");
|
||||||
|
const {
|
||||||
|
default: flattenColorPalette,
|
||||||
|
} = require("tailwindcss/lib/util/flattenColorPalette");
|
||||||
|
|
||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
export default {
|
export default {
|
||||||
content: [
|
content: [
|
||||||
@@ -35,5 +40,16 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
plugins: [],
|
plugins: [addVariablesForColors],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function addVariablesForColors({ addBase, theme }) {
|
||||||
|
let allColors = flattenColorPalette(theme("colors"));
|
||||||
|
let newVars = Object.fromEntries(
|
||||||
|
Object.entries(allColors).map(([key, val]) => [`--${key}`, val])
|
||||||
|
);
|
||||||
|
|
||||||
|
addBase({
|
||||||
|
":root": newVars,
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user