mirror of
https://github.com/revoltchat/revite.git
synced 2024-11-25 08:30:58 -05:00
feat(@ui): migrate textarea and tip
This commit is contained in:
parent
b4777e9816
commit
f3bdbe52d9
14 changed files with 41 additions and 607 deletions
|
@ -1,31 +0,0 @@
|
||||||
.container {
|
|
||||||
font-size: .875rem;
|
|
||||||
line-height: 20px;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.textarea {
|
|
||||||
width: 100%;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
|
|
||||||
textarea::placeholder {
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.hide {
|
|
||||||
width: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ghost {
|
|
||||||
width: 100%;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
|
|
||||||
top: 0;
|
|
||||||
position: absolute;
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
import styled, { css } from "styled-components/macro";
|
|
||||||
|
|
||||||
export interface TextAreaProps {
|
|
||||||
code?: boolean;
|
|
||||||
padding?: string;
|
|
||||||
lineHeight?: string;
|
|
||||||
hideBorder?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const TEXT_AREA_BORDER_WIDTH = 2;
|
|
||||||
export const DEFAULT_TEXT_AREA_PADDING = 16;
|
|
||||||
export const DEFAULT_LINE_HEIGHT = 20;
|
|
||||||
|
|
||||||
export default styled.textarea<TextAreaProps>`
|
|
||||||
width: 100%;
|
|
||||||
resize: none;
|
|
||||||
display: block;
|
|
||||||
color: var(--foreground);
|
|
||||||
background: var(--secondary-background);
|
|
||||||
padding: ${(props) => props.padding ?? "var(--textarea-padding)"};
|
|
||||||
line-height: ${(props) =>
|
|
||||||
props.lineHeight ?? "var(--textarea-line-height)"};
|
|
||||||
|
|
||||||
${(props) =>
|
|
||||||
props.hideBorder &&
|
|
||||||
css`
|
|
||||||
border: none;
|
|
||||||
`}
|
|
||||||
|
|
||||||
${(props) =>
|
|
||||||
!props.hideBorder &&
|
|
||||||
css`
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
transition: border-color 0.2s ease-in-out;
|
|
||||||
border: var(--input-border-width) solid transparent;
|
|
||||||
`}
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
outline: none;
|
|
||||||
|
|
||||||
${(props) =>
|
|
||||||
!props.hideBorder &&
|
|
||||||
css`
|
|
||||||
border: var(--input-border-width) solid var(--accent);
|
|
||||||
`}
|
|
||||||
}
|
|
||||||
|
|
||||||
${(props) =>
|
|
||||||
props.code
|
|
||||||
? css`
|
|
||||||
font-family: var(--monospace-font), monospace;
|
|
||||||
`
|
|
||||||
: css`
|
|
||||||
font-family: inherit;
|
|
||||||
`}
|
|
||||||
|
|
||||||
font-variant-ligatures: var(--ligatures);
|
|
||||||
`;
|
|
|
@ -1,72 +0,0 @@
|
||||||
import { InfoCircle } from "@styled-icons/boxicons-regular";
|
|
||||||
import styled, { css } from "styled-components/macro";
|
|
||||||
|
|
||||||
import { Children } from "../../types/Preact";
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
warning?: boolean;
|
|
||||||
error?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Separator = styled.div<Props>`
|
|
||||||
height: 1px;
|
|
||||||
width: calc(100% - 10px);
|
|
||||||
background: var(--secondary-header);
|
|
||||||
margin: 18px auto;
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const TipBase = styled.div<Props>`
|
|
||||||
display: flex;
|
|
||||||
padding: 12px;
|
|
||||||
font-weight: 500;
|
|
||||||
overflow: hidden;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
font-size: 14px;
|
|
||||||
background: var(--primary-header);
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
border: 2px solid var(--secondary-header);
|
|
||||||
|
|
||||||
a {
|
|
||||||
cursor: pointer;
|
|
||||||
&:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
svg {
|
|
||||||
flex-shrink: 0;
|
|
||||||
margin-inline-end: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
${(props) =>
|
|
||||||
props.warning &&
|
|
||||||
css`
|
|
||||||
color: var(--warning);
|
|
||||||
border: 2px solid var(--warning);
|
|
||||||
background: var(--secondary-header);
|
|
||||||
`}
|
|
||||||
|
|
||||||
${(props) =>
|
|
||||||
props.error &&
|
|
||||||
css`
|
|
||||||
color: white;
|
|
||||||
border: 2px solid var(--error);
|
|
||||||
background: var(--error);
|
|
||||||
`}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default function Tip(
|
|
||||||
props: Props & { children: Children; hideSeparator?: boolean },
|
|
||||||
) {
|
|
||||||
const { children, hideSeparator, ...tipProps } = props;
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{!hideSeparator && <Separator />}
|
|
||||||
<TipBase {...tipProps}>
|
|
||||||
<InfoCircle size={20} />
|
|
||||||
<span>{children}</span>
|
|
||||||
</TipBase>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -3,7 +3,8 @@ import styled from "styled-components/macro";
|
||||||
import { RefObject } from "preact";
|
import { RefObject } from "preact";
|
||||||
import { useEffect, useLayoutEffect, useRef } from "preact/hooks";
|
import { useEffect, useLayoutEffect, useRef } from "preact/hooks";
|
||||||
|
|
||||||
import TextArea, { TextAreaProps } from "../components/ui/TextArea";
|
import { TextArea } from "@revoltchat/ui";
|
||||||
|
import type { TextAreaProps } from "@revoltchat/ui/esm/components/design/atoms/inputs/TextArea";
|
||||||
|
|
||||||
import { internalSubscribe } from "./eventEmitter";
|
import { internalSubscribe } from "./eventEmitter";
|
||||||
import { isTouchscreenDevice } from "./isTouchscreenDevice";
|
import { isTouchscreenDevice } from "./isTouchscreenDevice";
|
||||||
|
|
|
@ -4,14 +4,13 @@ import styled from "styled-components/macro";
|
||||||
|
|
||||||
import { useEffect, useState } from "preact/hooks";
|
import { useEffect, useState } from "preact/hooks";
|
||||||
|
|
||||||
import { Button, ComboBox, Preloader } from "@revoltchat/ui";
|
import { Button, ComboBox, Preloader, Tip } from "@revoltchat/ui";
|
||||||
|
|
||||||
import { useClient } from "../../context/revoltjs/RevoltClient";
|
import { useClient } from "../../context/revoltjs/RevoltClient";
|
||||||
|
|
||||||
import UserIcon from "../../components/common/user/UserIcon";
|
import UserIcon from "../../components/common/user/UserIcon";
|
||||||
import Markdown from "../../components/markdown/Markdown";
|
import Markdown from "../../components/markdown/Markdown";
|
||||||
import Overline from "../../components/ui/Overline";
|
import Overline from "../../components/ui/Overline";
|
||||||
import Tip from "../../components/ui/Tip";
|
|
||||||
|
|
||||||
const BotInfo = styled.div`
|
const BotInfo = styled.div`
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
|
@ -46,9 +45,7 @@ export default function InviteBot() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ padding: "6em" }}>
|
<div style={{ padding: "6em" }}>
|
||||||
<Tip warning hideSeparator>
|
<Tip palette="warning">This section is under construction.</Tip>
|
||||||
This section is under construction.
|
|
||||||
</Tip>
|
|
||||||
{typeof data === "undefined" && <Preloader type="spinner" />}
|
{typeof data === "undefined" && <Preloader type="spinner" />}
|
||||||
{data && (
|
{data && (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -14,7 +14,7 @@ import styles from "./Panes.module.scss";
|
||||||
import { Text } from "preact-i18n";
|
import { Text } from "preact-i18n";
|
||||||
import { useContext, useEffect, useState } from "preact/hooks";
|
import { useContext, useEffect, useState } from "preact/hooks";
|
||||||
|
|
||||||
import { Button } from "@revoltchat/ui";
|
import { Button, LineDivider, Tip } from "@revoltchat/ui";
|
||||||
|
|
||||||
import { stopPropagation } from "../../../lib/stopPropagation";
|
import { stopPropagation } from "../../../lib/stopPropagation";
|
||||||
|
|
||||||
|
@ -27,7 +27,6 @@ import {
|
||||||
|
|
||||||
import Tooltip from "../../../components/common/Tooltip";
|
import Tooltip from "../../../components/common/Tooltip";
|
||||||
import UserIcon from "../../../components/common/user/UserIcon";
|
import UserIcon from "../../../components/common/user/UserIcon";
|
||||||
import Tip from "../../../components/ui/Tip";
|
|
||||||
import CategoryButton from "../../../components/ui/fluent/CategoryButton";
|
import CategoryButton from "../../../components/ui/fluent/CategoryButton";
|
||||||
|
|
||||||
export const Account = observer(() => {
|
export const Account = observer(() => {
|
||||||
|
@ -227,6 +226,7 @@ export const Account = observer(() => {
|
||||||
<Text id="app.settings.pages.account.manage.delete" />
|
<Text id="app.settings.pages.account.manage.delete" />
|
||||||
</CategoryButton>
|
</CategoryButton>
|
||||||
</a>
|
</a>
|
||||||
|
<LineDivider />
|
||||||
<Tip>
|
<Tip>
|
||||||
<span>
|
<span>
|
||||||
<Text id="app.settings.tips.account.a" />
|
<Text id="app.settings.tips.account.a" />
|
||||||
|
|
|
@ -2,7 +2,7 @@ import styles from "./Panes.module.scss";
|
||||||
import { Text } from "preact-i18n";
|
import { Text } from "preact-i18n";
|
||||||
import { useEffect, useState } from "preact/hooks";
|
import { useEffect, useState } from "preact/hooks";
|
||||||
|
|
||||||
import { Button, ComboBox } from "@revoltchat/ui";
|
import { Button, ComboBox, Tip } from "@revoltchat/ui";
|
||||||
|
|
||||||
import { stopPropagation } from "../../../lib/stopPropagation";
|
import { stopPropagation } from "../../../lib/stopPropagation";
|
||||||
import { voiceState } from "../../../lib/vortex/VoiceState";
|
import { voiceState } from "../../../lib/vortex/VoiceState";
|
||||||
|
@ -10,7 +10,6 @@ import { voiceState } from "../../../lib/vortex/VoiceState";
|
||||||
import opusSVG from "../assets/opus_logo.svg";
|
import opusSVG from "../assets/opus_logo.svg";
|
||||||
|
|
||||||
import Overline from "../../../components/ui/Overline";
|
import Overline from "../../../components/ui/Overline";
|
||||||
import Tip from "../../../components/ui/Tip";
|
|
||||||
|
|
||||||
{
|
{
|
||||||
/*import OpusSVG from "../assets/opus_logo.svg";*/
|
/*import OpusSVG from "../assets/opus_logo.svg";*/
|
||||||
|
@ -96,13 +95,13 @@ export function Audio() {
|
||||||
<>
|
<>
|
||||||
<div class={styles.audio}>
|
<div class={styles.audio}>
|
||||||
{!permission && (
|
{!permission && (
|
||||||
<Tip error hideSeparator>
|
<Tip palette="error">
|
||||||
<Text id="app.settings.pages.audio.tip_grant_permission" />
|
<Text id="app.settings.pages.audio.tip_grant_permission" />
|
||||||
</Tip>
|
</Tip>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{error && permission === "prompt" && (
|
{error && permission === "prompt" && (
|
||||||
<Tip error hideSeparator>
|
<Tip palette="error">
|
||||||
<Text id="app.settings.pages.audio.tip_retry" />
|
<Text id="app.settings.pages.audio.tip_retry" />
|
||||||
<a onClick={handleAskForPermission}>
|
<a onClick={handleAskForPermission}>
|
||||||
<Text id="app.settings.pages.audio.button_retry" />
|
<Text id="app.settings.pages.audio.button_retry" />
|
||||||
|
|
|
@ -5,7 +5,7 @@ import styles from "./Panes.module.scss";
|
||||||
import { Text } from "preact-i18n";
|
import { Text } from "preact-i18n";
|
||||||
import { useMemo } from "preact/hooks";
|
import { useMemo } from "preact/hooks";
|
||||||
|
|
||||||
import { Checkbox } from "@revoltchat/ui";
|
import { Checkbox, LineDivider, Tip } from "@revoltchat/ui";
|
||||||
|
|
||||||
import { useApplicationState } from "../../../mobx/State";
|
import { useApplicationState } from "../../../mobx/State";
|
||||||
|
|
||||||
|
@ -23,7 +23,6 @@ import {
|
||||||
Languages as Langs,
|
Languages as Langs,
|
||||||
} from "../../../../external/lang/Languages";
|
} from "../../../../external/lang/Languages";
|
||||||
import Emoji from "../../../components/common/Emoji";
|
import Emoji from "../../../components/common/Emoji";
|
||||||
import Tip from "../../../components/ui/Tip";
|
|
||||||
|
|
||||||
type Key = [Language, LanguageEntry];
|
type Key = [Language, LanguageEntry];
|
||||||
|
|
||||||
|
@ -198,16 +197,17 @@ export const Languages = observer(() => {
|
||||||
.filter(([, lang]) => lang.cat === "alt")
|
.filter(([, lang]) => lang.cat === "alt")
|
||||||
.map(EntryFactory)}
|
.map(EntryFactory)}
|
||||||
</div>
|
</div>
|
||||||
|
<LineDivider />
|
||||||
<Tip>
|
<Tip>
|
||||||
<span>
|
<span>
|
||||||
<Text id="app.settings.tips.languages.a" />
|
<Text id="app.settings.tips.languages.a" />{" "}
|
||||||
</span>{" "}
|
<a
|
||||||
<a
|
href="https://weblate.insrt.uk/engage/revolt/?utm_source=widget"
|
||||||
href="https://weblate.insrt.uk/engage/revolt/?utm_source=widget"
|
target="_blank"
|
||||||
target="_blank"
|
rel="noreferrer">
|
||||||
rel="noreferrer">
|
<Text id="app.settings.tips.languages.b" />
|
||||||
<Text id="app.settings.tips.languages.b" />
|
</a>
|
||||||
</a>
|
</span>
|
||||||
</Tip>
|
</Tip>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -11,7 +11,7 @@ import styles from "./Panes.module.scss";
|
||||||
import { Text } from "preact-i18n";
|
import { Text } from "preact-i18n";
|
||||||
import { useCallback, useEffect, useState } from "preact/hooks";
|
import { useCallback, useEffect, useState } from "preact/hooks";
|
||||||
|
|
||||||
import { Button, Checkbox, InputBox } from "@revoltchat/ui";
|
import { Button, Checkbox, InputBox, Tip } from "@revoltchat/ui";
|
||||||
|
|
||||||
import TextAreaAutoSize from "../../../lib/TextAreaAutoSize";
|
import TextAreaAutoSize from "../../../lib/TextAreaAutoSize";
|
||||||
import { internalEmit } from "../../../lib/eventEmitter";
|
import { internalEmit } from "../../../lib/eventEmitter";
|
||||||
|
@ -28,7 +28,6 @@ import AutoComplete, {
|
||||||
import CollapsibleSection from "../../../components/common/CollapsibleSection";
|
import CollapsibleSection from "../../../components/common/CollapsibleSection";
|
||||||
import Tooltip from "../../../components/common/Tooltip";
|
import Tooltip from "../../../components/common/Tooltip";
|
||||||
import UserIcon from "../../../components/common/user/UserIcon";
|
import UserIcon from "../../../components/common/user/UserIcon";
|
||||||
import Tip from "../../../components/ui/Tip";
|
|
||||||
import CategoryButton from "../../../components/ui/fluent/CategoryButton";
|
import CategoryButton from "../../../components/ui/fluent/CategoryButton";
|
||||||
|
|
||||||
interface Data {
|
interface Data {
|
||||||
|
@ -465,9 +464,7 @@ function BotCard({ bot, onDelete, onUpdate }: Props) {
|
||||||
|
|
||||||
{error && (
|
{error && (
|
||||||
<div className={styles.botSection}>
|
<div className={styles.botSection}>
|
||||||
<Tip error hideSeparator>
|
<Tip palette="error">{error}</Tip>
|
||||||
{error}
|
|
||||||
</Tip>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
|
@ -2,11 +2,10 @@ import { Refresh } from "@styled-icons/boxicons-regular";
|
||||||
|
|
||||||
import { useEffect, useState } from "preact/hooks";
|
import { useEffect, useState } from "preact/hooks";
|
||||||
|
|
||||||
import { Button, Checkbox } from "@revoltchat/ui";
|
import { Button, Checkbox, Tip } from "@revoltchat/ui";
|
||||||
|
|
||||||
import RLogo from "../assets/revolt_r.svg";
|
import RLogo from "../assets/revolt_r.svg";
|
||||||
|
|
||||||
import Tip from "../../../components/ui/Tip";
|
|
||||||
import CategoryButton from "../../../components/ui/fluent/CategoryButton";
|
import CategoryButton from "../../../components/ui/fluent/CategoryButton";
|
||||||
|
|
||||||
export function Native() {
|
export function Native() {
|
||||||
|
@ -27,7 +26,7 @@ export function Native() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ marginTop: "10px" }}>
|
<div style={{ marginTop: "10px" }}>
|
||||||
<Tip hideSeparator>Some options might require a restart.</Tip>
|
<Tip>Some options might require a restart.</Tip>
|
||||||
<h3>App Behavior</h3>
|
<h3>App Behavior</h3>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
value={autoStart ?? false}
|
value={autoStart ?? false}
|
||||||
|
|
|
@ -3,12 +3,10 @@ import { observer } from "mobx-react-lite";
|
||||||
import styles from "./Panes.module.scss";
|
import styles from "./Panes.module.scss";
|
||||||
import { Text } from "preact-i18n";
|
import { Text } from "preact-i18n";
|
||||||
|
|
||||||
import { Button, Checkbox } from "@revoltchat/ui";
|
import { Button, Checkbox, Tip } from "@revoltchat/ui";
|
||||||
|
|
||||||
import { useApplicationState } from "../../../mobx/State";
|
import { useApplicationState } from "../../../mobx/State";
|
||||||
|
|
||||||
import Tip from "../../../components/ui/Tip";
|
|
||||||
|
|
||||||
// Just keeping this here for general purpose. Should probably be exported
|
// Just keeping this here for general purpose. Should probably be exported
|
||||||
// elsewhere, though.
|
// elsewhere, though.
|
||||||
interface Plugin {
|
interface Plugin {
|
||||||
|
@ -69,7 +67,7 @@ export const PluginsPage = observer(() => {
|
||||||
const plugins = useApplicationState().plugins;
|
const plugins = useApplicationState().plugins;
|
||||||
return (
|
return (
|
||||||
<div className={styles.plugins}>
|
<div className={styles.plugins}>
|
||||||
<Tip error hideSeparator>
|
<Tip palette="error">
|
||||||
<Text id="app.settings.pages.plugins.wip" />
|
<Text id="app.settings.pages.plugins.wip" />
|
||||||
</Tip>
|
</Tip>
|
||||||
{plugins.list().map((plugin) => {
|
{plugins.list().map((plugin) => {
|
||||||
|
|
|
@ -7,7 +7,7 @@ import styles from "./Panes.module.scss";
|
||||||
import { Text } from "preact-i18n";
|
import { Text } from "preact-i18n";
|
||||||
import { useCallback, useContext, useEffect, useState } from "preact/hooks";
|
import { useCallback, useContext, useEffect, useState } from "preact/hooks";
|
||||||
|
|
||||||
import { Button } from "@revoltchat/ui";
|
import { Button, LineDivider, Tip } from "@revoltchat/ui";
|
||||||
|
|
||||||
import TextAreaAutoSize from "../../../lib/TextAreaAutoSize";
|
import TextAreaAutoSize from "../../../lib/TextAreaAutoSize";
|
||||||
import { useTranslation } from "../../../lib/i18n";
|
import { useTranslation } from "../../../lib/i18n";
|
||||||
|
@ -23,7 +23,6 @@ import {
|
||||||
import AutoComplete, {
|
import AutoComplete, {
|
||||||
useAutoComplete,
|
useAutoComplete,
|
||||||
} from "../../../components/common/AutoComplete";
|
} from "../../../components/common/AutoComplete";
|
||||||
import Tip from "../../../components/ui/Tip";
|
|
||||||
|
|
||||||
export const Profile = observer(() => {
|
export const Profile = observer(() => {
|
||||||
const status = useContext(StatusContext);
|
const status = useContext(StatusContext);
|
||||||
|
@ -203,11 +202,15 @@ export const Profile = observer(() => {
|
||||||
<Text id="app.special.modals.actions.save" />
|
<Text id="app.special.modals.actions.save" />
|
||||||
</Button>
|
</Button>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<LineDivider />
|
||||||
<Tip>
|
<Tip>
|
||||||
<span>Want to change your username?</span>{" "}
|
<span>
|
||||||
<a onClick={() => switchPage("account")}>
|
Want to change your username?{" "}
|
||||||
Head over to your account settings.
|
<a onClick={() => switchPage("account")}>
|
||||||
</a>
|
Head over to your account settings.
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
</Tip>
|
</Tip>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -18,13 +18,12 @@ import styles from "./Panes.module.scss";
|
||||||
import { Text } from "preact-i18n";
|
import { Text } from "preact-i18n";
|
||||||
import { useContext, useEffect, useState } from "preact/hooks";
|
import { useContext, useEffect, useState } from "preact/hooks";
|
||||||
|
|
||||||
import { Button, Preloader } from "@revoltchat/ui";
|
import { Button, LineDivider, Preloader, Tip } from "@revoltchat/ui";
|
||||||
|
|
||||||
import { dayjs } from "../../../context/Locale";
|
import { dayjs } from "../../../context/Locale";
|
||||||
import { useIntermediate } from "../../../context/intermediate/Intermediate";
|
import { useIntermediate } from "../../../context/intermediate/Intermediate";
|
||||||
import { AppContext } from "../../../context/revoltjs/RevoltClient";
|
import { AppContext } from "../../../context/revoltjs/RevoltClient";
|
||||||
|
|
||||||
import Tip from "../../../components/ui/Tip";
|
|
||||||
import CategoryButton from "../../../components/ui/fluent/CategoryButton";
|
import CategoryButton from "../../../components/ui/fluent/CategoryButton";
|
||||||
|
|
||||||
dayjs.extend(relativeTime);
|
dayjs.extend(relativeTime);
|
||||||
|
@ -248,13 +247,14 @@ export function Sessions() {
|
||||||
<Text id="app.settings.pages.sessions.logout" />
|
<Text id="app.settings.pages.sessions.logout" />
|
||||||
</CategoryButton>
|
</CategoryButton>
|
||||||
|
|
||||||
|
<LineDivider />
|
||||||
<Tip>
|
<Tip>
|
||||||
<span>
|
<span>
|
||||||
<Text id="app.settings.tips.sessions.a" />
|
<Text id="app.settings.tips.sessions.a" />{" "}
|
||||||
</span>{" "}
|
<a onClick={() => switchPage("account")}>
|
||||||
<a onClick={() => switchPage("account")}>
|
<Text id="app.settings.tips.sessions.b" />
|
||||||
<Text id="app.settings.tips.sessions.b" />
|
</a>
|
||||||
</a>
|
</span>
|
||||||
</Tip>
|
</Tip>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,399 +0,0 @@
|
||||||
/**
|
|
||||||
* ! DEPRECATED FILE
|
|
||||||
* ! DO NOT IMPORT
|
|
||||||
*
|
|
||||||
* Replaced by Revolt Discover
|
|
||||||
*/
|
|
||||||
import { Check } from "@styled-icons/boxicons-regular";
|
|
||||||
import {
|
|
||||||
Star,
|
|
||||||
Brush,
|
|
||||||
Bookmark,
|
|
||||||
BarChartAlt2,
|
|
||||||
} from "@styled-icons/boxicons-solid";
|
|
||||||
import styled from "styled-components/macro";
|
|
||||||
|
|
||||||
import { Text } from "preact-i18n";
|
|
||||||
import { useEffect, useState } from "preact/hooks";
|
|
||||||
|
|
||||||
import { useApplicationState } from "../../../mobx/State";
|
|
||||||
|
|
||||||
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(),
|
|
||||||
);
|
|
||||||
|
|
||||||
export const fetchTheme = (slug: string): Promise<Theme> =>
|
|
||||||
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<string, ThemeMetadata>;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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<JSX.HTMLAttributes<SVGSVGElement>, "as"> & {
|
|
||||||
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>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
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<Record<string, Theme>>({});
|
|
||||||
|
|
||||||
const themes = useApplicationState().settings.theme;
|
|
||||||
|
|
||||||
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 (
|
|
||||||
<ThemeShopRoot>
|
|
||||||
<h5>
|
|
||||||
<Text id="app.settings.pages.theme_shop.description" />
|
|
||||||
</h5>
|
|
||||||
{/*<LoadFail>
|
|
||||||
<h5>
|
|
||||||
Oops! Couldn't load the theme shop. Make sure you're
|
|
||||||
connected to the internet and try again.
|
|
||||||
</h5>
|
|
||||||
</LoadFail>*/}
|
|
||||||
<Tip warning hideSeparator>
|
|
||||||
The Theme Shop is currently under construction.
|
|
||||||
</Tip>
|
|
||||||
|
|
||||||
{/* FIXME INTEGRATE WITH MOBX */}
|
|
||||||
{/*<ActiveTheme>
|
|
||||||
<div class="active-indicator">
|
|
||||||
<Check size="16" />
|
|
||||||
<Text id="app.settings.pages.theme_shop.active" />
|
|
||||||
</div>
|
|
||||||
<div class="container">
|
|
||||||
<div class="theme">theme svg goes here</div>
|
|
||||||
<div class="info">
|
|
||||||
<div class="title">Theme Title</div>
|
|
||||||
<div class="author">
|
|
||||||
<Text id="app.settings.pages.theme_shop.by" />{" "}
|
|
||||||
Author
|
|
||||||
</div>
|
|
||||||
<h5>This is a theme description.</h5>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ActiveTheme>
|
|
||||||
<InputBox placeholder="<Text id="app.settings.pages.theme_shop.search" />" contrast />
|
|
||||||
<Category>
|
|
||||||
<div class="title">
|
|
||||||
<Bookmark size={16} />
|
|
||||||
<Text id="app.settings.pages.theme_shop.category.saved" />
|
|
||||||
</div>
|
|
||||||
<a class="view">
|
|
||||||
<Text id="app.settings.pages.theme_shop.category.manage" />
|
|
||||||
</a>
|
|
||||||
</Category>
|
|
||||||
|
|
||||||
<Category>
|
|
||||||
<div class="title">
|
|
||||||
<Star size={16} />
|
|
||||||
<Text id="app.settings.pages.theme_shop.category.new" />
|
|
||||||
</div>
|
|
||||||
<a class="view">
|
|
||||||
<Text id="app.settings.pages.theme_shop.category.viewall" />
|
|
||||||
</a>
|
|
||||||
</Category>
|
|
||||||
|
|
||||||
<Category>
|
|
||||||
<div class="title">
|
|
||||||
<BarChartAlt2 size={16} />
|
|
||||||
<Text id="app.settings.pages.theme_shop.category.highest" />
|
|
||||||
</div>
|
|
||||||
<a class="view">
|
|
||||||
<Text id="app.settings.pages.theme_shop.category.viewall" />
|
|
||||||
</a>
|
|
||||||
</Category>
|
|
||||||
|
|
||||||
<Category>
|
|
||||||
<div class="title">
|
|
||||||
<Brush size={16} />
|
|
||||||
<Text id="app.settings.pages.theme_shop.category.default" />
|
|
||||||
</div>
|
|
||||||
<a class="view">
|
|
||||||
<Text id="app.settings.pages.theme_shop.category.viewall" />
|
|
||||||
</a>
|
|
||||||
</Category>*/}
|
|
||||||
<hr />
|
|
||||||
<ThemeList>
|
|
||||||
{themeList?.map(([slug, theme]) => (
|
|
||||||
<ThemeInfo
|
|
||||||
key={slug}
|
|
||||||
data-loaded={Reflect.has(themeData, slug)}>
|
|
||||||
<button
|
|
||||||
class="preview"
|
|
||||||
onClick={() =>
|
|
||||||
themes.hydrate(themeData[slug], true)
|
|
||||||
}>
|
|
||||||
<div class="previewBox">
|
|
||||||
<div class="hover">
|
|
||||||
<Text id="app.settings.pages.theme_shop.use" />
|
|
||||||
</div>
|
|
||||||
<ThemePreview
|
|
||||||
slug={slug}
|
|
||||||
theme={themeData[slug]}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
<h1 class="name">{theme.name}</h1>
|
|
||||||
{/* Maybe id's of the users should be included as well / instead? */}
|
|
||||||
<div class="creator">
|
|
||||||
<Text id="app.settings.pages.theme_shop.by" />{" "}
|
|
||||||
{theme.creator}
|
|
||||||
</div>
|
|
||||||
<h5 class="description">{theme.description}</h5>
|
|
||||||
</ThemeInfo>
|
|
||||||
))}
|
|
||||||
</ThemeList>
|
|
||||||
</ThemeShopRoot>
|
|
||||||
);
|
|
||||||
}
|
|
Loading…
Reference in a new issue