mirror of
https://github.com/revoltchat/revite.git
synced 2024-12-24 06:32:08 -05:00
Remove stray console.log and update translations.
Update themes endpoint and filter unmatched commits.
This commit is contained in:
parent
efbbb6f1aa
commit
048267b419
6 changed files with 196 additions and 158 deletions
2
.env
2
.env
|
@ -1,2 +1,2 @@
|
|||
VITE_API_URL=https://api.revolt.chat
|
||||
VITE_THEMES_URL=https://static.revolt.chat/themes
|
||||
VITE_THEMES_URL=https://themes.revolt.chat
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
VITE_API_URL=__API_URL__
|
||||
VITE_THEMES_URL=https://static.revolt.chat/themes
|
||||
VITE_THEMES_URL=https://themes.revolt.chat
|
2
external/lang
vendored
2
external/lang
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 2d5b96a5c72bb706b73e9b67d5be395810a18c15
|
||||
Subproject commit edebbe8a1d720b2ecbedbf137e2e7c2f2e3d1e13
|
|
@ -44,7 +44,7 @@ if (typeof window !== "undefined") {
|
|||
if (code) {
|
||||
navigator.clipboard.writeText(code.textContent?.trim() ?? "");
|
||||
}
|
||||
} catch (e) { }
|
||||
} catch (e) {}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -150,8 +150,8 @@ export default function Renderer({ content, disallowBigEmoji }: MarkdownProps) {
|
|||
`<@${element.dataset.mentionId}>`,
|
||||
"mention",
|
||||
);
|
||||
ev.preventDefault()
|
||||
return
|
||||
ev.preventDefault();
|
||||
return;
|
||||
}
|
||||
case "channel_mention": {
|
||||
internalEmit(
|
||||
|
@ -160,8 +160,8 @@ export default function Renderer({ content, disallowBigEmoji }: MarkdownProps) {
|
|||
`<#${element.dataset.mentionId}>`,
|
||||
"channel_mention",
|
||||
);
|
||||
ev.preventDefault()
|
||||
return
|
||||
ev.preventDefault();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -194,7 +194,6 @@ export default function Renderer({ content, disallowBigEmoji }: MarkdownProps) {
|
|||
element.removeAttribute("target");
|
||||
|
||||
const link = determineLink(element.href);
|
||||
console.log(link)
|
||||
switch (link.type) {
|
||||
case "profile": {
|
||||
element.setAttribute(
|
||||
|
@ -203,20 +202,20 @@ export default function Renderer({ content, disallowBigEmoji }: MarkdownProps) {
|
|||
);
|
||||
element.setAttribute(
|
||||
"data-mention-id",
|
||||
link.id
|
||||
)
|
||||
link.id,
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "navigate": {
|
||||
if (link.navigation_type === 'channel') {
|
||||
if (link.navigation_type === "channel") {
|
||||
element.setAttribute(
|
||||
"data-type",
|
||||
"channel_mention",
|
||||
);
|
||||
element.setAttribute(
|
||||
"data-mention-id",
|
||||
link.channel_id
|
||||
)
|
||||
link.channel_id,
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
type LinkType =
|
||||
| { type: "profile"; id: string }
|
||||
| { type: "navigate"; path: string; navigation_type?: null }
|
||||
| { type: "navigate"; path: string; navigation_type: 'channel'; channel_id: string }
|
||||
| {
|
||||
type: "navigate";
|
||||
path: string;
|
||||
navigation_type: "channel";
|
||||
channel_id: string;
|
||||
}
|
||||
| { type: "external"; href: string; url: URL }
|
||||
| { type: "none" };
|
||||
|
||||
|
@ -12,7 +17,8 @@ const ALLOWED_ORIGINS = [
|
|||
"local.revolt.chat",
|
||||
];
|
||||
|
||||
const CHANNEL_PATH_RE = /^\/server\/[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26}\/channel\/[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26}$/
|
||||
const CHANNEL_PATH_RE =
|
||||
/^\/server\/[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26}\/channel\/[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26}$/;
|
||||
|
||||
export function determineLink(href?: string): LinkType {
|
||||
let internal,
|
||||
|
@ -30,9 +36,13 @@ export function determineLink(href?: string): LinkType {
|
|||
return { type: "profile", id };
|
||||
}
|
||||
} else {
|
||||
console.log(path)
|
||||
if(CHANNEL_PATH_RE.test(path)) {
|
||||
return { type: 'navigate', path, navigation_type: 'channel', channel_id: path.slice(43) }
|
||||
if (CHANNEL_PATH_RE.test(path)) {
|
||||
return {
|
||||
type: "navigate",
|
||||
path,
|
||||
navigation_type: "channel",
|
||||
channel_id: path.slice(43),
|
||||
};
|
||||
}
|
||||
return { type: "navigate", path };
|
||||
}
|
||||
|
|
|
@ -1,178 +1,207 @@
|
|||
import { useEffect, useState } from "preact/hooks"
|
||||
import styled from "styled-components"
|
||||
import Tip from "../../../components/ui/Tip"
|
||||
import { Theme, generateVariables } from '../../../context/Theme'
|
||||
import { dispatch } from "../../../redux"
|
||||
import styled from "styled-components";
|
||||
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
|
||||
import { dispatch } from "../../../redux";
|
||||
|
||||
import { Theme, generateVariables } from "../../../context/Theme";
|
||||
|
||||
import Tip from "../../../components/ui/Tip";
|
||||
import previewPath from "../assets/preview.svg";
|
||||
|
||||
import { GIT_REVISION } from "../../../revision";
|
||||
|
||||
export const fetchManifest = (): Promise<Manifest> =>
|
||||
fetch(`${import.meta.env.VITE_THEMES_URL}/manifest.json`).then(res => res.json())
|
||||
fetch(`${import.meta.env.VITE_THEMES_URL}/manifest.json`).then((res) =>
|
||||
res.json(),
|
||||
);
|
||||
|
||||
export const fetchTheme = (slug: string): Promise<Theme> =>
|
||||
fetch(`${import.meta.env.VITE_THEMES_URL}/theme_${slug}.json`).then(res => res.json())
|
||||
|
||||
fetch(`${import.meta.env.VITE_THEMES_URL}/theme_${slug}.json`).then((res) =>
|
||||
res.json(),
|
||||
);
|
||||
|
||||
interface ThemeMetadata {
|
||||
name: string,
|
||||
creator: string,
|
||||
description: string
|
||||
name: string;
|
||||
creator: string;
|
||||
commit?: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
type Manifest = {
|
||||
generated: string,
|
||||
themes: Record<string, ThemeMetadata>
|
||||
}
|
||||
generated: string;
|
||||
themes: Record<string, ThemeMetadata>;
|
||||
};
|
||||
|
||||
// TODO: ability to preview / display the settings set like in the appearance pane
|
||||
const ThemeInfo = styled.article`
|
||||
display: grid;
|
||||
grid:
|
||||
"preview name creator" min-content
|
||||
"preview desc desc" 1fr
|
||||
/ 200px 1fr 1fr;
|
||||
|
||||
gap: 0.5rem 1rem;
|
||||
padding: 1rem;
|
||||
border-radius: var(--border-radius);
|
||||
background: var(--secondary-background);
|
||||
display: grid;
|
||||
grid:
|
||||
"preview name creator" min-content
|
||||
"preview desc desc" 1fr
|
||||
/ 200px 1fr 1fr;
|
||||
|
||||
gap: 0.5rem 1rem;
|
||||
padding: 1rem;
|
||||
border-radius: var(--border-radius);
|
||||
background: var(--secondary-background);
|
||||
|
||||
&[data-loaded] {
|
||||
.preview {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&[data-loaded] {
|
||||
.preview {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
grid-area: preview;
|
||||
aspect-ratio: 323 / 202;
|
||||
|
||||
.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;
|
||||
background-color: var(--secondary-background);
|
||||
border-radius: calc(var(--border-radius) / 2);
|
||||
|
||||
// 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;
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
object-fit: contain;
|
||||
.name {
|
||||
grid-area: name;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&:hover, &:active, &:focus-visible {
|
||||
outline: 3px solid var(--tertiary-background);
|
||||
|
||||
.creator {
|
||||
grid-area: creator;
|
||||
justify-self: end;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
grid-area: name;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.creator {
|
||||
grid-area: creator;
|
||||
justify-self: end;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.description {
|
||||
grid-area: desc;
|
||||
}
|
||||
`
|
||||
.description {
|
||||
grid-area: desc;
|
||||
}
|
||||
`;
|
||||
|
||||
const ThemeList = styled.div`
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
`
|
||||
|
||||
import previewPath from '../assets/preview.svg'
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
`;
|
||||
|
||||
const ThemedSVG = styled.svg<{ theme: Theme }>`
|
||||
${props => props.theme && generateVariables(props.theme)}
|
||||
`
|
||||
${(props) => props.theme && generateVariables(props.theme)}
|
||||
`;
|
||||
|
||||
type ThemePreviewProps = Omit<JSX.HTMLAttributes<SVGSVGElement>, "as"> & {
|
||||
slug?: string,
|
||||
theme?: Theme
|
||||
onThemeLoaded?: (theme: Theme) => void
|
||||
slug?: string;
|
||||
theme?: Theme;
|
||||
onThemeLoaded?: (theme: Theme) => void;
|
||||
};
|
||||
|
||||
const ThemePreview = ({ theme, ...props }: ThemePreviewProps) => {
|
||||
return <ThemedSVG {...props} theme={theme} width="323" height="202" aria-hidden="true" data-loaded={!!theme}>
|
||||
<use href={`${previewPath}#preview`} width="100%" height="100%" />
|
||||
</ThemedSVG >
|
||||
}
|
||||
return (
|
||||
<ThemedSVG
|
||||
{...props}
|
||||
theme={theme}
|
||||
width="323"
|
||||
height="202"
|
||||
aria-hidden="true"
|
||||
data-loaded={!!theme}>
|
||||
<use href={`${previewPath}#preview`} width="100%" height="100%" />
|
||||
</ThemedSVG>
|
||||
);
|
||||
};
|
||||
|
||||
const ThemeShopRoot = styled.div`
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
`
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
`;
|
||||
|
||||
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<Record<string, Theme>>({});
|
||||
// setThemeList is for adding more / lazy loading in the future
|
||||
const [themeList, setThemeList] = useState<
|
||||
[string, ThemeMetadata][] | null
|
||||
>(null);
|
||||
const [themeData, setThemeData] = useState<Record<string, Theme>>({});
|
||||
|
||||
async function fetchThemeList() {
|
||||
const manifest = await fetchManifest()
|
||||
setThemeList(Object.entries(manifest.themes))
|
||||
}
|
||||
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 }))
|
||||
}
|
||||
async function getTheme(slug: string) {
|
||||
const theme = await fetchTheme(slug);
|
||||
setThemeData((data) => ({ ...data, [slug]: theme }));
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
fetchThemeList()
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
fetchThemeList();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
themeList?.forEach(([slug]) => {
|
||||
getTheme(slug)
|
||||
})
|
||||
}, [themeList])
|
||||
useEffect(() => {
|
||||
themeList?.forEach(([slug]) => {
|
||||
getTheme(slug);
|
||||
});
|
||||
}, [themeList]);
|
||||
|
||||
return (<ThemeShopRoot>
|
||||
<Tip warning>This section is under construction.</Tip>
|
||||
<ThemeList>
|
||||
{themeList?.map(([slug, theme]) => (
|
||||
<ThemeInfo key={slug} data-loaded={Reflect.has(themeData, slug)}>
|
||||
<h2 class="name">{theme.name}</h2>
|
||||
{/* Maybe id's of the users should be included as well / instead? */}
|
||||
<div class="creator">by {theme.creator}</div>
|
||||
<div class="description">{theme.description}</div>
|
||||
<button
|
||||
class="preview"
|
||||
onClick={() => dispatch({
|
||||
type: "SETTINGS_SET_THEME",
|
||||
theme: {
|
||||
custom: themeData[slug],
|
||||
}
|
||||
})}
|
||||
>
|
||||
<ThemePreview
|
||||
slug={slug}
|
||||
theme={themeData[slug]}
|
||||
/>
|
||||
</button>
|
||||
</ThemeInfo>
|
||||
))}
|
||||
</ThemeList>
|
||||
</ThemeShopRoot>)
|
||||
}
|
||||
return (
|
||||
<ThemeShopRoot>
|
||||
<Tip warning hideSeparator>
|
||||
This section is under construction.
|
||||
</Tip>
|
||||
<ThemeList>
|
||||
{themeList?.map(([slug, theme]) => (
|
||||
<ThemeInfo
|
||||
key={slug}
|
||||
data-loaded={Reflect.has(themeData, slug)}>
|
||||
<h2 class="name">{theme.name}</h2>
|
||||
{/* Maybe id's of the users should be included as well / instead? */}
|
||||
<div class="creator">by {theme.creator}</div>
|
||||
<div class="description">{theme.description}</div>
|
||||
<button
|
||||
class="preview"
|
||||
onClick={() =>
|
||||
dispatch({
|
||||
type: "SETTINGS_SET_THEME",
|
||||
theme: {
|
||||
custom: themeData[slug],
|
||||
},
|
||||
})
|
||||
}>
|
||||
<ThemePreview slug={slug} theme={themeData[slug]} />
|
||||
</button>
|
||||
</ThemeInfo>
|
||||
))}
|
||||
</ThemeList>
|
||||
</ThemeShopRoot>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue