import { Plus, Check } from "@styled-icons/boxicons-regular"; import { Star, BarChartAlt2, Brush, Bookmark, } from "@styled-icons/boxicons-solid"; import styled from "styled-components"; import { useEffect, useState } from "preact/hooks"; import { dispatch } from "../../../redux"; import { Theme, generateVariables, ThemeOptions } from "../../../context/Theme"; import InputBox from "../../../components/ui/InputBox"; import Tip from "../../../components/ui/Tip"; import previewPath from "../assets/preview.svg"; import { GIT_REVISION } from "../../../revision"; export const fetchManifest = (): Promise => fetch(`${import.meta.env.VITE_THEMES_URL}/manifest.json`).then((res) => res.json(), ); export const fetchTheme = (slug: string): Promise => fetch(`${import.meta.env.VITE_THEMES_URL}/theme_${slug}.json`).then((res) => res.json(), ); export interface ThemeMetadata { name: string; creator: string; commit?: string; description: string; } export type Manifest = { generated: string; themes: Record; }; // TODO: ability to preview / display the settings set like in the appearance pane const ThemeInfo = styled.article` display: flex; flex-direction: column; gap: 10px; padding: 1rem; border-radius: var(--border-radius); background: var(--secondary-background); &[data-loaded] { .preview { opacity: 1; } } .preview { grid-area: preview; aspect-ratio: 323 / 202; background-color: var(--secondary-background); border-radius: calc(var(--border-radius) / 2); // prep style for later outline: 3px solid transparent; // hide random svg parts, crop border on firefox overflow: hidden; // hide until loaded opacity: 0; // style button border: 0; margin: 0; padding: 0; transition: 0.25s opacity, 0.25s outline; > * { grid-area: 1 / 1; } svg { height: 100%; width: 100%; object-fit: contain; } &:hover, &:active, &:focus-visible { outline: 3px solid var(--tertiary-background); } } .name { margin-top: 5px !important; grid-area: name; margin: 0; } .creator { grid-area: creator; justify-self: end; font-size: 0.75rem; } .description { margin-bottom: 5px; grid-area: desc; } .previewBox { position: relative; height: 100%; width: 100%; .hover { opacity: 0; font-family: var(--font), sans-serif; font-variant-ligatures: var(--ligatures); font-weight: 600; display: flex; align-items: center; justify-content: center; color: white; height: 100%; width: 100%; z-index: 10; position: absolute; background: rgba(0, 0, 0, 0.5); cursor: pointer; transition: opacity 0.2s ease-in-out; &:hover { opacity: 1; } } > svg { height: 100%; } } `; const ThemeList = styled.div` display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 1rem; `; const Banner = styled.div` display: flex; flex-direction: column; `; const Category = styled.div` display: flex; gap: 8px; align-items: center; .title { display: flex; align-items: center; gap: 8px; flex-grow: 1; } .view { font-size: 12px; } `; const ActiveTheme = styled.div` display: flex; flex-direction: column; background: var(--secondary-background); padding: 0; border-radius: var(--border-radius); gap: 8px; overflow: hidden; .active-indicator { display: flex; gap: 6px; align-items: center; background: var(--accent); width: 100%; padding: 5px 10px; font-size: 13px; font-weight: 400; color: white; } .title { font-size: 1.2rem; font-weight: 600; } .author { font-size: 12px; margin-bottom: 5px; } .theme { width: 124px; height: 80px; background: var(--tertiary-background); border-radius: 4px; } .container { display: flex; gap: 16px; padding: 10px 16px 16px; } `; const ThemedSVG = styled.svg<{ theme: Theme }>` ${(props) => props.theme && generateVariables(props.theme)} `; type ThemePreviewProps = Omit, "as"> & { slug?: string; theme?: Theme; onThemeLoaded?: (theme: Theme) => void; }; const ThemePreview = ({ theme, ...props }: ThemePreviewProps) => { return ( ); }; const ThemeShopRoot = styled.div` display: grid; gap: 1rem; h5 { margin-bottom: 0; } `; export function ThemeShop() { // setThemeList is for adding more / lazy loading in the future const [themeList, setThemeList] = useState< [string, ThemeMetadata][] | null >(null); const [themeData, setThemeData] = useState>({}); async function fetchThemeList() { const manifest = await fetchManifest(); setThemeList( Object.entries(manifest.themes).filter((x) => x[1].commit ? x[1].commit === GIT_REVISION : true, ), ); } async function getTheme(slug: string) { const theme = await fetchTheme(slug); setThemeData((data) => ({ ...data, [slug]: theme })); } useEffect(() => { fetchThemeList(); }, []); useEffect(() => { themeList?.forEach(([slug]) => { getTheme(slug); }); }, [themeList]); return (
Browse hundreds of themes, created and curated by the community.
{/*
Oops! Couldn't load the theme shop. Make sure you're connected to the internet and try again.
*/} The Theme Shop is currently under construction.
{/* FIXME INTEGRATE WITH MOBX */} {/*
Currently active
theme svg goes here
Theme Title
by Author
This is a theme description.
Saved
Manage installed
New this week
View all
Default themes
View all
Highest rated
View all
*/} {themeList?.map(([slug, theme]) => (

{theme.name}

{/* Maybe id's of the users should be included as well / instead? */}
by {theme.creator}
{theme.description}
))}
); }