mirror of
https://github.com/revoltchat/revite.git
synced 2024-11-05 23:25:44 -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_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_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) {
|
if (code) {
|
||||||
navigator.clipboard.writeText(code.textContent?.trim() ?? "");
|
navigator.clipboard.writeText(code.textContent?.trim() ?? "");
|
||||||
}
|
}
|
||||||
} catch (e) { }
|
} catch (e) {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,8 +150,8 @@ export default function Renderer({ content, disallowBigEmoji }: MarkdownProps) {
|
||||||
`<@${element.dataset.mentionId}>`,
|
`<@${element.dataset.mentionId}>`,
|
||||||
"mention",
|
"mention",
|
||||||
);
|
);
|
||||||
ev.preventDefault()
|
ev.preventDefault();
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
case "channel_mention": {
|
case "channel_mention": {
|
||||||
internalEmit(
|
internalEmit(
|
||||||
|
@ -160,8 +160,8 @@ export default function Renderer({ content, disallowBigEmoji }: MarkdownProps) {
|
||||||
`<#${element.dataset.mentionId}>`,
|
`<#${element.dataset.mentionId}>`,
|
||||||
"channel_mention",
|
"channel_mention",
|
||||||
);
|
);
|
||||||
ev.preventDefault()
|
ev.preventDefault();
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -194,7 +194,6 @@ export default function Renderer({ content, disallowBigEmoji }: MarkdownProps) {
|
||||||
element.removeAttribute("target");
|
element.removeAttribute("target");
|
||||||
|
|
||||||
const link = determineLink(element.href);
|
const link = determineLink(element.href);
|
||||||
console.log(link)
|
|
||||||
switch (link.type) {
|
switch (link.type) {
|
||||||
case "profile": {
|
case "profile": {
|
||||||
element.setAttribute(
|
element.setAttribute(
|
||||||
|
@ -203,20 +202,20 @@ export default function Renderer({ content, disallowBigEmoji }: MarkdownProps) {
|
||||||
);
|
);
|
||||||
element.setAttribute(
|
element.setAttribute(
|
||||||
"data-mention-id",
|
"data-mention-id",
|
||||||
link.id
|
link.id,
|
||||||
)
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "navigate": {
|
case "navigate": {
|
||||||
if (link.navigation_type === 'channel') {
|
if (link.navigation_type === "channel") {
|
||||||
element.setAttribute(
|
element.setAttribute(
|
||||||
"data-type",
|
"data-type",
|
||||||
"channel_mention",
|
"channel_mention",
|
||||||
);
|
);
|
||||||
element.setAttribute(
|
element.setAttribute(
|
||||||
"data-mention-id",
|
"data-mention-id",
|
||||||
link.channel_id
|
link.channel_id,
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
type LinkType =
|
type LinkType =
|
||||||
| { type: "profile"; id: string }
|
| { type: "profile"; id: string }
|
||||||
| { type: "navigate"; path: string; navigation_type?: null }
|
| { 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: "external"; href: string; url: URL }
|
||||||
| { type: "none" };
|
| { type: "none" };
|
||||||
|
|
||||||
|
@ -12,7 +17,8 @@ const ALLOWED_ORIGINS = [
|
||||||
"local.revolt.chat",
|
"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 {
|
export function determineLink(href?: string): LinkType {
|
||||||
let internal,
|
let internal,
|
||||||
|
@ -30,9 +36,13 @@ export function determineLink(href?: string): LinkType {
|
||||||
return { type: "profile", id };
|
return { type: "profile", id };
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log(path)
|
if (CHANNEL_PATH_RE.test(path)) {
|
||||||
if(CHANNEL_PATH_RE.test(path)) {
|
return {
|
||||||
return { type: 'navigate', path, navigation_type: 'channel', channel_id: path.slice(43) }
|
type: "navigate",
|
||||||
|
path,
|
||||||
|
navigation_type: "channel",
|
||||||
|
channel_id: path.slice(43),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
return { type: "navigate", path };
|
return { type: "navigate", path };
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,178 +1,207 @@
|
||||||
import { useEffect, useState } from "preact/hooks"
|
import styled from "styled-components";
|
||||||
import styled from "styled-components"
|
|
||||||
import Tip from "../../../components/ui/Tip"
|
import { useEffect, useState } from "preact/hooks";
|
||||||
import { Theme, generateVariables } from '../../../context/Theme'
|
|
||||||
import { dispatch } from "../../../redux"
|
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> =>
|
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> =>
|
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 {
|
interface ThemeMetadata {
|
||||||
name: string,
|
name: string;
|
||||||
creator: string,
|
creator: string;
|
||||||
description: string
|
commit?: string;
|
||||||
|
description: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Manifest = {
|
type Manifest = {
|
||||||
generated: string,
|
generated: string;
|
||||||
themes: Record<string, ThemeMetadata>
|
themes: Record<string, ThemeMetadata>;
|
||||||
}
|
};
|
||||||
|
|
||||||
// TODO: ability to preview / display the settings set like in the appearance pane
|
// TODO: ability to preview / display the settings set like in the appearance pane
|
||||||
const ThemeInfo = styled.article`
|
const ThemeInfo = styled.article`
|
||||||
display: grid;
|
display: grid;
|
||||||
grid:
|
grid:
|
||||||
"preview name creator" min-content
|
"preview name creator" min-content
|
||||||
"preview desc desc" 1fr
|
"preview desc desc" 1fr
|
||||||
/ 200px 1fr 1fr;
|
/ 200px 1fr 1fr;
|
||||||
|
|
||||||
gap: 0.5rem 1rem;
|
gap: 0.5rem 1rem;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
border-radius: var(--border-radius);
|
border-radius: var(--border-radius);
|
||||||
background: var(--secondary-background);
|
background: var(--secondary-background);
|
||||||
|
|
||||||
|
&[data-loaded] {
|
||||||
|
.preview {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&[data-loaded] {
|
|
||||||
.preview {
|
.preview {
|
||||||
opacity: 1;
|
grid-area: preview;
|
||||||
}
|
aspect-ratio: 323 / 202;
|
||||||
}
|
|
||||||
|
|
||||||
.preview {
|
background-color: var(--secondary-background);
|
||||||
grid-area: preview;
|
border-radius: calc(var(--border-radius) / 2);
|
||||||
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
|
// prep style for later
|
||||||
overflow: hidden;
|
outline: 3px solid transparent;
|
||||||
|
|
||||||
// hide until loaded
|
// hide random svg parts, crop border on firefox
|
||||||
opacity: 0;
|
overflow: hidden;
|
||||||
|
|
||||||
// style button
|
// hide until loaded
|
||||||
border: 0;
|
opacity: 0;
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
// style button
|
||||||
|
border: 0;
|
||||||
transition: 0.25s opacity, 0.25s outline;
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
> * {
|
|
||||||
grid-area: 1 / 1;
|
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 {
|
.name {
|
||||||
height: 100%;
|
grid-area: name;
|
||||||
width: 100%;
|
margin: 0;
|
||||||
object-fit: contain;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover, &:active, &:focus-visible {
|
.creator {
|
||||||
outline: 3px solid var(--tertiary-background);
|
grid-area: creator;
|
||||||
|
justify-self: end;
|
||||||
|
font-size: 0.75rem;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.name {
|
.description {
|
||||||
grid-area: name;
|
grid-area: desc;
|
||||||
margin: 0;
|
}
|
||||||
}
|
`;
|
||||||
|
|
||||||
.creator {
|
|
||||||
grid-area: creator;
|
|
||||||
justify-self: end;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.description {
|
|
||||||
grid-area: desc;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const ThemeList = styled.div`
|
const ThemeList = styled.div`
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
`
|
`;
|
||||||
|
|
||||||
import previewPath from '../assets/preview.svg'
|
|
||||||
|
|
||||||
const ThemedSVG = styled.svg<{ theme: Theme }>`
|
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"> & {
|
type ThemePreviewProps = Omit<JSX.HTMLAttributes<SVGSVGElement>, "as"> & {
|
||||||
slug?: string,
|
slug?: string;
|
||||||
theme?: Theme
|
theme?: Theme;
|
||||||
onThemeLoaded?: (theme: Theme) => void
|
onThemeLoaded?: (theme: Theme) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ThemePreview = ({ theme, ...props }: ThemePreviewProps) => {
|
const ThemePreview = ({ theme, ...props }: ThemePreviewProps) => {
|
||||||
return <ThemedSVG {...props} theme={theme} width="323" height="202" aria-hidden="true" data-loaded={!!theme}>
|
return (
|
||||||
<use href={`${previewPath}#preview`} width="100%" height="100%" />
|
<ThemedSVG
|
||||||
</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`
|
const ThemeShopRoot = styled.div`
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
`
|
`;
|
||||||
|
|
||||||
export function ThemeShop() {
|
export function ThemeShop() {
|
||||||
// setThemeList is for adding more / lazy loading in the future
|
// setThemeList is for adding more / lazy loading in the future
|
||||||
const [themeList, setThemeList] = useState<[string, ThemeMetadata][] | null>(null);
|
const [themeList, setThemeList] = useState<
|
||||||
const [themeData, setThemeData] = useState<Record<string, Theme>>({});
|
[string, ThemeMetadata][] | null
|
||||||
|
>(null);
|
||||||
|
const [themeData, setThemeData] = useState<Record<string, Theme>>({});
|
||||||
|
|
||||||
async function fetchThemeList() {
|
async function fetchThemeList() {
|
||||||
const manifest = await fetchManifest()
|
const manifest = await fetchManifest();
|
||||||
setThemeList(Object.entries(manifest.themes))
|
setThemeList(
|
||||||
}
|
Object.entries(manifest.themes).filter((x) =>
|
||||||
|
x[1].commit ? x[1].commit === GIT_REVISION : true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
async function getTheme(slug: string) {
|
async function getTheme(slug: string) {
|
||||||
const theme = await fetchTheme(slug);
|
const theme = await fetchTheme(slug);
|
||||||
setThemeData(data => ({ ...data, [slug]: theme }))
|
setThemeData((data) => ({ ...data, [slug]: theme }));
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchThemeList()
|
fetchThemeList();
|
||||||
}, [])
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
themeList?.forEach(([slug]) => {
|
themeList?.forEach(([slug]) => {
|
||||||
getTheme(slug)
|
getTheme(slug);
|
||||||
})
|
});
|
||||||
}, [themeList])
|
}, [themeList]);
|
||||||
|
|
||||||
return (<ThemeShopRoot>
|
return (
|
||||||
<Tip warning>This section is under construction.</Tip>
|
<ThemeShopRoot>
|
||||||
<ThemeList>
|
<Tip warning hideSeparator>
|
||||||
{themeList?.map(([slug, theme]) => (
|
This section is under construction.
|
||||||
<ThemeInfo key={slug} data-loaded={Reflect.has(themeData, slug)}>
|
</Tip>
|
||||||
<h2 class="name">{theme.name}</h2>
|
<ThemeList>
|
||||||
{/* Maybe id's of the users should be included as well / instead? */}
|
{themeList?.map(([slug, theme]) => (
|
||||||
<div class="creator">by {theme.creator}</div>
|
<ThemeInfo
|
||||||
<div class="description">{theme.description}</div>
|
key={slug}
|
||||||
<button
|
data-loaded={Reflect.has(themeData, slug)}>
|
||||||
class="preview"
|
<h2 class="name">{theme.name}</h2>
|
||||||
onClick={() => dispatch({
|
{/* Maybe id's of the users should be included as well / instead? */}
|
||||||
type: "SETTINGS_SET_THEME",
|
<div class="creator">by {theme.creator}</div>
|
||||||
theme: {
|
<div class="description">{theme.description}</div>
|
||||||
custom: themeData[slug],
|
<button
|
||||||
}
|
class="preview"
|
||||||
})}
|
onClick={() =>
|
||||||
>
|
dispatch({
|
||||||
<ThemePreview
|
type: "SETTINGS_SET_THEME",
|
||||||
slug={slug}
|
theme: {
|
||||||
theme={themeData[slug]}
|
custom: themeData[slug],
|
||||||
/>
|
},
|
||||||
</button>
|
})
|
||||||
</ThemeInfo>
|
}>
|
||||||
))}
|
<ThemePreview slug={slug} theme={themeData[slug]} />
|
||||||
</ThemeList>
|
</button>
|
||||||
</ThemeShopRoot>)
|
</ThemeInfo>
|
||||||
}
|
))}
|
||||||
|
</ThemeList>
|
||||||
|
</ThemeShopRoot>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue