diff --git a/package.json b/package.json index 8b587394..63865c45 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,7 @@ "@hcaptcha/react-hcaptcha": "^0.3.6", "@insertish/vite-plugin-babel-macros": "^1.0.5", "@preact/preset-vite": "^2.0.0", - "@revoltchat/ui": "1.0.34", + "@revoltchat/ui": "1.0.36", "@rollup/plugin-replace": "^2.4.2", "@styled-icons/boxicons-logos": "^10.38.0", "@styled-icons/boxicons-regular": "^10.38.0", diff --git a/src/components/common/AgeGate.tsx b/src/components/common/AgeGate.tsx index 825721b8..fc0fbdfa 100644 --- a/src/components/common/AgeGate.tsx +++ b/src/components/common/AgeGate.tsx @@ -6,13 +6,11 @@ import styled from "styled-components/macro"; import { Text } from "preact-i18n"; import { useState } from "preact/hooks"; -import { Button } from "@revoltchat/ui"; +import { Button, Checkbox } from "@revoltchat/ui"; import { useApplicationState } from "../../mobx/State"; import { SECTION_NSFW } from "../../mobx/stores/Layout"; -import Checkbox from "../ui/Checkbox"; - import { Children } from "../../types/Preact"; const Base = styled.div` @@ -81,10 +79,10 @@ export default observer((props: Props) => { layout.toggleSectionState(SECTION_NSFW, false)}> - - + title={} + value={layout.getSectionState(SECTION_NSFW, false)} + onChange={() => layout.toggleSectionState(SECTION_NSFW, false)} + />
)} - {joinError && } + {joinError && ( + + + + )} ); } diff --git a/src/components/common/messaging/embed/EmbedMediaActions.tsx b/src/components/common/messaging/embed/EmbedMediaActions.tsx index 27f82c75..6f06b1dd 100644 --- a/src/components/common/messaging/embed/EmbedMediaActions.tsx +++ b/src/components/common/messaging/embed/EmbedMediaActions.tsx @@ -3,7 +3,7 @@ import { API } from "revolt.js"; import styles from "./Embed.module.scss"; -import IconButton from "../../../ui/IconButton"; +import { IconButton } from "@revoltchat/ui"; interface Props { embed: API.Image; diff --git a/src/components/common/user/UserCheckbox.tsx b/src/components/common/user/UserCheckbox.tsx index f810bc43..b80ca1c1 100644 --- a/src/components/common/user/UserCheckbox.tsx +++ b/src/components/common/user/UserCheckbox.tsx @@ -1,11 +1,11 @@ import { User } from "revolt.js"; -import Checkbox, { CheckboxProps } from "../../ui/Checkbox"; +import { Checkbox } from "@revoltchat/ui"; import UserIcon from "./UserIcon"; import { Username } from "./UserShort"; -type UserProps = Omit & { user: User }; +type UserProps = { value: boolean; onChange: (v: boolean) => void; user: User }; export default function UserCheckbox({ user, ...props }: UserProps) { return ( diff --git a/src/components/common/user/UserHeader.tsx b/src/components/common/user/UserHeader.tsx index e3d207c6..0ab85bfd 100644 --- a/src/components/common/user/UserHeader.tsx +++ b/src/components/common/user/UserHeader.tsx @@ -7,13 +7,12 @@ import styled from "styled-components/macro"; import { openContextMenu } from "preact-context-menu"; import { Text, Localizer } from "preact-i18n"; +import { Header, IconButton } from "@revoltchat/ui"; + import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice"; import { useIntermediate } from "../../../context/intermediate/Intermediate"; -import Header from "../../ui/Header"; -import IconButton from "../../ui/IconButton"; - import Tooltip from "../Tooltip"; import UserStatus from "./UserStatus"; @@ -52,7 +51,7 @@ export default observer(({ user }: Props) => { const { writeClipboard } = useIntermediate(); return ( -
+
}> diff --git a/src/components/navigation/BottomNavigation.tsx b/src/components/navigation/BottomNavigation.tsx index e0b5faf1..c7e527ba 100644 --- a/src/components/navigation/BottomNavigation.tsx +++ b/src/components/navigation/BottomNavigation.tsx @@ -3,6 +3,8 @@ import { observer } from "mobx-react-lite"; import { useHistory, useLocation } from "react-router"; import styled, { css } from "styled-components/macro"; +import { IconButton } from "@revoltchat/ui"; + import ConditionalLink from "../../lib/ConditionalLink"; import { useApplicationState } from "../../mobx/State"; @@ -10,7 +12,6 @@ import { useApplicationState } from "../../mobx/State"; import { useClient } from "../../context/revoltjs/RevoltClient"; import UserIcon from "../common/user/UserIcon"; -import IconButton from "../ui/IconButton"; const Base = styled.div` background: var(--secondary-background); diff --git a/src/components/navigation/items/ButtonItem.tsx b/src/components/navigation/items/ButtonItem.tsx index 7d6a8467..a71f3183 100644 --- a/src/components/navigation/items/ButtonItem.tsx +++ b/src/components/navigation/items/ButtonItem.tsx @@ -5,23 +5,22 @@ import { User, Channel } from "revolt.js"; import styles from "./Item.module.scss"; import classNames from "classnames"; -import { Ref } from "preact"; import { useTriggerEvents } from "preact-context-menu"; import { Localizer, Text } from "preact-i18n"; +import { IconButton } from "@revoltchat/ui"; + import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice"; import { stopPropagation } from "../../../lib/stopPropagation"; import { useIntermediate } from "../../../context/intermediate/Intermediate"; +import { Children } from "../../../types/Preact"; import ChannelIcon from "../../common/ChannelIcon"; import Tooltip from "../../common/Tooltip"; import UserIcon from "../../common/user/UserIcon"; import { Username } from "../../common/user/UserShort"; import UserStatus from "../../common/user/UserStatus"; -import IconButton from "../../ui/IconButton"; - -import { Children } from "../../../types/Preact"; type CommonProps = Omit< JSX.HTMLAttributes, diff --git a/src/components/navigation/items/ConnectionStatus.tsx b/src/components/navigation/items/ConnectionStatus.tsx index de0d42b7..899b3d8e 100644 --- a/src/components/navigation/items/ConnectionStatus.tsx +++ b/src/components/navigation/items/ConnectionStatus.tsx @@ -1,14 +1,14 @@ import { Text } from "preact-i18n"; import { useContext } from "preact/hooks"; +import { Banner } from "@revoltchat/ui"; + import { ClientStatus, StatusContext, useClient, } from "../../../context/revoltjs/RevoltClient"; -import Banner from "../../ui/Banner"; - export default function ConnectionStatus() { const status = useContext(StatusContext); const client = useClient(); diff --git a/src/components/navigation/left/HomeSidebar.tsx b/src/components/navigation/left/HomeSidebar.tsx index bb471340..220f542c 100644 --- a/src/components/navigation/left/HomeSidebar.tsx +++ b/src/components/navigation/left/HomeSidebar.tsx @@ -1,3 +1,4 @@ +import { Plus } from "@styled-icons/boxicons-regular"; import { Home, UserDetail, @@ -11,6 +12,8 @@ import styled, { css } from "styled-components/macro"; import { Text } from "preact-i18n"; import { useContext, useEffect } from "preact/hooks"; +import { Category, IconButton } from "@revoltchat/ui"; + import ConditionalLink from "../../../lib/ConditionalLink"; import PaintCounter from "../../../lib/PaintCounter"; import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice"; @@ -20,7 +23,6 @@ import { useApplicationState } from "../../../mobx/State"; import { useIntermediate } from "../../../context/intermediate/Intermediate"; import { AppContext } from "../../../context/revoltjs/RevoltClient"; -import Category from "../../ui/Category"; import placeholderSVG from "../items/placeholder.svg"; import { GenericSidebarBase, GenericSidebarList } from "../SidebarBase"; @@ -125,15 +127,18 @@ export default observer(() => { )} - } - action={() => - openScreen({ - id: "special_input", - type: "create_group", - }) - } - /> + + + + openScreen({ + id: "special_input", + type: "create_group", + }) + }> + + + {channels.length === 0 && ( )} diff --git a/src/components/navigation/left/ServerSidebar.tsx b/src/components/navigation/left/ServerSidebar.tsx index 0e228fa4..4a771701 100644 --- a/src/components/navigation/left/ServerSidebar.tsx +++ b/src/components/navigation/left/ServerSidebar.tsx @@ -1,12 +1,12 @@ import { observer } from "mobx-react-lite"; import { Redirect, useParams } from "react-router"; -import { Server } from "revolt.js"; import styled, { css } from "styled-components/macro"; -import { Ref } from "preact"; import { useTriggerEvents } from "preact-context-menu"; import { useEffect } from "preact/hooks"; +import { Category } from "@revoltchat/ui"; + import ConditionalLink from "../../../lib/ConditionalLink"; import PaintCounter from "../../../lib/PaintCounter"; import { internalEmit } from "../../../lib/eventEmitter"; @@ -18,8 +18,6 @@ import { useClient } from "../../../context/revoltjs/RevoltClient"; import CollapsibleSection from "../../common/CollapsibleSection"; import ServerHeader from "../../common/ServerHeader"; -import Category from "../../ui/Category"; - import { ChannelButton } from "../items/ButtonItem"; import ConnectionStatus from "../items/ConnectionStatus"; @@ -126,7 +124,7 @@ export default observer(() => { }> + summary={{category.title}}> {channels} , ); diff --git a/src/components/navigation/right/Search.tsx b/src/components/navigation/right/Search.tsx index f6924e60..8694ab11 100644 --- a/src/components/navigation/right/Search.tsx +++ b/src/components/navigation/right/Search.tsx @@ -5,15 +5,11 @@ import styled from "styled-components/macro"; import { Text } from "preact-i18n"; import { useEffect, useState } from "preact/hooks"; -import { Button } from "@revoltchat/ui"; +import { Button, Category, Error, InputBox, Preloader } from "@revoltchat/ui"; import { useClient } from "../../../context/revoltjs/RevoltClient"; import Message from "../../common/messaging/Message"; -import InputBox from "../../ui/InputBox"; -import Overline from "../../ui/Overline"; -import Preloader from "../../ui/Preloader"; - import { GenericSidebarBase, GenericSidebarList } from "../SidebarBase"; type SearchState = @@ -103,12 +99,14 @@ export function SearchSidebar({ close }: Props) { - - « back to members - - + + « back to members} + /> + + - + e.key === "Enter" && search()} diff --git a/src/components/settings/AppearanceShims.tsx b/src/components/settings/AppearanceShims.tsx index 3b3f4f09..4e7ea7e8 100644 --- a/src/components/settings/AppearanceShims.tsx +++ b/src/components/settings/AppearanceShims.tsx @@ -6,6 +6,14 @@ import pSBC from "shade-blend-color"; import { Text } from "preact-i18n"; +import { + CategoryButton, + Checkbox, + ColourSwatches, + ComboBox, + Radio, +} from "@revoltchat/ui"; + import TextAreaAutoSize from "../../lib/TextAreaAutoSize"; import { useApplicationState } from "../../mobx/State"; @@ -19,12 +27,6 @@ import { MONOSPACE_FONT_KEYS, } from "../../context/Theme"; -import Checkbox from "../ui/Checkbox"; -import ColourSwatches from "../ui/ColourSwatches"; -import ComboBox from "../ui/ComboBox"; -import Radio from "../ui/Radio"; -import CategoryButton from "../ui/fluent/CategoryButton"; - import { EmojiSelector } from "./appearance/EmojiSelector"; import { ThemeBaseSelector } from "./appearance/ThemeBaseSelector"; @@ -57,8 +59,7 @@ export const ThemeShopShim = () => { action="chevron" description={ - } - hover> + }> @@ -119,19 +120,24 @@ export const DisplayCompactShim = () => {
+ } description={ } - checked> - - + value={true} + /> + } description={ } - disabled> - - + value={false} + disabled + />
); @@ -200,13 +206,13 @@ export const DisplayLigaturesShim = observer(() => { return ( <> settings.set("appearance:ligatures", v)} + title={} description={ - }> - - + } + /> ); }); @@ -219,13 +225,15 @@ export const ShowSendButtonShim = observer(() => { return ( settings.set("appearance:show_send_button", v)} + title={ + + } description={ - }> - - + } + /> ); }); @@ -237,13 +245,15 @@ export const DisplaySeasonalShim = observer(() => { return ( settings.set("appearance:seasonal", v)} + title={ + + } description={ - }> - - + } + /> ); }); @@ -255,13 +265,15 @@ export const DisplayTransparencyShim = observer(() => { return ( settings.set("appearance:transparency", v)} + title={ + + } description={ - }> - - + } + /> ); }); diff --git a/src/components/settings/appearance/ThemeOverrides.tsx b/src/components/settings/appearance/ThemeOverrides.tsx index 572a19e7..bb5c2cc9 100644 --- a/src/components/settings/appearance/ThemeOverrides.tsx +++ b/src/components/settings/appearance/ThemeOverrides.tsx @@ -2,14 +2,14 @@ import { Pencil } from "@styled-icons/boxicons-regular"; import { observer } from "mobx-react-lite"; import styled from "styled-components/macro"; +import { InputBox } from "@revoltchat/ui"; + import { useDebounceCallback } from "../../../lib/debounce"; import { useApplicationState } from "../../../mobx/State"; import { Variables } from "../../../context/Theme"; -import InputBox from "../../ui/InputBox"; - const Container = styled.div` row-gap: 8px; display: grid; diff --git a/src/components/settings/roles/PermissionSelect.tsx b/src/components/settings/roles/PermissionSelect.tsx index 1ab84c59..5f921132 100644 --- a/src/components/settings/roles/PermissionSelect.tsx +++ b/src/components/settings/roles/PermissionSelect.tsx @@ -7,8 +7,7 @@ import styled, { css } from "styled-components"; import { Text } from "preact-i18n"; import { useMemo } from "preact/hooks"; -import Checkbox from "../../ui/Checkbox"; -import { OverrideSwitch } from "@revoltchat/ui"; +import { Checkbox, OverrideSwitch } from "@revoltchat/ui"; interface PermissionSelectProps { id: keyof typeof Permission; @@ -136,7 +135,7 @@ export function PermissionSelect({ ) : ( onChange( Long.fromNumber(value, false) diff --git a/src/components/ui/Banner.tsx b/src/components/ui/Banner.tsx deleted file mode 100644 index ca4704d9..00000000 --- a/src/components/ui/Banner.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import styled from "styled-components/macro"; - -export default styled.div` - padding: 8px; - font-size: 14px; - text-align: center; - - color: var(--accent); - background: var(--primary-background); -`; diff --git a/src/components/ui/Category.tsx b/src/components/ui/Category.tsx deleted file mode 100644 index baaa50df..00000000 --- a/src/components/ui/Category.tsx +++ /dev/null @@ -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>` - 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, - "children" | "as" | "action" -> & { - text: Children; - action?: () => void; - variant?: "default" | "uniform"; -}; - -export default function Category(props: Props) { - const { text, action, ...otherProps } = props; - - return ( - - {text} - {action && } - - ); -} diff --git a/src/components/ui/Checkbox.tsx b/src/components/ui/Checkbox.tsx deleted file mode 100644 index 3c28fc89..00000000 --- a/src/components/ui/Checkbox.tsx +++ /dev/null @@ -1,123 +0,0 @@ -import { Check } from "@styled-icons/boxicons-regular"; -import styled, { css } from "styled-components/macro"; - -import { Children } from "../../types/Preact"; - -export const CheckboxBase = styled.label` - gap: 4px; - z-index: 1; - display: flex; - margin-top: 20px; - align-items: center; - border-radius: var(--border-radius); - - cursor: pointer; - font-size: 18px; - user-select: none; - - transition: 0.2s ease all; - - input { - display: none; - } - - &:hover { - .check { - background: var(--background); - } - } - - &[disabled] { - opacity: 0.5; - cursor: not-allowed; - - &:hover { - background: unset; - } - } -`; - -const CheckboxContent = styled.span` - display: flex; - flex-grow: 1; - font-size: 14px; - gap: 2px; - font-weight: 600; - flex-direction: column; -`; - -const CheckboxDescription = styled.span` - font-size: 0.75rem; - font-weight: 500; - color: var(--secondary-foreground); -`; - -export const Checkmark = styled.div<{ checked: boolean; contrast?: boolean }>` - margin: 4px; - width: 24px; - height: 24px; - display: grid; - flex-shrink: 0; - place-items: center; - transition: 0.2s ease all; - border-radius: var(--border-radius); - background: var(--secondary-background); - - svg { - color: var(--secondary-background); - } - - ${(props) => - props.contrast && - css` - background: var(--primary-background); - - svg { - color: var(--primary-background); - } - `} - - ${(props) => - props.checked && - css` - background: var(--accent) !important; - `} -`; - -export interface CheckboxProps { - checked: boolean; - disabled?: boolean; - contrast?: boolean; - className?: string; - children?: Children; - description?: Children; - onChange: (state: boolean) => void; -} - -export default function Checkbox(props: CheckboxProps) { - return ( - - - {props.children} - {props.description && ( - - {props.description} - - )} - - - !props.disabled && props.onChange(!props.checked) - } - /> - - - - - ); -} diff --git a/src/components/ui/ColourSwatches.tsx b/src/components/ui/ColourSwatches.tsx deleted file mode 100644 index a73e913f..00000000 --- a/src/components/ui/ColourSwatches.tsx +++ /dev/null @@ -1,162 +0,0 @@ -import { Check } from "@styled-icons/boxicons-regular"; -import { Palette } from "@styled-icons/boxicons-solid"; -import styled, { css } from "styled-components/macro"; - -import { RefObject } from "preact"; -import { useRef } from "preact/hooks"; - -import { useDebounceCallback } from "../../lib/debounce"; - -interface Props { - value: string; - onChange: (value: string) => void; -} - -const presets = [ - [ - "#7B68EE", - "#3498DB", - "#1ABC9C", - "#F1C40F", - "#FF7F50", - "#FD6671", - "#E91E63", - "#D468EE", - ], - [ - "#594CAD", - "#206694", - "#11806A", - "#C27C0E", - "#CD5B45", - "#FF424F", - "#AD1457", - "#954AA8", - ], -]; - -const SwatchesBase = styled.div` - /*gap: 8px;*/ - display: flex; - - input { - width: 0; - height: 0; - top: 72px; - opacity: 0; - padding: 0; - border: 0; - position: relative; - pointer-events: none; - } - - .overlay { - position: relative; - width: 0; - - div { - width: 8px; - height: 68px; - background: linear-gradient( - to right, - var(--primary-background), - transparent - ); - } - } -`; - -const Swatch = styled.div<{ type: "small" | "large"; colour: string }>` - flex-shrink: 0; - cursor: pointer; - border-radius: var(--border-radius); - background-color: ${(props) => props.colour}; - - display: grid; - place-items: center; - - &:hover { - border: 3px solid var(--foreground); - transition: border ease-in-out 0.07s; - } - - svg { - color: white; - } - - ${(props) => - props.type === "small" - ? css` - width: 30px; - height: 30px; - - svg { - /*stroke-width: 2;*/ - } - ` - : css` - width: 68px; - height: 68px; - `} -`; - -const Rows = styled.div` - gap: 8px; - display: flex; - flex-direction: column; - overflow: auto; - padding-bottom: 4px; - - > div { - gap: 8px; - display: flex; - flex-direction: row; - padding-inline-start: 8px; - } -`; - -export default function ColourSwatches({ value, onChange }: Props) { - const ref = useRef() as RefObject; - const setValue = useDebounceCallback( - (value) => onChange(value as string), - [onChange], - 100, - ); - - return ( - - setValue(ev.currentTarget.value)} - /> - ref.current?.click()}> - - - -
-
-
- - - {presets.map((row, i) => ( -
- {row.map((swatch, i) => ( - onChange(swatch)}> - {swatch === value && } - - ))} -
- ))} -
- - ); -} diff --git a/src/components/ui/ComboBox.tsx b/src/components/ui/ComboBox.tsx deleted file mode 100644 index 3267a856..00000000 --- a/src/components/ui/ComboBox.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import styled from "styled-components/macro"; - -export default styled.select` - width: 100%; - padding: 10px; - cursor: pointer; - border-radius: var(--border-radius); - font-weight: 500; - font-family: inherit; - font-size: var(--text-size); - color: var(--secondary-foreground); - background: var(--secondary-background); - - border: none; - outline: 2px solid transparent; - transition: box-shadow 0.2s ease-in-out; - transition: outline-color 0.2s ease-in-out; - - &:focus { - box-shadow: 0 0 0 1.5pt var(--accent); - } - - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; -`; diff --git a/src/components/ui/DateDivider.tsx b/src/components/ui/DateDivider.tsx deleted file mode 100644 index f50af712..00000000 --- a/src/components/ui/DateDivider.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import styled, { css } from "styled-components/macro"; - -import { dayjs } from "../../context/Locale"; - -const Base = styled.div<{ unread?: boolean }>` - height: 0; - display: flex; - user-select: none; - align-items: center; - margin: 17px 12px 5px; - border-top: thin solid var(--tertiary-foreground); - - time { - margin-top: -2px; - font-size: 0.6875rem; - line-height: 0.6875rem; - padding-inline-start: 5px; - padding-inline-end: 5px; - color: var(--tertiary-foreground); - background: var(--primary-background); - } - - ${(props) => - props.unread && - css` - border-top: thin solid var(--accent); - `} -`; - -const Unread = styled.div` - background: var(--accent); - color: var(--accent-contrast); - font-size: 7px; - padding: 2px 6px; - font-size: 10px; - border-radius: 60px; - font-weight: 600; - margin-top: -1px; -`; - -interface Props { - date?: Date; - unread?: boolean; -} - -export default function DateDivider({ unread, date }: Props) { - return ( - - {unread && NEW} - {date && } - - ); -} diff --git a/src/components/ui/Details.tsx b/src/components/ui/Details.tsx deleted file mode 100644 index 7e905366..00000000 --- a/src/components/ui/Details.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import styled, { css } from "styled-components/macro"; - -import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice"; - -export default styled.details<{ sticky?: boolean; large?: boolean }>` - summary { - ${(props) => - props.sticky && - css` - top: 48px; - z-index: 10; - position: sticky; - ${() => - isTouchscreenDevice && - css` - top: 56px; - `} - `} - - ${(props) => - props.large && - css` - /*padding: 5px 0;*/ - background: var(--primary-background); - color: var(--secondary-foreground); - - .padding { - /*TOFIX: make this applicable only for the friends list menu, DO NOT REMOVE.*/ - display: flex; - align-items: center; - padding: 5px 0; - margin: 0.8em 0px 0.4em; - cursor: pointer; - } - `} - - outline: none; - cursor: pointer; - list-style: none; - user-select: none; - align-items: center; - transition: 0.2s opacity; - - font-size: 12px; - font-weight: 600; - text-transform: uppercase; - - &::marker, - &::-webkit-details-marker { - display: none; - } - - .title { - flex-grow: 1; - margin-top: 1px; - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - } - - .padding { - display: flex; - align-items: center; - - > svg { - flex-shrink: 0; - margin-inline-end: 4px; - transition: 0.2s ease transform; - } - } - } - - &:not([open]) { - summary { - opacity: 0.7; - } - - summary svg { - transform: rotateZ(-90deg); - } - } -`; diff --git a/src/components/ui/Header.tsx b/src/components/ui/Header.tsx index 4ff01ea5..860a294d 100644 --- a/src/components/ui/Header.tsx +++ b/src/components/ui/Header.tsx @@ -7,6 +7,8 @@ import { observer } from "mobx-react-lite"; import { useLocation } from "react-router-dom"; import styled, { css } from "styled-components/macro"; +import { Header } from "@revoltchat/ui"; + import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice"; import { useApplicationState } from "../../mobx/State"; @@ -18,82 +20,11 @@ interface Props { topBorder?: boolean; bottomBorder?: boolean; - background?: boolean; - transparent?: boolean; + withBackground?: boolean; + withTransparency?: boolean; placement: "primary" | "secondary"; } -const Header = styled.div` - 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` display: flex; align-items: center; @@ -128,7 +59,7 @@ export const PageHeader = observer( return (
{!noBurger && } diff --git a/src/components/ui/IconButton.tsx b/src/components/ui/IconButton.tsx deleted file mode 100644 index 6127d5f0..00000000 --- a/src/components/ui/IconButton.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import styled, { css } from "styled-components/macro"; - -interface Props { - rotate?: string; - type?: "default" | "circle"; -} - -const normal = `var(--secondary-foreground)`; -const hover = `var(--foreground)`; - -export default styled.div` - z-index: 1; - display: grid; - cursor: pointer; - place-items: center; - - transition: 0.1s ease all; - - fill: ${normal}; - color: ${normal}; - - a { - color: ${normal}; - } - - svg { - transition: 0.2s ease transform; - } - - &:hover { - fill: ${hover}; - color: ${hover}; - - a { - color: ${hover}; - } - } - - ${(props) => - props.type === "circle" && - css` - padding: 4px; - border-radius: var(--border-radius-half); - background-color: var(--secondary-header); - - &:hover { - background-color: var(--primary-header); - } - `} - - ${(props) => - props.rotate && - css` - svg { - transform: rotateZ(${props.rotate}); - } - `} -`; diff --git a/src/components/ui/InputBox.tsx b/src/components/ui/InputBox.tsx deleted file mode 100644 index ed48fdff..00000000 --- a/src/components/ui/InputBox.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import styled, { css } from "styled-components/macro"; - -interface Props { - readonly contrast?: boolean; -} - -export default styled.input` - z-index: 1; - font-size: 1rem; - padding: 8px 16px; - border-radius: var(--border-radius); - - font-family: inherit; - color: var(--foreground); - background: var(--primary-background); - transition: 0.2s ease background-color; - - border: none; - outline: 2px solid transparent; - transition: outline-color 0.2s ease-in-out; - transition: box-shadow 0.2s ease-in-out; - - &:hover { - background: var(--secondary-background); - } - - &:focus { - box-shadow: 0 0 0 1.5pt var(--accent); - } - - ${(props) => - props.contrast && - css` - color: var(--secondary-foreground); - background: var(--secondary-background); - - &:hover { - background: var(--hover); - } - `} -`; diff --git a/src/components/ui/LineDivider.tsx b/src/components/ui/LineDivider.tsx deleted file mode 100644 index b158c8ce..00000000 --- a/src/components/ui/LineDivider.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import styled from "styled-components/macro"; - -export default styled.div` - height: 0; - opacity: 0.6; - flex-shrink: 0; - margin: 8px 15px; - border-top: 1px solid var(--tertiary-foreground); -`; diff --git a/src/components/ui/Masks.tsx b/src/components/ui/Masks.tsx deleted file mode 100644 index 54fe74a6..00000000 --- a/src/components/ui/Masks.tsx +++ /dev/null @@ -1,26 +0,0 @@ -// This file must be imported and used at least once for SVG masks. - -export default function Masks() { - return ( - - - - - - - - - - - - - - - - - - - - - ); -} diff --git a/src/components/ui/Modal.tsx b/src/components/ui/Modal.tsx deleted file mode 100644 index 06eaf0d5..00000000 --- a/src/components/ui/Modal.tsx +++ /dev/null @@ -1,263 +0,0 @@ -/* eslint-disable react-hooks/rules-of-hooks */ -import styled, { css, keyframes } from "styled-components/macro"; - -import { createPortal, useCallback, useEffect, useState } from "preact/compat"; - -import { Button } from "@revoltchat/ui"; -import { Props as ButtonProps } from "@revoltchat/ui/esm/components/design/atoms/inputs/Button"; - -import { internalSubscribe } from "../../lib/eventEmitter"; - -import { Children } from "../../types/Preact"; - -const open = keyframes` - 0% {opacity: 0;} - 70% {opacity: 0;} - 100% {opacity: 1;} -`; - -const close = keyframes` - 0% {opacity: 1;} - 70% {opacity: 0;} - 100% {opacity: 0;} -`; - -const zoomIn = keyframes` - 0% {transform: scale(0.5);} - 98% {transform: scale(1.01);} - 100% {transform: scale(1);} -`; - -const zoomOut = keyframes` - 0% {transform: scale(1);} - 100% {transform: scale(0.5);} -`; - -const ModalBase = styled.div` - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: 9999; - position: fixed; - max-height: 100%; - user-select: none; - - animation-name: ${open}; - animation-duration: 0.2s; - - display: grid; - overflow-y: auto; - place-items: center; - - color: var(--foreground); - background: rgba(0, 0, 0, 0.8); - - &.closing { - animation-name: ${close}; - animation-fill-mode: forwards; - } - - &.closing > div { - animation-name: ${zoomOut}; - } -`; - -const ModalContainer = styled.div` - overflow: hidden; - max-width: calc(100vw - 20px); - border-radius: var(--border-radius); - - animation-name: ${zoomIn}; - animation-duration: 0.25s; - animation-timing-function: cubic-bezier(0.3, 0.3, 0.18, 1.1); -`; - -const ModalContent = styled.div< - { [key in "attachment" | "noBackground" | "border" | "padding"]?: boolean } ->` - text-overflow: ellipsis; - border-radius: var(--border-radius); - - h3 { - font-size: 14px; - text-transform: uppercase; - margin: 0; - margin-bottom: 10px; - color: var(--foreground); - } - - h5 { - margin: 0; - font-size: 13px; - font-weight: 500; - color: var(--secondary-foreground); - } - - form { - display: flex; - flex-direction: column; - gap: 8px; - - > div { - margin: 0; - color: var(--secondary-foreground); - font-size: 12px; - } - } - - .description { - color: var(--tertiary-foreground); - font-size: 90%; - } - - ${(props) => - !props.noBackground && - css` - background: var(--secondary-header); - `} - - ${(props) => - props.padding && - css` - padding: 1rem; - min-width: 450px; - `} - - ${(props) => - props.attachment && - css` - border-radius: var(--border-radius) var(--border-radius) 0 0; - `} - - ${(props) => - props.border && - css` - border-radius: var(--border-radius); - border: 2px solid var(--secondary-background); - `} -`; - -const ModalActions = styled.div` - gap: 8px; - display: flex; - flex-direction: row-reverse; - padding: 1rem; - background: var(--secondary-background); - border-radius: 0 0 var(--border-radius) var(--border-radius); -`; - -export type Action = Omit< - JSX.HTMLAttributes, - "as" | "onClick" -> & { - palette?: ButtonProps["palette"]; - confirmation?: boolean; - onClick: () => void; -}; - -interface Props { - children?: Children; - title?: Children; - description?: Children; - - disallowClosing?: boolean; - noBackground?: boolean; - dontModal?: boolean; - padding?: boolean; - - onClose?: () => void; - actions?: Action[]; - disabled?: boolean; - palette?: ButtonProps["palette"]; - border?: boolean; - visible: boolean; -} - -export let isModalClosing = false; - -export default function Modal(props: Props) { - if (!props.visible) return null; - - const content = ( - - {props.title &&

{props.title}

} - - {props.description &&
{props.description}
} - {props.children} -
- ); - - if (props.dontModal) { - return content; - } - - const [animateClose, setAnimateClose] = useState(false); - isModalClosing = animateClose; - const onClose = useCallback(() => { - setAnimateClose(true); - setTimeout(() => props.onClose!(), 2e2); - }, [setAnimateClose, props]); - - useEffect(() => internalSubscribe("Modal", "close", onClose), [onClose]); - - useEffect(() => { - if (props.disallowClosing) return; - - function keyDown(e: KeyboardEvent) { - if (e.key === "Escape") { - onClose(); - } - } - - document.body.addEventListener("keydown", keyDown); - return () => document.body.removeEventListener("keydown", keyDown); - }, [props.disallowClosing, onClose]); - - const confirmationAction = props.actions?.find( - (action) => action.confirmation, - ); - - useEffect(() => { - if (!confirmationAction) return; - - // ! TODO: this may be done better if we - // ! can focus the button although that - // ! doesn't seem to work... - function keyDown(e: KeyboardEvent) { - if (e.key === "Enter") { - confirmationAction!.onClick(); - } - } - - document.body.addEventListener("keydown", keyDown); - return () => document.body.removeEventListener("keydown", keyDown); - }, [confirmationAction]); - - return createPortal( - - (e.cancelBubble = true)}> - {content} - {props.actions && ( - - {props.actions.map((x, index) => ( - )} {error && error.name === "NotAllowedError" && ( - + + + )}
diff --git a/src/pages/settings/panes/Experiments.tsx b/src/pages/settings/panes/Experiments.tsx index b91e756f..8f36f7bd 100644 --- a/src/pages/settings/panes/Experiments.tsx +++ b/src/pages/settings/panes/Experiments.tsx @@ -3,14 +3,14 @@ import { observer } from "mobx-react-lite"; import styles from "./Panes.module.scss"; import { Text } from "preact-i18n"; +import { Checkbox, Column } from "@revoltchat/ui"; + import { useApplicationState } from "../../../mobx/State"; import { AVAILABLE_EXPERIMENTS, EXPERIMENTS, } from "../../../mobx/stores/Experiments"; -import Checkbox from "../../../components/ui/Checkbox"; - export const ExperimentsPage = observer(() => { const experiments = useApplicationState().experiments; @@ -19,15 +19,19 @@ export const ExperimentsPage = observer(() => {

- {AVAILABLE_EXPERIMENTS.map((key) => ( - experiments.setEnabled(key, enabled)} - description={EXPERIMENTS[key].description}> - {EXPERIMENTS[key].title} - - ))} + + {AVAILABLE_EXPERIMENTS.map((key) => ( + + experiments.setEnabled(key, enabled) + } + description={EXPERIMENTS[key].description} + title={EXPERIMENTS[key].title} + /> + ))} + {AVAILABLE_EXPERIMENTS.length === 0 && (
diff --git a/src/pages/settings/panes/Feedback.tsx b/src/pages/settings/panes/Feedback.tsx index 106159db..66125beb 100644 --- a/src/pages/settings/panes/Feedback.tsx +++ b/src/pages/settings/panes/Feedback.tsx @@ -5,7 +5,7 @@ import { Link } from "react-router-dom"; import styles from "./Panes.module.scss"; import { Text } from "preact-i18n"; -import CategoryButton from "../../../components/ui/fluent/CategoryButton"; +import { CategoryButton } from "@revoltchat/ui"; export function Feedback() { return ( @@ -15,7 +15,6 @@ export function Feedback() { target="_blank" rel="noreferrer"> } description={ @@ -29,7 +28,6 @@ export function Feedback() { target="_blank" rel="noreferrer"> } description={ @@ -43,7 +41,6 @@ export function Feedback() { target="_blank" rel="noreferrer"> } description={ @@ -55,7 +52,6 @@ export function Feedback() { } description="You can report issues and discuss improvements with us directly here."> diff --git a/src/pages/settings/panes/Languages.tsx b/src/pages/settings/panes/Languages.tsx index a555e56d..c347de52 100644 --- a/src/pages/settings/panes/Languages.tsx +++ b/src/pages/settings/panes/Languages.tsx @@ -5,6 +5,8 @@ import styles from "./Panes.module.scss"; import { Text } from "preact-i18n"; import { useMemo } from "preact/hooks"; +import { Checkbox, LineDivider, Tip } from "@revoltchat/ui"; + import { useApplicationState } from "../../../mobx/State"; import britannyFlagSVG from "../assets/flags/brittany.svg"; @@ -21,8 +23,6 @@ import { Languages as Langs, } from "../../../../external/lang/Languages"; import Emoji from "../../../components/common/Emoji"; -import Checkbox from "../../../components/ui/Checkbox"; -import Tip from "../../../components/ui/Tip"; type Key = [Language, LanguageEntry]; @@ -41,66 +41,82 @@ function Entry({ entry: [x, lang], selected, onSelect }: Props) { -
- {lang.i18n === "vec" ? ( - - ) : lang.i18n === "br" ? ( - - ) : lang.i18n === "ckb" ? ( - - ) : lang.i18n === "eo" ? ( - - ) : lang.i18n === "ta" ? ( - - ) : lang.emoji === "🙂" ? ( - - ) : lang.emoji === "🪄" ? ( - - ) : ( - - )} -
- - {lang.display} {lang.verified && } - -
+ value={selected} + onChange={onSelect} + title={ + <> +
+ {lang.i18n === "vec" ? ( + + ) : lang.i18n === "br" ? ( + + ) : lang.i18n === "ckb" ? ( + + ) : lang.i18n === "eo" ? ( + + ) : lang.i18n === "ta" ? ( + + ) : lang.emoji === "🙂" ? ( + + ) : lang.emoji === "🪄" ? ( + + ) : ( + + )} +
+ + {lang.display} {lang.verified && } + + + } + /> ); } @@ -181,16 +197,17 @@ export const Languages = observer(() => { .filter(([, lang]) => lang.cat === "alt") .map(EntryFactory)}
+ - - {" "} - - - + {" "} + + + + ); diff --git a/src/pages/settings/panes/MyBots.tsx b/src/pages/settings/panes/MyBots.tsx index 1a50d726..c3a49d70 100644 --- a/src/pages/settings/panes/MyBots.tsx +++ b/src/pages/settings/panes/MyBots.tsx @@ -11,7 +11,13 @@ import styles from "./Panes.module.scss"; import { Text } from "preact-i18n"; import { useCallback, useEffect, useState } from "preact/hooks"; -import { Button } from "@revoltchat/ui"; +import { + Button, + CategoryButton, + Checkbox, + InputBox, + Tip, +} from "@revoltchat/ui"; import TextAreaAutoSize from "../../../lib/TextAreaAutoSize"; import { internalEmit } from "../../../lib/eventEmitter"; @@ -28,10 +34,6 @@ import AutoComplete, { import CollapsibleSection from "../../../components/common/CollapsibleSection"; import Tooltip from "../../../components/common/Tooltip"; import UserIcon from "../../../components/common/user/UserIcon"; -import Checkbox from "../../../components/ui/Checkbox"; -import InputBox from "../../../components/ui/InputBox"; -import Tip from "../../../components/ui/Tip"; -import CategoryButton from "../../../components/ui/fluent/CategoryButton"; interface Data { _id: string; @@ -436,15 +438,14 @@ function BotCard({ bot, onDelete, onUpdate }: Props) { /> } description={ } - onChange={(v) => setData({ ...data, public: v })}> - - + onChange={(v) => setData({ ...data, public: v })} + />

@@ -452,6 +453,7 @@ function BotCard({ bot, onDelete, onUpdate }: Props) { - - {error} - + {error} )} diff --git a/src/pages/settings/panes/Native.tsx b/src/pages/settings/panes/Native.tsx index 8da95a67..da02d777 100644 --- a/src/pages/settings/panes/Native.tsx +++ b/src/pages/settings/panes/Native.tsx @@ -2,11 +2,8 @@ import { Refresh } from "@styled-icons/boxicons-regular"; import { useEffect, useState } from "preact/hooks"; -import { Button } from "@revoltchat/ui"; +import { Button, CategoryButton, Checkbox, Tip } from "@revoltchat/ui"; -import Checkbox from "../../../components/ui/Checkbox"; -import Tip from "../../../components/ui/Tip"; -import CategoryButton from "../../../components/ui/fluent/CategoryButton"; import RLogo from "../assets/revolt_r.svg"; export function Native() { @@ -27,10 +24,10 @@ export function Native() { return (
- Some options might require a restart. + Some options might require a restart.

App Behavior

{ if (v) { @@ -41,12 +38,12 @@ export function Native() { setAutoStart(v); }} - description="Launch Revolt when you log into your computer."> - Start with computer - + title="Start with computer" + description="Launch Revolt when you log into your computer." + /> { window.native.set("minimiseToTray", minimiseToTray); setConfig({ @@ -54,11 +51,11 @@ export function Native() { minimiseToTray, }); }} - description="Instead of closing, Revolt will hide in your tray."> - Minimise to Tray - + title="Minimise to Tray" + description="Instead of closing, Revolt will hide in your tray." + /> { window.native.set("discordRPC", discordRPC); setConfig({ @@ -66,11 +63,11 @@ export function Native() { discordRPC, }); }} - description="Rep Revolt on your Discord status."> - Enable Discord status - + title="Enable Discord status" + description="Rep Revolt on your Discord status." + /> { const build = nightly ? "nightly" : "stable"; window.native.set("build", build); @@ -80,12 +77,13 @@ export function Native() { build, }); }} - description="Use the beta branch of Revolt."> - Revolt Nightly - + title="Revolt Nightly" + description="Use the beta branch of Revolt." + /> +

Titlebar

{ window.native.set("frame", !frame); setHintRelaunch(true); @@ -94,12 +92,12 @@ export function Native() { frame: !frame, }); }} - description={<>Let Revolt use its own window frame.}> - Custom window frame - + title="Custom window frame" + description="Let Revolt use its own window frame." + /> { window.native.set("frame", !frame); setHintRelaunch(true); @@ -108,12 +106,12 @@ export function Native() { frame: !frame, }); }} - description="Show mute/deafen buttons on the titlebar."> - Enable quick action buttons - + title="Enable quick action buttons" + description="Show mute/deafen buttons on the titlebar." + />

Advanced

{ window.native.set( "hardwareAcceleration", @@ -125,9 +123,10 @@ export function Native() { hardwareAcceleration, }); }} - description="Uses your GPU to render the app, disable if you run into visual issues."> - Hardware Acceleration - + title="Hardware Acceleration" + description="Uses your GPU to render the app, disable if you run into visual issues." + /> +

); }); diff --git a/src/pages/settings/panes/Panes.module.scss b/src/pages/settings/panes/Panes.module.scss index ff5c3321..e5faf987 100644 --- a/src/pages/settings/panes/Panes.module.scss +++ b/src/pages/settings/panes/Panes.module.scss @@ -128,8 +128,9 @@ border-radius: var(--border-radius); width: 100%; display: flex; - display: grid; - place-items: center; + flex-direction: column; + align-items: center; + justify-content: center; grid-template-columns: minmax(auto, 100%); > div { @@ -480,12 +481,14 @@ border-radius: var(--border-radius); margin-top: 0; + transition: 0.1s ease background-color; + &:hover { background: var(--secondary-background); } } - .entry > span > span { + .entry > div div { gap: 8px; display: flex; align-items: center; diff --git a/src/pages/settings/panes/Plugins.tsx b/src/pages/settings/panes/Plugins.tsx index 8bf45a86..51b13f50 100644 --- a/src/pages/settings/panes/Plugins.tsx +++ b/src/pages/settings/panes/Plugins.tsx @@ -1,17 +1,12 @@ -import { Check } from "@styled-icons/boxicons-regular"; import { observer } from "mobx-react-lite"; -import styled from "styled-components"; import styles from "./Panes.module.scss"; import { Text } from "preact-i18n"; -import { Button } from "@revoltchat/ui"; +import { Button, Checkbox, Tip } from "@revoltchat/ui"; import { useApplicationState } from "../../../mobx/State"; -import { CheckboxBase, Checkmark } from "../../../components/ui/Checkbox"; -import Tip from "../../../components/ui/Tip"; - // Just keeping this here for general purpose. Should probably be exported // elsewhere, though. interface Plugin { @@ -21,45 +16,10 @@ interface Plugin { enabled: boolean | undefined; } -const CustomCheckboxBase = styled(CheckboxBase)` - margin-top: 0 !important; -`; -export interface CheckboxProps { - checked: boolean; - disabled?: boolean; - onChange: (state: boolean) => void; -} -function PluginCheckbox(props: CheckboxProps) { - // HACK HACK HACK(lexisother): THIS ENTIRE THING IS A HACK!!!! - /* - Until some reviewer points me in the right direction, I've resorted to - fabricating my own checkbox component. - "WHY?!", you might ask. Well, the normal `Checkbox` component can take - textual contents, and *also* adds a `margin-top` of 20 pixels. - We... don't need that. At all. *Especially* the margin. It makes our card - look disproportionate. - - Apologies, @insert! - */ - return ( - - - !props.disabled && props.onChange(!props.checked) - } - /> - - - - - ); -} - interface CardProps { plugin: Plugin; } + function PluginCard({ plugin }: CardProps) { const plugins = useApplicationState().plugins; @@ -70,12 +30,14 @@ function PluginCard({ plugin }: CardProps) {
-
- {plugin.namespace} / {plugin.id} -
- + {plugin.namespace} / {plugin.id} + + } onChange={() => { !plugin.enabled ? plugins.load(plugin.namespace, plugin.id) @@ -105,7 +67,7 @@ export const PluginsPage = observer(() => { const plugins = useApplicationState().plugins; return (
- + {plugins.list().map((plugin) => { diff --git a/src/pages/settings/panes/Profile.tsx b/src/pages/settings/panes/Profile.tsx index 45fe95fb..ff1dd44b 100644 --- a/src/pages/settings/panes/Profile.tsx +++ b/src/pages/settings/panes/Profile.tsx @@ -7,7 +7,7 @@ import styles from "./Panes.module.scss"; import { Text } from "preact-i18n"; 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 { useTranslation } from "../../../lib/i18n"; @@ -23,7 +23,6 @@ import { import AutoComplete, { useAutoComplete, } from "../../../components/common/AutoComplete"; -import Tip from "../../../components/ui/Tip"; export const Profile = observer(() => { const status = useContext(StatusContext); @@ -203,11 +202,15 @@ export const Profile = observer(() => {

+ + - Want to change your username?{" "} - switchPage("account")}> - Head over to your account settings. - + + Want to change your username?{" "} + switchPage("account")}> + Head over to your account settings. + +
); diff --git a/src/pages/settings/panes/Sessions.tsx b/src/pages/settings/panes/Sessions.tsx index 24bb1bb2..0e951f4c 100644 --- a/src/pages/settings/panes/Sessions.tsx +++ b/src/pages/settings/panes/Sessions.tsx @@ -18,16 +18,18 @@ import styles from "./Panes.module.scss"; import { Text } from "preact-i18n"; import { useContext, useEffect, useState } from "preact/hooks"; -import { Button } from "@revoltchat/ui"; +import { + Button, + CategoryButton, + LineDivider, + Preloader, + Tip, +} from "@revoltchat/ui"; import { dayjs } from "../../../context/Locale"; import { useIntermediate } from "../../../context/intermediate/Intermediate"; import { AppContext } from "../../../context/revoltjs/RevoltClient"; -import Preloader from "../../../components/ui/Preloader"; -import Tip from "../../../components/ui/Tip"; -import CategoryButton from "../../../components/ui/fluent/CategoryButton"; - dayjs.extend(relativeTime); export function Sessions() { @@ -249,13 +251,14 @@ export function Sessions() { + - - {" "} - switchPage("account")}> - - + {" "} + switchPage("account")}> + + +
); diff --git a/src/pages/settings/panes/Sync.tsx b/src/pages/settings/panes/Sync.tsx index f426c4d4..c068634f 100644 --- a/src/pages/settings/panes/Sync.tsx +++ b/src/pages/settings/panes/Sync.tsx @@ -3,11 +3,11 @@ import { observer } from "mobx-react-lite"; import styles from "./Panes.module.scss"; import { Text } from "preact-i18n"; +import { Checkbox, Column } from "@revoltchat/ui"; + import { useApplicationState } from "../../../mobx/State"; import { SyncKeys } from "../../../mobx/stores/Sync"; -import Checkbox from "../../../components/ui/Checkbox"; - export const Sync = observer(() => { const sync = useApplicationState().sync; @@ -20,26 +20,28 @@ export const Sync = observer(() => {

- {( - [ - ["appearance", "appearance.title"], - ["theme", "appearance.theme"], - ["locale", "language.title"], - // notifications sync is always-on - ] as [SyncKeys, string][] - ).map(([key, title]) => ( - - } - onChange={() => sync.toggle(key)}> - - - ))} + + {( + [ + ["appearance", "appearance.title"], + ["theme", "appearance.theme"], + ["locale", "language.title"], + // notifications sync is always-on + ] as [SyncKeys, string][] + ).map(([key, title]) => ( + } + description={ + + } + onChange={() => sync.toggle(key)} + /> + ))} + {/*
Last sync at 12:00
*/} diff --git a/src/pages/settings/panes/ThemeShop.tsx b/src/pages/settings/panes/ThemeShop.tsx deleted file mode 100644 index ea010736..00000000 --- a/src/pages/settings/panes/ThemeShop.tsx +++ /dev/null @@ -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 => - fetch(`${import.meta.env.VITE_THEMES_URL}/manifest.json`).then((res) => - res.json(), - ); - -export const fetchTheme = (slug: string): Promise => - 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; -}; - -// 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, "as"> & { - slug?: string; - theme?: Theme; - onThemeLoaded?: (theme: Theme) => void; -}; - -const ThemePreview = ({ theme, ...props }: ThemePreviewProps) => { - return ( - - ); -}; - -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>({}); - - 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 ( - -
- -
- {/* -
- Oops! Couldn't load the theme shop. Make sure you're - connected to the internet and try again. -
-
*/} - - The Theme Shop is currently under construction. - - - {/* FIXME INTEGRATE WITH MOBX */} - {/* -
- - -
-
-
theme svg goes here
-
-
Theme Title
-
- {" "} - Author -
-
This is a theme description.
-
-
-
- " contrast /> - -
- - -
- - - -
- - -
- - -
- - - -
- - -
- - -
- - - -
- - -
- - -
- - - -
*/} -
- - {themeList?.map(([slug, theme]) => ( - - -

{theme.name}

- {/* Maybe id's of the users should be included as well / instead? */} -
- {" "} - {theme.creator} -
-
{theme.description}
-
- ))} -
-
- ); -} diff --git a/src/pages/settings/server/Bans.tsx b/src/pages/settings/server/Bans.tsx index a6fefeec..ed458946 100644 --- a/src/pages/settings/server/Bans.tsx +++ b/src/pages/settings/server/Bans.tsx @@ -8,10 +8,9 @@ import styles from "./Panes.module.scss"; import { Text } from "preact-i18n"; import { useEffect, useMemo, useState } from "preact/hooks"; +import { IconButton, InputBox, Preloader } from "@revoltchat/ui"; + import UserIcon from "../../../components/common/user/UserIcon"; -import IconButton from "../../../components/ui/IconButton"; -import Preloader from "../../../components/ui/Preloader"; -import { InputBox } from "@revoltchat/ui"; interface InnerProps { ban: API.ServerBan; diff --git a/src/pages/settings/server/Categories.tsx b/src/pages/settings/server/Categories.tsx index 5dd9e74c..9d73dd26 100644 --- a/src/pages/settings/server/Categories.tsx +++ b/src/pages/settings/server/Categories.tsx @@ -8,6 +8,8 @@ import { ulid } from "ulid"; import { Text } from "preact-i18n"; import { useCallback, useEffect, useMemo, useState } from "preact/hooks"; +import { SaveStatus } from "@revoltchat/ui"; + import { useAutosave } from "../../../lib/debounce"; import { Draggable, Droppable } from "../../../lib/dnd"; import { noop } from "../../../lib/js"; @@ -15,7 +17,6 @@ import { noop } from "../../../lib/js"; import { useIntermediate } from "../../../context/intermediate/Intermediate"; import ChannelIcon from "../../../components/common/ChannelIcon"; -import SaveStatus, { EditStatus } from "../../../components/ui/SaveStatus"; const KanbanEntry = styled.div` padding: 2px 4px; @@ -132,7 +133,9 @@ interface Props { } export const Categories = observer(({ server }: Props) => { - const [status, setStatus] = useState("saved"); + const [status, setStatus] = useState<"saved" | "editing" | "saving">( + "saved", + ); const [categories, setCategories] = useState( server.categories ?? [], ); diff --git a/src/pages/settings/server/Invites.tsx b/src/pages/settings/server/Invites.tsx index f4f64d1f..cbd6abbb 100644 --- a/src/pages/settings/server/Invites.tsx +++ b/src/pages/settings/server/Invites.tsx @@ -7,12 +7,12 @@ import styles from "./Panes.module.scss"; import { Text } from "preact-i18n"; import { useEffect, useState } from "preact/hooks"; +import { IconButton, Preloader } from "@revoltchat/ui"; + import { getChannelName } from "../../../context/revoltjs/util"; import UserIcon from "../../../components/common/user/UserIcon"; import { Username } from "../../../components/common/user/UserShort"; -import IconButton from "../../../components/ui/IconButton"; -import Preloader from "../../../components/ui/Preloader"; interface InnerProps { invite: API.Invite; diff --git a/src/pages/settings/server/Members.tsx b/src/pages/settings/server/Members.tsx index d7cd740e..ff2f08b2 100644 --- a/src/pages/settings/server/Members.tsx +++ b/src/pages/settings/server/Members.tsx @@ -9,14 +9,17 @@ import styles from "./Panes.module.scss"; import { Text } from "preact-i18n"; import { useEffect, useMemo, useState } from "preact/hooks"; -import { Button, Preloader } from "@revoltchat/ui"; +import { + Button, + Category, + Checkbox, + IconButton, + InputBox, + Preloader, +} from "@revoltchat/ui"; import UserIcon from "../../../components/common/user/UserIcon"; import { Username } from "../../../components/common/user/UserShort"; -import Checkbox from "../../../components/ui/Checkbox"; -import IconButton from "../../../components/ui/IconButton"; -import InputBox from "../../../components/ui/InputBox"; -import Overline from "../../../components/ui/Overline"; interface InnerProps { member: Member; @@ -48,13 +51,21 @@ const Inner = observer(({ member }: InnerProps) => {
{open && (
- Roles + Roles {Object.keys(server_roles).map((key) => { const role = server_roles[key]; return ( + {role.name} + + } onChange={(v) => { if (v) { setRoles([...roles, key]); @@ -63,14 +74,8 @@ const Inner = observer(({ member }: InnerProps) => { roles.filter((x) => x !== key), ); } - }}> - - {role.name} - - + }} + /> ); })}