feat(@ui): migrate category / overline and header

This commit is contained in:
Paul Makles 2022-05-30 14:42:09 +01:00
parent 673efc0586
commit 68b9d5ea79
35 changed files with 187 additions and 384 deletions

View file

@ -5,16 +5,17 @@ import { useTriggerEvents } from "preact-context-menu";
import { memo } from "preact/compat"; import { memo } from "preact/compat";
import { useEffect, useState } from "preact/hooks"; import { useEffect, useState } from "preact/hooks";
import { Category } from "@revoltchat/ui";
import { internalEmit } from "../../../lib/eventEmitter"; import { internalEmit } from "../../../lib/eventEmitter";
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice"; import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
import { QueuedMessage } from "../../../mobx/stores/MessageQueue"; import { QueuedMessage } from "../../../mobx/stores/MessageQueue";
import { I18nError } from "../../../context/Locale";
import { useIntermediate } from "../../../context/intermediate/Intermediate"; import { useIntermediate } from "../../../context/intermediate/Intermediate";
import { useClient } from "../../../context/revoltjs/RevoltClient"; import { useClient } from "../../../context/revoltjs/RevoltClient";
import Overline from "../../ui/Overline";
import { Children } from "../../../types/Preact"; import { Children } from "../../../types/Preact";
import Markdown from "../../markdown/Markdown"; import Markdown from "../../markdown/Markdown";
import UserIcon from "../user/UserIcon"; import UserIcon from "../user/UserIcon";
@ -162,7 +163,9 @@ const Message = observer(
{replacement ?? <Markdown content={content} />} {replacement ?? <Markdown content={content} />}
{!queued && <InviteList message={message} />} {!queued && <InviteList message={message} />}
{queued?.error && ( {queued?.error && (
<Overline type="error" error={queued.error} /> <Category>
<I18nError error={queued.error} />
</Category>
)} )}
{message.attachments?.map((attachment, index) => ( {message.attachments?.map((attachment, index) => (
<Attachment <Attachment

View file

@ -7,10 +7,11 @@ import styled, { css } from "styled-components/macro";
import { useContext, useEffect, useState } from "preact/hooks"; import { useContext, useEffect, useState } from "preact/hooks";
import { Button, Preloader } from "@revoltchat/ui"; import { Button, Category, Preloader } from "@revoltchat/ui";
import { isTouchscreenDevice } from "../../../../lib/isTouchscreenDevice"; import { isTouchscreenDevice } from "../../../../lib/isTouchscreenDevice";
import { I18nError } from "../../../../context/Locale";
import { import {
AppContext, AppContext,
ClientStatus, ClientStatus,
@ -19,7 +20,6 @@ import {
import { takeError } from "../../../../context/revoltjs/util"; import { takeError } from "../../../../context/revoltjs/util";
import ServerIcon from "../../../../components/common/ServerIcon"; import ServerIcon from "../../../../components/common/ServerIcon";
import Overline from "../../../ui/Overline";
const EmbedInviteBase = styled.div` const EmbedInviteBase = styled.div`
width: 400px; width: 400px;
@ -159,7 +159,11 @@ export function EmbedInvite({ code }: Props) {
</Button> </Button>
)} )}
</EmbedInviteBase> </EmbedInviteBase>
{joinError && <Overline type="error" error={joinError} />} {joinError && (
<Category>
<I18nError error={joinError} />
</Category>
)}
</> </>
); );
} }

View file

@ -7,13 +7,12 @@ import styled from "styled-components/macro";
import { openContextMenu } from "preact-context-menu"; import { openContextMenu } from "preact-context-menu";
import { Text, Localizer } from "preact-i18n"; import { Text, Localizer } from "preact-i18n";
import { IconButton } from "@revoltchat/ui"; import { Header, IconButton } from "@revoltchat/ui";
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice"; import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
import { useIntermediate } from "../../../context/intermediate/Intermediate"; import { useIntermediate } from "../../../context/intermediate/Intermediate";
import Header from "../../ui/Header";
import Tooltip from "../Tooltip"; import Tooltip from "../Tooltip";
import UserStatus from "./UserStatus"; import UserStatus from "./UserStatus";
@ -52,7 +51,7 @@ export default observer(({ user }: Props) => {
const { writeClipboard } = useIntermediate(); const { writeClipboard } = useIntermediate();
return ( return (
<Header topBorder placement="secondary"> <Header topBorder palette="secondary">
<HeaderBase> <HeaderBase>
<Localizer> <Localizer>
<Tooltip content={<Text id="app.special.copy_username" />}> <Tooltip content={<Text id="app.special.copy_username" />}>

View file

@ -1,3 +1,4 @@
import { Plus } from "@styled-icons/boxicons-regular";
import { import {
Home, Home,
UserDetail, UserDetail,
@ -11,6 +12,8 @@ import styled, { css } from "styled-components/macro";
import { Text } from "preact-i18n"; import { Text } from "preact-i18n";
import { useContext, useEffect } from "preact/hooks"; import { useContext, useEffect } from "preact/hooks";
import { Category, IconButton } from "@revoltchat/ui";
import ConditionalLink from "../../../lib/ConditionalLink"; import ConditionalLink from "../../../lib/ConditionalLink";
import PaintCounter from "../../../lib/PaintCounter"; import PaintCounter from "../../../lib/PaintCounter";
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice"; import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
@ -20,7 +23,6 @@ import { useApplicationState } from "../../../mobx/State";
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 Category from "../../ui/Category";
import placeholderSVG from "../items/placeholder.svg"; import placeholderSVG from "../items/placeholder.svg";
import { GenericSidebarBase, GenericSidebarList } from "../SidebarBase"; import { GenericSidebarBase, GenericSidebarList } from "../SidebarBase";
@ -125,15 +127,18 @@ export default observer(() => {
</ButtonItem> </ButtonItem>
</Link> </Link>
)} )}
<Category <Category>
text={<Text id="app.main.categories.conversations" />} <Text id="app.main.categories.conversations" />
action={() => <IconButton
onClick={() =>
openScreen({ openScreen({
id: "special_input", id: "special_input",
type: "create_group", type: "create_group",
}) })
} }>
/> <Plus size={16} />
</IconButton>
</Category>
{channels.length === 0 && ( {channels.length === 0 && (
<img src={placeholderSVG} loading="eager" /> <img src={placeholderSVG} loading="eager" />
)} )}

View file

@ -30,11 +30,14 @@ export default observer(() => {
return ( return (
<ServerList <ServerList
// @ts-expect-error FIXME
client={client} client={client}
active={server_id} active={server_id}
createServer={createServer} createServer={createServer}
// @ts-expect-error FIXME
permit={state.notifications} permit={state.notifications}
home={state.layout.getLastHomePath} home={state.layout.getLastHomePath}
// @ts-expect-error FIXME
servers={state.ordering.orderedServers} servers={state.ordering.orderedServers}
reorder={state.ordering.reorderServer} reorder={state.ordering.reorderServer}
/> />

View file

@ -1,12 +1,12 @@
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { Redirect, useParams } from "react-router"; import { Redirect, useParams } from "react-router";
import { Server } from "revolt.js";
import styled, { css } from "styled-components/macro"; import styled, { css } from "styled-components/macro";
import { Ref } from "preact";
import { useTriggerEvents } from "preact-context-menu"; import { useTriggerEvents } from "preact-context-menu";
import { useEffect } from "preact/hooks"; import { useEffect } from "preact/hooks";
import { Category } from "@revoltchat/ui";
import ConditionalLink from "../../../lib/ConditionalLink"; import ConditionalLink from "../../../lib/ConditionalLink";
import PaintCounter from "../../../lib/PaintCounter"; import PaintCounter from "../../../lib/PaintCounter";
import { internalEmit } from "../../../lib/eventEmitter"; import { internalEmit } from "../../../lib/eventEmitter";
@ -18,8 +18,6 @@ import { useClient } from "../../../context/revoltjs/RevoltClient";
import CollapsibleSection from "../../common/CollapsibleSection"; import CollapsibleSection from "../../common/CollapsibleSection";
import ServerHeader from "../../common/ServerHeader"; import ServerHeader from "../../common/ServerHeader";
import Category from "../../ui/Category";
import { ChannelButton } from "../items/ButtonItem"; import { ChannelButton } from "../items/ButtonItem";
import ConnectionStatus from "../items/ConnectionStatus"; import ConnectionStatus from "../items/ConnectionStatus";
@ -126,7 +124,7 @@ export default observer(() => {
<CollapsibleSection <CollapsibleSection
id={`category_${category.id}`} id={`category_${category.id}`}
defaultValue defaultValue
summary={<Category text={category.title} />}> summary={<Category>{category.title}</Category>}>
{channels} {channels}
</CollapsibleSection>, </CollapsibleSection>,
); );

View file

@ -5,12 +5,11 @@ import styled from "styled-components/macro";
import { Text } from "preact-i18n"; import { Text } from "preact-i18n";
import { useEffect, useState } from "preact/hooks"; import { useEffect, useState } from "preact/hooks";
import { Button, InputBox, Preloader } from "@revoltchat/ui"; import { Button, Category, Error, InputBox, Preloader } from "@revoltchat/ui";
import { useClient } from "../../../context/revoltjs/RevoltClient"; import { useClient } from "../../../context/revoltjs/RevoltClient";
import Message from "../../common/messaging/Message"; import Message from "../../common/messaging/Message";
import Overline from "../../ui/Overline";
import { GenericSidebarBase, GenericSidebarList } from "../SidebarBase"; import { GenericSidebarBase, GenericSidebarList } from "../SidebarBase";
type SearchState = type SearchState =
@ -100,12 +99,14 @@ export function SearchSidebar({ close }: Props) {
<GenericSidebarBase data-scroll-offset="with-padding"> <GenericSidebarBase data-scroll-offset="with-padding">
<GenericSidebarList> <GenericSidebarList>
<SearchBase> <SearchBase>
<Overline type="accent" block hover> <Category>
<a onClick={close}>« back to members</a> <Error
</Overline> error={<a onClick={close}>« back to members</a>}
<Overline type="subtle" block> />
</Category>
<Category>
<Text id="app.main.channel.search.title" /> <Text id="app.main.channel.search.title" />
</Overline> </Category>
<InputBox <InputBox
value={query} value={query}
onKeyDown={(e) => e.key === "Enter" && search()} onKeyDown={(e) => e.key === "Enter" && search()}

View file

@ -1,55 +0,0 @@
import { Plus } from "@styled-icons/boxicons-regular";
import styled, { css } from "styled-components/macro";
import { Children } from "../../types/Preact";
const CategoryBase = styled.div<Pick<Props, "variant">>`
font-size: 12px;
font-weight: 700;
text-transform: uppercase;
margin-top: 4px;
padding: 6px 0 6px 8px;
margin-bottom: 4px;
white-space: nowrap;
display: flex;
align-items: center;
flex-direction: row;
justify-content: space-between;
svg {
cursor: pointer;
}
&:first-child {
margin-top: 0;
padding-top: 0;
}
${(props) =>
props.variant === "uniform" &&
css`
padding-top: 6px;
`}
`;
type Props = Omit<
JSX.HTMLAttributes<HTMLDivElement>,
"children" | "as" | "action"
> & {
text: Children;
action?: () => void;
variant?: "default" | "uniform";
};
export default function Category(props: Props) {
const { text, action, ...otherProps } = props;
return (
<CategoryBase {...otherProps}>
{text}
{action && <Plus size={16} onClick={action} />}
</CategoryBase>
);
}

View file

@ -7,6 +7,8 @@ import { observer } from "mobx-react-lite";
import { useLocation } from "react-router-dom"; import { useLocation } from "react-router-dom";
import styled, { css } from "styled-components/macro"; import styled, { css } from "styled-components/macro";
import { Header } from "@revoltchat/ui";
import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice"; import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
import { useApplicationState } from "../../mobx/State"; import { useApplicationState } from "../../mobx/State";
@ -18,82 +20,11 @@ interface Props {
topBorder?: boolean; topBorder?: boolean;
bottomBorder?: boolean; bottomBorder?: boolean;
background?: boolean; withBackground?: boolean;
transparent?: boolean; withTransparency?: boolean;
placement: "primary" | "secondary"; placement: "primary" | "secondary";
} }
const Header = styled.div<Props>`
gap: 10px;
flex: 0 auto;
display: flex;
flex-shrink: 0;
padding: 0 16px;
font-weight: 600;
user-select: none;
align-items: center;
height: var(--header-height);
background-size: cover !important;
background-position: center !important;
svg {
flex-shrink: 0;
}
.menu {
margin-inline-end: 8px;
color: var(--secondary-foreground);
}
${(props) =>
props.transparent
? css`
background-color: rgba(
var(--primary-header-rgb),
max(var(--min-opacity), 0.75)
);
backdrop-filter: blur(20px);
z-index: 20;
position: absolute;
width: 100%;
`
: css`
background-color: var(--primary-header);
`}
${(props) =>
props.background &&
css`
height: 120px !important;
align-items: flex-end;
text-shadow: 0px 0px 1px black;
`}
${(props) =>
props.placement === "secondary" &&
css`
background-color: var(--secondary-header);
padding: 14px;
`}
${(props) =>
props.topBorder &&
css`
border-start-start-radius: 8px;
`}
${(props) =>
props.bottomBorder &&
css`
border-end-start-radius: 8px;
`}
`;
export default Header;
const IconContainer = styled.div` const IconContainer = styled.div`
display: flex; display: flex;
align-items: center; align-items: center;
@ -128,7 +59,7 @@ export const PageHeader = observer(
return ( return (
<Header <Header
{...props} {...props}
placement="primary" palette="primary"
topBorder={!visible} topBorder={!visible}
bottomBorder={!pathname.includes("/server")}> bottomBorder={!pathname.includes("/server")}>
{!noBurger && <HamburgerAction />} {!noBurger && <HamburgerAction />}

View file

@ -1,91 +0,0 @@
import styled, { css } from "styled-components/macro";
import { Text } from "preact-i18n";
import { Children } from "../../types/Preact";
type Props = Omit<JSX.HTMLAttributes<HTMLDivElement>, "children" | "as"> & {
error?: string;
hover?: boolean;
block?: boolean;
spaced?: boolean;
noMargin?: boolean;
children?: Children;
type?: "default" | "subtle" | "error" | "accent";
};
const OverlineBase = styled.div<Omit<Props, "children" | "error">>`
display: inline;
transition: 0.2s ease filter;
${(props) =>
props.hover &&
css`
cursor: pointer;
transition: 0.2s ease filter;
&:hover {
filter: brightness(1.2);
}
`}
${(props) =>
!props.noMargin &&
css`
margin: 0.4em 0;
`}
${(props) =>
props.spaced &&
css`
margin-top: 0.8em;
`}
font-size: 14px;
font-weight: 600;
color: var(--foreground);
text-transform: uppercase;
${(props) =>
props.type === "subtle" &&
css`
font-size: 12px;
color: var(--secondary-foreground);
`}
${(props) =>
props.type === "error" &&
css`
font-size: 12px;
font-weight: 400;
color: var(--error);
`}
${(props) =>
props.type === "accent" &&
css`
font-size: 12px;
font-weight: 400;
color: var(--accent);
`}
${(props) =>
props.block &&
css`
display: block;
`}
`;
export default function Overline(props: Props) {
return (
<OverlineBase {...props}>
{props.children}
{props.children && props.error && <> &middot; </>}
{props.error && (
<Overline type="error">
<Text id={`error.${props.error}`}>{props.error}</Text>
</Overline>
)}
</OverlineBase>
);
}

View file

@ -5,9 +5,11 @@ import update from "dayjs/plugin/updateLocale";
import defaultsDeep from "lodash.defaultsdeep"; import defaultsDeep from "lodash.defaultsdeep";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { IntlProvider } from "preact-i18n"; import { IntlProvider, Text } from "preact-i18n";
import { useCallback, useEffect, useState } from "preact/hooks"; import { useCallback, useEffect, useState } from "preact/hooks";
import { Error } from "@revoltchat/ui";
import { useApplicationState } from "../mobx/State"; import { useApplicationState } from "../mobx/State";
import { Languages } from "../../external/lang/Languages"; import { Languages } from "../../external/lang/Languages";
@ -143,3 +145,12 @@ function transformLanguage(source: Dictionary) {
return obj; return obj;
} }
export function I18nError({ error, children }: { error: any; children?: any }) {
return (
<Error
error={error ? <Text id={error} children={error} /> : undefined}
children={children}
/>
);
}

View file

@ -4,11 +4,11 @@ import { Server } from "revolt.js";
import { Text } from "preact-i18n"; import { Text } from "preact-i18n";
import { useContext, useState } from "preact/hooks"; import { useContext, useState } from "preact/hooks";
import { InputBox } from "@revoltchat/ui"; import { Category, InputBox } from "@revoltchat/ui";
import Modal from "../../../components/ui/Modal"; import Modal from "../../../components/ui/Modal";
import Overline from "../../../components/ui/Overline";
import { Children } from "../../../types/Preact"; import { Children } from "../../../types/Preact";
import { I18nError } from "../../Locale";
import { AppContext } from "../../revoltjs/RevoltClient"; import { AppContext } from "../../revoltjs/RevoltClient";
import { takeError } from "../../revoltjs/util"; import { takeError } from "../../revoltjs/util";
@ -60,11 +60,15 @@ export function InputModal({
]} ]}
onClose={onClose}> onClose={onClose}>
{field ? ( {field ? (
<Overline error={error} block> <Category>
{field} <I18nError error={error}>{field}</I18nError>
</Overline> </Category>
) : ( ) : (
error && <Overline error={error} type="error" block /> error && (
<Category>
<I18nError error={error} />
</Category>
)
)} )}
<InputBox <InputBox
value={value} value={value}

View file

@ -7,15 +7,15 @@ import styles from "./Prompt.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 { InputBox, Radio } from "@revoltchat/ui"; import { Category, Error, InputBox, Radio } from "@revoltchat/ui";
import { TextReact } from "../../../lib/i18n"; import { TextReact } from "../../../lib/i18n";
import Message from "../../../components/common/messaging/Message"; import Message from "../../../components/common/messaging/Message";
import UserIcon from "../../../components/common/user/UserIcon"; import UserIcon from "../../../components/common/user/UserIcon";
import Modal, { Action } from "../../../components/ui/Modal"; import Modal, { Action } from "../../../components/ui/Modal";
import Overline from "../../../components/ui/Overline";
import { Children } from "../../../types/Preact"; import { Children } from "../../../types/Preact";
import { I18nError } from "../../Locale";
import { AppContext } from "../../revoltjs/RevoltClient"; import { AppContext } from "../../revoltjs/RevoltClient";
import { takeError } from "../../revoltjs/util"; import { takeError } from "../../revoltjs/util";
import { useIntermediate } from "../Intermediate"; import { useIntermediate } from "../Intermediate";
@ -44,7 +44,11 @@ export function PromptModal({
actions={actions} actions={actions}
onClose={onClose} onClose={onClose}
disabled={disabled}> disabled={disabled}>
{error && <Overline error={error} type="error" />} {error && (
<Category>
<I18nError error={error} />
</Category>
)}
{content} {content}
</Modal> </Modal>
); );
@ -385,9 +389,9 @@ export const SpecialPromptModal = observer((props: SpecialProps) => {
id="app.special.modals.prompt.confirm_ban" id="app.special.modals.prompt.confirm_ban"
fields={{ name: props.user?.username }} fields={{ name: props.user?.username }}
/> />
<Overline> <Category>
<Text id="app.special.modals.prompt.confirm_ban_reason" /> <Text id="app.special.modals.prompt.confirm_ban_reason" />
</Overline> </Category>
<InputBox <InputBox
value={reason ?? ""} value={reason ?? ""}
onChange={(e) => onChange={(e) =>
@ -452,9 +456,9 @@ export const SpecialPromptModal = observer((props: SpecialProps) => {
]} ]}
content={ content={
<> <>
<Overline block type="subtle"> <Category>
<Text id="app.main.servers.channel_type" /> <Text id="app.main.servers.channel_type" />
</Overline> </Category>
<Radio <Radio
title={ title={
<Text id="app.main.servers.text_channel" /> <Text id="app.main.servers.text_channel" />
@ -469,9 +473,9 @@ export const SpecialPromptModal = observer((props: SpecialProps) => {
value={type === "Voice"} value={type === "Voice"}
onSelect={() => setType("Voice")} onSelect={() => setType("Voice")}
/> />
<Overline block type="subtle"> <Category>
<Text id="app.main.servers.channel_name" /> <Text id="app.main.servers.channel_name" />
</Overline> </Category>
<InputBox <InputBox
value={name} value={name}
onChange={(e) => setName(e.currentTarget.value)} onChange={(e) => setName(e.currentTarget.value)}
@ -527,9 +531,9 @@ export const SpecialPromptModal = observer((props: SpecialProps) => {
]} ]}
content={ content={
<> <>
<Overline block type="subtle"> <Category>
<Text id="app.main.servers.category_name" /> <Text id="app.main.servers.category_name" />
</Overline> </Category>
<InputBox <InputBox
value={name} value={name}
onChange={(e) => setName(e.currentTarget.value)} onChange={(e) => setName(e.currentTarget.value)}

View file

@ -4,10 +4,11 @@ import { API } from "revolt.js";
import { Text } from "preact-i18n"; import { Text } from "preact-i18n";
import { useContext, useState } from "preact/hooks"; import { useContext, useState } from "preact/hooks";
import Modal from "../../../components/ui/Modal"; import { Category } from "@revoltchat/ui";
import Overline from "../../../components/ui/Overline";
import Modal from "../../../components/ui/Modal";
import FormField from "../../../pages/login/FormField"; import FormField from "../../../pages/login/FormField";
import { I18nError } from "../../Locale";
import { AppContext } from "../../revoltjs/RevoltClient"; import { AppContext } from "../../revoltjs/RevoltClient";
import { takeError } from "../../revoltjs/util"; import { takeError } from "../../revoltjs/util";
@ -70,9 +71,10 @@ export function CreateBotModal({ onClose, onCreate }: Props) {
error={errors.name?.message} error={errors.name?.message}
/> />
{error && ( {error && (
<Overline type="error" error={error}> <Category>
<Text id="app.special.popovers.create_bot.failed" /> <Text id="app.special.popovers.create_bot.failed" />{" "}
</Overline> &middot; <I18nError error={error} />
</Category>
)} )}
</form> </form>
</Modal> </Modal>

View file

@ -3,9 +3,9 @@ import { SubmitHandler, useForm } from "react-hook-form";
import { Text } from "preact-i18n"; import { Text } from "preact-i18n";
import { useContext, useState } from "preact/hooks"; import { useContext, useState } from "preact/hooks";
import Modal from "../../../components/ui/Modal"; import { Category, Error } from "@revoltchat/ui";
import Overline from "../../../components/ui/Overline";
import Modal from "../../../components/ui/Modal";
import FormField from "../../../pages/login/FormField"; import FormField from "../../../pages/login/FormField";
import { AppContext } from "../../revoltjs/RevoltClient"; import { AppContext } from "../../revoltjs/RevoltClient";
import { takeError } from "../../revoltjs/util"; import { takeError } from "../../revoltjs/util";
@ -140,9 +140,13 @@ export function ModifyAccountModal({ onClose, field }: Props) {
disabled={processing} disabled={processing}
/> />
{error && ( {error && (
<Overline type="error" error={error}> <Category compact>
<Error
error={
<Text id="app.special.modals.account.failed" /> <Text id="app.special.modals.account.failed" />
</Overline> }
/>
</Category>
)} )}
</form> </form>
</Modal> </Modal>

View file

@ -5,12 +5,11 @@ import styles from "./ServerIdentityModal.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, InputBox } from "@revoltchat/ui"; import { Button, Category, InputBox } from "@revoltchat/ui";
import { noop } from "../../../lib/js"; import { noop } from "../../../lib/js";
import Modal from "../../../components/ui/Modal"; import Modal from "../../../components/ui/Modal";
import Overline from "../../../components/ui/Overline";
import { FileUploader } from "../../revoltjs/FileUploads"; import { FileUploader } from "../../revoltjs/FileUploads";
import { useClient } from "../../revoltjs/RevoltClient"; import { useClient } from "../../revoltjs/RevoltClient";
@ -47,9 +46,9 @@ export const ServerIdentityModal = observer(({ server, onClose }: Props) => {
onClose={onClose}> onClose={onClose}>
<div className={styles.identityMain}> <div className={styles.identityMain}>
<div> <div>
<Overline type="subtle"> <Category>
<Text id="app.special.popovers.server_identity.avatar" /> <Text id="app.special.popovers.server_identity.avatar" />
</Overline> </Category>
<FileUploader <FileUploader
width={80} width={80}
height={80} height={80}
@ -78,9 +77,9 @@ export const ServerIdentityModal = observer(({ server, onClose }: Props) => {
/> />
</div> </div>
<div> <div>
<Overline type="subtle"> <Category>
<Text id="app.special.popovers.server_identity.nickname" /> <Text id="app.special.popovers.server_identity.nickname" />
</Overline> </Category>
<InputBox <InputBox
value={nickname} value={nickname}
placeholder={client.user!.username} placeholder={client.user!.username}

View file

@ -4,7 +4,6 @@ import { useState } from "preact/hooks";
import UserCheckbox from "../../../components/common/user/UserCheckbox"; import UserCheckbox from "../../../components/common/user/UserCheckbox";
import Modal from "../../../components/ui/Modal"; import Modal from "../../../components/ui/Modal";
import { useClient } from "../../revoltjs/RevoltClient"; import { useClient } from "../../revoltjs/RevoltClient";
interface Props { interface Props {
@ -42,7 +41,7 @@ export function UserPicker(props: Props) {
<UserCheckbox <UserCheckbox
key={x._id} key={x._id}
user={x} user={x}
checked={selected.includes(x._id)} value={selected.includes(x._id)}
onChange={(v) => { onChange={(v) => {
if (v) { if (v) {
setSelected([...selected, x._id]); setSelected([...selected, x._id]);

View file

@ -15,7 +15,7 @@ import styles from "./UserProfile.module.scss";
import { Localizer, Text } from "preact-i18n"; import { Localizer, Text } from "preact-i18n";
import { useContext, useEffect, useLayoutEffect, useState } from "preact/hooks"; import { useContext, useEffect, useLayoutEffect, useState } from "preact/hooks";
import { Button, IconButton, Preloader } from "@revoltchat/ui"; import { Button, Category, Error, IconButton, Preloader } from "@revoltchat/ui";
import { noop } from "../../../lib/js"; import { noop } from "../../../lib/js";
@ -28,7 +28,6 @@ import { Username } from "../../../components/common/user/UserShort";
import UserStatus from "../../../components/common/user/UserStatus"; import UserStatus from "../../../components/common/user/UserStatus";
import Markdown from "../../../components/markdown/Markdown"; import Markdown from "../../../components/markdown/Markdown";
import Modal from "../../../components/ui/Modal"; import Modal from "../../../components/ui/Modal";
import Overline from "../../../components/ui/Overline";
import { import {
ClientStatus, ClientStatus,
StatusContext, StatusContext,
@ -278,19 +277,19 @@ export const UserProfile = observer(
<div> <div>
{flags & 1 ? ( {flags & 1 ? (
/** ! FIXME: i18n this area */ /** ! FIXME: i18n this area */
<Overline type="error" block> <Category>
User is suspended <Error error="User is suspended" />
</Overline> </Category>
) : undefined} ) : undefined}
{flags & 2 ? ( {flags & 2 ? (
<Overline type="error" block> <Category>
User deleted their account <Error error="User deleted their account" />
</Overline> </Category>
) : undefined} ) : undefined}
{flags & 4 ? ( {flags & 4 ? (
<Overline type="error" block> <Category>
User is banned <Error error="User is banned" />
</Overline> </Category>
) : undefined} ) : undefined}
{user.bot ? ( {user.bot ? (
<> <>

View file

@ -4,6 +4,8 @@ import { useHistory, useParams } from "react-router-dom";
import { Text } from "preact-i18n"; import { Text } from "preact-i18n";
import { useContext, useEffect } from "preact/hooks"; import { useContext, useEffect } from "preact/hooks";
import { Header } from "@revoltchat/ui";
import { useIntermediate } from "../context/intermediate/Intermediate"; import { useIntermediate } from "../context/intermediate/Intermediate";
import { import {
AppContext, AppContext,
@ -11,8 +13,6 @@ import {
StatusContext, StatusContext,
} from "../context/revoltjs/RevoltClient"; } from "../context/revoltjs/RevoltClient";
import Header from "../components/ui/Header";
export default function Open() { export default function Open() {
const history = useHistory(); const history = useHistory();
const client = useContext(AppContext); const client = useContext(AppContext);
@ -22,7 +22,7 @@ export default function Open() {
if (status !== ClientStatus.ONLINE) { if (status !== ClientStatus.ONLINE) {
return ( return (
<Header placement="primary"> <Header palette="primary">
<Text id="general.loading" /> <Text id="general.loading" />
</Header> </Header>
); );
@ -72,7 +72,7 @@ export default function Open() {
}); });
return ( return (
<Header placement="primary"> <Header palette="primary">
<Text id="general.loading" /> <Text id="general.loading" />
</Header> </Header>
); );

View file

@ -1,31 +1,19 @@
import { import { At, Hash } from "@styled-icons/boxicons-regular";
At,
ChevronLeft,
ChevronRight,
Hash,
} from "@styled-icons/boxicons-regular";
import { Notepad, Group } from "@styled-icons/boxicons-solid"; import { Notepad, Group } from "@styled-icons/boxicons-solid";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { Channel } from "revolt.js"; import { Channel } from "revolt.js";
import { User } from "revolt.js"; import { User } from "revolt.js";
import styled, { css } from "styled-components/macro"; import styled from "styled-components/macro";
import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice"; import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
import { useApplicationState } from "../../mobx/State";
import { SIDEBAR_CHANNELS, SIDEBAR_MEMBERS } from "../../mobx/stores/Layout";
import { useIntermediate } from "../../context/intermediate/Intermediate"; import { useIntermediate } from "../../context/intermediate/Intermediate";
import { getChannelName } from "../../context/revoltjs/util"; import { getChannelName } from "../../context/revoltjs/util";
import { useStatusColour } from "../../components/common/user/UserIcon"; import { useStatusColour } from "../../components/common/user/UserIcon";
import UserStatus from "../../components/common/user/UserStatus"; import UserStatus from "../../components/common/user/UserStatus";
import Header, {
HamburgerAction,
PageHeader,
} from "../../components/ui/Header";
import Markdown from "../../components/markdown/Markdown"; import Markdown from "../../components/markdown/Markdown";
import { PageHeader } from "../../components/ui/Header";
import HeaderActions from "./actions/HeaderActions"; import HeaderActions from "./actions/HeaderActions";
export interface ChannelHeaderProps { export interface ChannelHeaderProps {
@ -98,7 +86,7 @@ export default observer(({ channel }: ChannelHeaderProps) => {
} }
return ( return (
<PageHeader icon={icon} transparent> <PageHeader icon={icon} withTransparency>
<Info> <Info>
<span className="name">{name}</span> <span className="name">{name}</span>
{isTouchscreenDevice && {isTouchscreenDevice &&

View file

@ -7,7 +7,7 @@ import { TextReact } from "../../lib/i18n";
import { AppContext } from "../../context/revoltjs/RevoltClient"; import { AppContext } from "../../context/revoltjs/RevoltClient";
import Header, { PageHeader } from "../../components/ui/Header"; import { PageHeader } from "../../components/ui/Header";
export default function Developer() { export default function Developer() {
// const voice = useContext(VoiceContext); // const voice = useContext(VoiceContext);

View file

@ -5,7 +5,7 @@ import styled, { css } from "styled-components/macro";
import { useEffect, useMemo, useRef, useState } from "preact/hooks"; import { useEffect, useMemo, useRef, useState } from "preact/hooks";
import { Preloader } from "@revoltchat/ui"; import { Header, Preloader } from "@revoltchat/ui";
import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice"; import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
@ -14,8 +14,6 @@ import { useApplicationState } from "../../mobx/State";
import { Overrides } from "../../context/Theme"; import { Overrides } from "../../context/Theme";
import { useIntermediate } from "../../context/intermediate/Intermediate"; import { useIntermediate } from "../../context/intermediate/Intermediate";
import Header from "../../components/ui/Header";
const Container = styled.div` const Container = styled.div`
flex-grow: 1; flex-grow: 1;
display: flex; display: flex;
@ -165,7 +163,7 @@ export default function Discover() {
return ( return (
<Container> <Container>
{isTouchscreenDevice && ( {isTouchscreenDevice && (
<Header placement="primary" transparent> <Header palette="primary" withTransparency>
<Compass size={27} /> <Compass size={27} />
Discover Discover
</Header> </Header>

View file

@ -68,7 +68,10 @@ export default observer(() => {
const isEmpty = lists.reduce((p: number, n) => p + n.length, 0) === 0; const isEmpty = lists.reduce((p: number, n) => p + n.length, 0) === 0;
return ( return (
<> <>
<PageHeader icon={<UserDetail size={24} />} transparent noBurger> <PageHeader
icon={<UserDetail size={24} />}
withTransparency
noBurger>
<div className={styles.title}> <div className={styles.title}>
<Text id="app.navigation.tabs.friends" /> <Text id="app.navigation.tabs.friends" />
</div> </div>

View file

@ -86,7 +86,7 @@ export default observer(() => {
</div> </div>
)} )}
<div className="content"> <div className="content">
<PageHeader icon={<HomeIcon size={24} />} transparent> <PageHeader icon={<HomeIcon size={24} />} withTransparency>
<Text id="app.navigation.tabs.home" /> <Text id="app.navigation.tabs.home" />
</PageHeader> </PageHeader>
<div className={styles.homeScreen}> <div className={styles.homeScreen}>

View file

@ -7,7 +7,7 @@ import styles from "./Invite.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, Category, Error, Preloader } from "@revoltchat/ui";
import { TextReact } from "../../lib/i18n"; import { TextReact } from "../../lib/i18n";
@ -23,7 +23,6 @@ import { takeError } from "../../context/revoltjs/util";
import ServerIcon from "../../components/common/ServerIcon"; import ServerIcon from "../../components/common/ServerIcon";
import UserIcon from "../../components/common/user/UserIcon"; import UserIcon from "../../components/common/user/UserIcon";
import Overline from "../../components/ui/Overline";
export default function Invite() { export default function Invite() {
const history = useHistory(); const history = useHistory();
@ -149,7 +148,9 @@ export default function Invite() {
}} }}
/> />
</h3> </h3>
<Overline type="error" error={error} /> <Category>
<Error error={error} />
</Category>
<Button <Button
palette="secondary" palette="secondary"
onClick={async () => { onClick={async () => {

View file

@ -4,13 +4,12 @@ import styled from "styled-components/macro";
import { useEffect, useState } from "preact/hooks"; import { useEffect, useState } from "preact/hooks";
import { Button, ComboBox, Preloader, Tip } from "@revoltchat/ui"; import { Button, Category, 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";
const BotInfo = styled.div` const BotInfo = styled.div`
gap: 12px; gap: 12px;
@ -58,7 +57,7 @@ export default function InviteBot() {
)} )}
</div> </div>
</BotInfo> </BotInfo>
<Overline type="subtle">Add to server</Overline> <Category>Add to server</Category>
<Option> <Option>
<ComboBox <ComboBox
value={server} value={server}
@ -81,7 +80,7 @@ export default function InviteBot() {
Add Add
</Button> </Button>
</Option> </Option>
<Overline type="subtle">Add to group</Overline> <Category>Add to group</Category>
<Option> <Option>
<ComboBox <ComboBox
value={group} value={group}

View file

@ -2,9 +2,9 @@ import { UseFormMethods } from "react-hook-form";
import { Text, Localizer } from "preact-i18n"; import { Text, Localizer } from "preact-i18n";
import { InputBox } from "@revoltchat/ui"; import { Category, InputBox } from "@revoltchat/ui";
import Overline from "../../components/ui/Overline"; import { I18nError } from "../../context/Locale";
type FieldType = type FieldType =
| "email" | "email"
@ -32,9 +32,11 @@ export default function FormField({
return ( return (
<> <>
{showOverline && ( {showOverline && (
<Overline error={error}> <Category compact>
<I18nError error={error}>
<Text id={`login.${type}`} /> <Text id={`login.${type}`} />
</Overline> </I18nError>
</Category>
)} )}
<Localizer> <Localizer>
<InputBox <InputBox

View file

@ -6,16 +6,16 @@ import styles from "../Login.module.scss";
import { Text } from "preact-i18n"; import { Text } from "preact-i18n";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
import { Button, Preloader } from "@revoltchat/ui"; import { Button, Category, Preloader } from "@revoltchat/ui";
import { Tip } from "@revoltchat/ui"; import { Tip } from "@revoltchat/ui";
import { useApplicationState } from "../../../mobx/State"; import { useApplicationState } from "../../../mobx/State";
import { I18nError } from "../../../context/Locale";
import { takeError } from "../../../context/revoltjs/util"; import { takeError } from "../../../context/revoltjs/util";
import WaveSVG from "../../settings/assets/wave.svg"; import WaveSVG from "../../settings/assets/wave.svg";
import Overline from "../../../components/ui/Overline";
import FormField from "../FormField"; import FormField from "../FormField";
import { CaptchaBlock, CaptchaProps } from "./CaptchaBlock"; import { CaptchaBlock, CaptchaProps } from "./CaptchaBlock";
import { MailProvider } from "./MailProvider"; import { MailProvider } from "./MailProvider";
@ -199,9 +199,11 @@ export const Form = observer(({ page, callback }: Props) => {
/> />
)} )}
{error && ( {error && (
<Overline type="error" error={error}> <Category>
<I18nError error={error}>
<Text id={`login.error.${page}`} /> <Text id={`login.error.${page}`} />
</Overline> </I18nError>
</Category>
)} )}
<Button> <Button>
<Text <Text

View file

@ -1,14 +1,14 @@
import { useHistory, useParams } from "react-router-dom"; import { useHistory, useParams } from "react-router-dom";
import { useContext, useEffect, useState } from "preact/hooks"; import { useEffect, useState } from "preact/hooks";
import { Preloader } from "@revoltchat/ui"; import { Category, Preloader } from "@revoltchat/ui";
import { useApplicationState } from "../../../mobx/State"; import { useApplicationState } from "../../../mobx/State";
import { I18nError } from "../../../context/Locale";
import { takeError } from "../../../context/revoltjs/util"; import { takeError } from "../../../context/revoltjs/util";
import Overline from "../../../components/ui/Overline";
import { Form } from "./Form"; import { Form } from "./Form";
export function FormResend() { export function FormResend() {
@ -41,7 +41,9 @@ export function FormVerify() {
}, []); }, []);
return error ? ( return error ? (
<Overline type="error" error={error} /> <Category>
<I18nError error={error} />
</Category>
) : ( ) : (
<Preloader type="ring" /> <Preloader type="ring" />
); );

View file

@ -7,8 +7,6 @@ import { Text } from "preact-i18n";
import { useClient } from "../../context/revoltjs/RevoltClient"; import { useClient } from "../../context/revoltjs/RevoltClient";
import { getChannelName } from "../../context/revoltjs/util"; import { getChannelName } from "../../context/revoltjs/util";
import Category from "../../components/ui/Category";
import { GenericSettings } from "./GenericSettings"; import { GenericSettings } from "./GenericSettings";
import Overview from "./channel/Overview"; import Overview from "./channel/Overview";
import Permissions from "./channel/Permissions"; import Permissions from "./channel/Permissions";

View file

@ -5,23 +5,15 @@ import { useHistory, useParams } from "react-router-dom";
import styles from "./Settings.module.scss"; import styles from "./Settings.module.scss";
import classNames from "classnames"; import classNames from "classnames";
import { Text } from "preact-i18n"; import { Text } from "preact-i18n";
import { import { useCallback, useEffect, useRef, useState } from "preact/hooks";
useCallback,
useContext,
useEffect,
useRef,
useState,
} from "preact/hooks";
import { IconButton, LineDivider } from "@revoltchat/ui"; import { Category, Header, IconButton, LineDivider } from "@revoltchat/ui";
import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice"; import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
import { useApplicationState } from "../../mobx/State"; import { useApplicationState } from "../../mobx/State";
import ButtonItem from "../../components/navigation/items/ButtonItem"; import ButtonItem from "../../components/navigation/items/ButtonItem";
import Category from "../../components/ui/Category";
import Header from "../../components/ui/Header";
import { Children } from "../../types/Preact"; import { Children } from "../../types/Preact";
interface Props { interface Props {
@ -98,7 +90,7 @@ export function GenericSettings({
/> />
</Helmet> </Helmet>
{isTouchscreenDevice && ( {isTouchscreenDevice && (
<Header placement="primary" transparent> <Header palette="primary" withTransparency>
{typeof page === "undefined" ? ( {typeof page === "undefined" ? (
<> <>
{showExitButton && ( {showExitButton && (
@ -139,10 +131,9 @@ export function GenericSettings({
entry.hidden ? undefined : ( entry.hidden ? undefined : (
<> <>
{entry.category && ( {entry.category && (
<Category <Category>
variant="uniform" {entry.category}
text={entry.category} </Category>
/>
)} )}
<ButtonItem <ButtonItem
active={ active={
@ -155,7 +146,9 @@ export function GenericSettings({
compact> compact>
{entry.icon} {entry.title} {entry.icon} {entry.title}
</ButtonItem> </ButtonItem>
{entry.divider && <LineDivider />} {entry.divider && (
<LineDivider compact />
)}
</> </>
), ),
)} )}

View file

@ -220,7 +220,6 @@ export const Account = observer(() => {
description={ description={
"Delete your account, including all of your data. (sends an email to contact@revolt.chat)" "Delete your account, including all of your data. (sends an email to contact@revolt.chat)"
} }
hover
action="external"> action="external">
<Text id="app.settings.pages.account.manage.delete" /> <Text id="app.settings.pages.account.manage.delete" />
</CategoryButton> </CategoryButton>

View file

@ -2,14 +2,14 @@ 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, Tip } from "@revoltchat/ui"; import { Button, Category, 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";
import opusSVG from "../assets/opus_logo.svg"; import { I18nError } from "../../../context/Locale";
import Overline from "../../../components/ui/Overline"; import opusSVG from "../assets/opus_logo.svg";
{ {
/*import OpusSVG from "../assets/opus_logo.svg";*/ /*import OpusSVG from "../assets/opus_logo.svg";*/
@ -169,11 +169,9 @@ export function Audio() {
</Button> </Button>
)} )}
{error && error.name === "NotAllowedError" && ( {error && error.name === "NotAllowedError" && (
<Overline <Category>
error="AudioPermissionBlock" <I18nError error="AudioPermissionBlock" />
type="error" </Category>
block
/>
)} )}
</div> </div>
</div> </div>

View file

@ -11,6 +11,7 @@ import { useEffect, useMemo, useState } from "preact/hooks";
import { import {
Button, Button,
Category,
Checkbox, Checkbox,
IconButton, IconButton,
InputBox, InputBox,
@ -19,7 +20,6 @@ import {
import UserIcon from "../../../components/common/user/UserIcon"; import UserIcon from "../../../components/common/user/UserIcon";
import { Username } from "../../../components/common/user/UserShort"; import { Username } from "../../../components/common/user/UserShort";
import Overline from "../../../components/ui/Overline";
interface InnerProps { interface InnerProps {
member: Member; member: Member;
@ -51,7 +51,7 @@ const Inner = observer(({ member }: InnerProps) => {
</div> </div>
{open && ( {open && (
<div className={styles.memberView}> <div className={styles.memberView}>
<Overline type="subtle">Roles</Overline> <Category>Roles</Category>
{Object.keys(server_roles).map((key) => { {Object.keys(server_roles).map((key) => {
const role = server_roles[key]; const role = server_roles[key];
return ( return (

View file

@ -13,13 +13,13 @@ import {
Checkbox, Checkbox,
ColourSwatches, ColourSwatches,
InputBox, InputBox,
Category,
} from "@revoltchat/ui"; } from "@revoltchat/ui";
import { useIntermediate } from "../../../context/intermediate/Intermediate"; import { useIntermediate } from "../../../context/intermediate/Intermediate";
import { PermissionList } from "../../../components/settings/roles/PermissionList"; import { PermissionList } from "../../../components/settings/roles/PermissionList";
import { RoleOrDefault } from "../../../components/settings/roles/RoleSelection"; import { RoleOrDefault } from "../../../components/settings/roles/RoleSelection";
import Overline from "../../../components/ui/Overline";
interface Props { interface Props {
server: Server; server: Server;
@ -136,9 +136,9 @@ export const Roles = observer(({ server }: Props) => {
{selected !== "default" && ( {selected !== "default" && (
<> <>
<section> <section>
<Overline type="subtle"> <Category>
<Text id="app.settings.permissions.role_name" /> <Text id="app.settings.permissions.role_name" />
</Overline> </Category>
<p> <p>
<InputBox <InputBox
value={currentRoleValue.name} value={currentRoleValue.name}
@ -153,9 +153,9 @@ export const Roles = observer(({ server }: Props) => {
</p> </p>
</section> </section>
<section> <section>
<Overline type="subtle"> <Category>
<Text id="app.settings.permissions.role_colour" /> <Text id="app.settings.permissions.role_colour" />
</Overline> </Category>
<p> <p>
<ColourSwatches <ColourSwatches
value={ value={
@ -169,9 +169,9 @@ export const Roles = observer(({ server }: Props) => {
</p> </p>
</section> </section>
<section> <section>
<Overline type="subtle"> <Category>
<Text id="app.settings.permissions.role_options" /> <Text id="app.settings.permissions.role_options" />
</Overline> </Category>
<p> <p>
<Checkbox <Checkbox
value={ value={
@ -190,9 +190,9 @@ export const Roles = observer(({ server }: Props) => {
</p> </p>
</section> </section>
<section> <section>
<Overline type="subtle"> <Category>
<Text id="app.settings.permissions.role_ranking" /> <Text id="app.settings.permissions.role_ranking" />
</Overline> </Category>
<p> <p>
<InputBox <InputBox
type="number" type="number"