Skip to content

Commit

Permalink
feat: WIP dorion theme browser
Browse files Browse the repository at this point in the history
  • Loading branch information
SpikeHD committed Dec 17, 2024
1 parent 5303952 commit f1e7ef7
Show file tree
Hide file tree
Showing 12 changed files with 304 additions and 8 deletions.
36 changes: 36 additions & 0 deletions plugins/dorion-theme-browser/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
interface ThemeOptions {
filter?: string
page?: string
sort?: 'popular' | 'creationdate' | 'name' | 'likes' | 'downloads' | 'recentlyupdated'
}

const BASE = 'https://betterdiscord.app'

export const themeListEndpoint = async (options: ThemeOptions) => {
const query = new URLSearchParams(options as Record<string, string>)

query.set('type', 'theme')
query.set('pages', '1')
query.set('sortDirection', 'descending')
query.set('tags', '[]')

const resp = await fetch(`${BASE}/Addon/GetApprovedAddons?${query}`)

if (!resp.ok) {
throw new Error('Failed to fetch themes')
}

const parser = new DOMParser()
const dom = parser.parseFromString(await resp.text(), 'text/html')

const themes = Array.from(dom.querySelectorAll('.card-wrap')).map((e: Element) => ({
thumbnail: `${BASE}${e.querySelector('.card-image')?.getAttribute('src')}`,
name: e.querySelector('.card-title')?.textContent?.trim(),
author: e.querySelector('.author-link')?.textContent?.trim(),
description: e.querySelector('.card-description')?.textContent?.trim(),
likes: e.querySelector('#addon-likes')?.textContent?.trim(),
downloads: e.querySelector('#addon-downloads')?.textContent?.trim(),
}))

return themes
}
60 changes: 60 additions & 0 deletions plugins/dorion-theme-browser/components/ThemeCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { css, classes } from './ThemeCard.tsx.scss'

interface Props {
key: string
theme: string
thumbnail: string
likes: string
downloads: string
description: string
author: string
install_url: string
}

const {
ui: {
injectCss,
Button,
Text
},
solid: {
createSignal,

Check failure on line 21 in plugins/dorion-theme-browser/components/ThemeCard.tsx

View workflow job for this annotation

GitHub Actions / tsc-eslint-checks

'createSignal' is assigned a value but never used
createEffect,

Check failure on line 22 in plugins/dorion-theme-browser/components/ThemeCard.tsx

View workflow job for this annotation

GitHub Actions / tsc-eslint-checks

'createEffect' is assigned a value but never used
},
} = shelter

let injectedCss = false

export function ThemeCard(props: Props) {
if (!injectedCss) {
injectCss(css)
injectedCss = true
}

const installTheme = () => {
// TODO
}

return (
<div class={classes.themeCard}>
<div class={classes.thumbnail} style={`background-image: url(${props.thumbnail})`}></div>

<div class={classes.info}>
<Text class={classes.name}>
<b>{props.theme}</b> by <b>{props.author}</b>
</Text>

<Text class={classes.contents}>{props.description}</Text>

<div class={classes.buttonContainer}>
<Button
class={classes.installButton}
onClick={installTheme}
>
Install
</Button>
</div>
</div>
</div>
)
}
51 changes: 51 additions & 0 deletions plugins/dorion-theme-browser/components/ThemeCard.tsx.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
.themeCard {
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: flex-start;
text-align: left;

padding: 0px;
margin: 8px;

color: var(--text-primary);
background: var(--background-secondary);

border-radius: 8px;

.thumbnail {
width: 100%;
height: 160px;
overflow: hidden;

/* TL & TR 8px, others 0px */
border-radius: 8px 8px 0px 0px;

/* image is set in the background so we can use cover */
background-size: cover;
background-position: center;
}

.info {
display: flex;
flex-direction: column;

margin-top: 6px;
padding: 16px;
width: 100%;

text-overflow: ellipsis;
overflow: hidden;

.name,
.contents,
.installButton {
margin-bottom: 8px;
}

.installButton {
margin-top: 8px;
width: 100%;
}
}
}
96 changes: 96 additions & 0 deletions plugins/dorion-theme-browser/components/ThemePage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { Dropdown } from '../../../components/Dropdown.jsx'
import { debounce } from '../../../util/debounce.js'
import { themeListEndpoint } from '../api.js'
import { ThemeCard } from './ThemeCard.jsx'
import { css, classes } from './ThemePage.tsx.scss'

const {
ui: {
injectCss,
Divider,
Header,
HeaderTags,
TextBox
},
solid: {
createSignal,
createEffect,
},
} = shelter

let injectedCss = false

export function ThemePage() {
if (!injectedCss) {
injectCss(css)
injectedCss = true
}

const [themeData, setThemeData] = createSignal<any[]>([])
const [sort, setSort] = createSignal('popular')
const [search, setSearch] = createSignal('')

createEffect(async () => {
await loadThemes()
})

const loadThemes = async () => {
setThemeData(await themeListEndpoint({ page: '1', sort: sort(), filter: search() }))
}

const doSearch = debounce((v: string) => setSearch(v), 500)

return (
<>
<Header tag={HeaderTags.H1} class={classes.tophead}>Theme Browser</Header>

<div class={classes.sortSection}>
<Dropdown
value={sort()}
onChange={(e) => {
setSort(e.target.value)
loadThemes()
}}
style='width: 30%;'
options={[
{ label: 'Popular', value: 'popular' },
{ label: 'Creation Date', value: 'creationdate' },
{ label: 'Name', value: 'name' },
{ label: 'Likes', value: 'likes' },
{ label: 'Downloads', value: 'downloads' },
{ label: 'Recently Updated', value: 'recentlyupdated' },
]}
placeholder={'Sort by...'}
/>

<span class={classes.searchBox}>
<TextBox
value={search()}
onInput={(v) => doSearch(v)}
placeholder={'Search...'}
/>
</span>
</div>


<Divider mt={16} mb={16} />

<div class={classes.themeCards}>
{
themeData().map((t) => (
<ThemeCard
key={t.name}
thumbnail={t.thumbnail}
likes={t.likes}
downloads={t.downloads}
theme={t.name}
description={t.description}
author={t.author}
install_url={t.install_url}
/>
))
}
</div>
</>
)
}
29 changes: 29 additions & 0 deletions plugins/dorion-theme-browser/components/ThemePage.tsx.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
.shead {
margin-top: 16px;
margin-bottom: 8px;
}

.bot16 {
margin-bottom: 16px;
}

.themeCards {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-gap: 8px;
margin-top: 16px;

width: 100%;
}

.sortSection {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}

.searchBox {
flex-grow: 0 !important;
width: 50%;
}
18 changes: 18 additions & 0 deletions plugins/dorion-theme-browser/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { appName } from '../../api/api.js'
import { ThemePage } from './components/ThemePage.jsx'

const {
settings: {
registerSection,
},
} = shelter

const uninjects = [
registerSection('divider'),
registerSection('header', 'Theme Browser'),
registerSection('section', `${appName}-theme-browser`, 'Theme Browser', ThemePage),
]

export const onUnload = () => {
uninjects.forEach((u) => u())
}
5 changes: 5 additions & 0 deletions plugins/dorion-theme-browser/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "Dorion Theme Browser",
"description": "Browse and install themes directly within Dorion",
"author": "SpikeHD"
}
6 changes: 3 additions & 3 deletions plugins/inline-css/components/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import hljs from 'highlight.js/lib/core'
import cssModule from 'highlight.js/lib/languages/css'

import {css, classes} from './Editor.scss'
import { debounce } from '../util'
import { Popout } from './Popout'
import { Window } from './Window'
import { debounce } from '../../../util/debounce.js'
import { Popout } from './Popout.jsx'
import { Window } from './Window.jsx'

interface Props {
styleElm?: HTMLStyleElement
Expand Down
6 changes: 3 additions & 3 deletions plugins/inline-css/components/Window.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { css, classes } from './Window.scss'
import Editor from './Editor'
import { Close } from './Close'
import { debounce } from '../util'
import Editor from './Editor.jsx'
import { Close } from './Close.jsx'
import { debounce } from '../../../util/debounce.js'

const {
ui: { injectCss },
Expand Down
2 changes: 1 addition & 1 deletion plugins/inline-css/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Editor from './components/Editor'
import Editor from './components/Editor.jsx'

const {
settings: {
Expand Down
3 changes: 2 additions & 1 deletion plugins/inline-css/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
"dependencies": {
"@srsholmes/solid-code-input": "^0.0.18",
"highlight.js": "^11.9.0"
}
},
"type": "module"
}
File renamed without changes.

0 comments on commit f1e7ef7

Please sign in to comment.