diff --git a/external/lang b/external/lang index 24766f8f5c2147ba866af922e3f5f42ad3ea44e4..2a9ef2c8025dd71a20fddca7c94cb7af30978c4b 160000 --- a/external/lang +++ b/external/lang @@ -1 +1 @@ -Subproject commit 24766f8f5c2147ba866af922e3f5f42ad3ea44e4 +Subproject commit 2a9ef2c8025dd71a20fddca7c94cb7af30978c4b diff --git a/package.json b/package.json index 595b73b7acdd6baa674907de7726b50d192c0015..2b7932669e2c6a2dc4bf0715601f016afa203a9e 100644 --- a/package.json +++ b/package.json @@ -25,8 +25,23 @@ "preact": "^10.5.13" }, "devDependencies": { - "@fontsource/fira-mono": "^4.4.5", + "@fontsource/atkinson-hyperlegible": "^4.4.5", + "@fontsource/bree-serif": "^4.4.5", + "@fontsource/comic-neue": "^4.4.5", + "@fontsource/fira-code": "^4.4.5", + "@fontsource/inter": "^4.4.5", + "@fontsource/lato": "^4.4.5", + "@fontsource/montserrat": "^4.4.5", + "@fontsource/noto-sans": "^4.4.5", "@fontsource/open-sans": "^4.4.5", + "@fontsource/poppins": "^4.4.5", + "@fontsource/raleway": "^4.4.5", + "@fontsource/roboto": "^4.4.5", + "@fontsource/roboto-mono": "^4.4.5", + "@fontsource/source-code-pro": "^4.4.5", + "@fontsource/space-mono": "^4.4.5", + "@fontsource/ubuntu": "^4.4.5", + "@fontsource/ubuntu-mono": "^4.4.5", "@hcaptcha/react-hcaptcha": "^0.3.6", "@preact/preset-vite": "^2.0.0", "@rollup/plugin-replace": "^2.4.2", @@ -86,7 +101,7 @@ "typescript": "^4.3.2", "ulid": "^2.3.0", "use-resize-observer": "^7.0.0", - "vite": "^2.3.7", + "vite": "npm:@insertish/vite@2.2.4-dynamic-import-css-f428476", "vite-plugin-pwa": "^0.8.1", "workbox-precaching": "^6.1.5" } diff --git a/src/components/common/CollapsibleSection.tsx b/src/components/common/CollapsibleSection.tsx new file mode 100644 index 0000000000000000000000000000000000000000..3b6409fb9a32b916f697110693ea57b5890e9302 --- /dev/null +++ b/src/components/common/CollapsibleSection.tsx @@ -0,0 +1,50 @@ +import Details from "../ui/Details"; +import { State, store } from "../../redux"; +import { Action } from "../../redux/reducers"; +import { Children } from "../../types/Preact"; +import { ChevronDown } from "@styled-icons/boxicons-regular"; + +interface Props { + id: string; + defaultValue: boolean; + + sticky?: boolean; + large?: boolean; + + summary: Children; + children: Children; +} + +export default function CollapsibleSection({ id, defaultValue, summary, children, ...detailsProps }: Props) { + const state: State = store.getState(); + + function setState(state: boolean) { + if (state === defaultValue) { + store.dispatch({ + type: 'SECTION_TOGGLE_UNSET', + id + } as Action); + } else { + store.dispatch({ + type: 'SECTION_TOGGLE_SET', + id, + state + } as Action); + } + } + + return ( + <Details + open={state.sectionToggle[id] ?? defaultValue} + onToggle={e => setState(e.currentTarget.open)} + {...detailsProps}> + <summary> + <div class="padding"> + <ChevronDown size={20} /> + { summary } + </div> + </summary> + { children } + </Details> + ) +} diff --git a/src/components/common/Tooltip.tsx b/src/components/common/Tooltip.tsx index c8497e4096a46ae6dc7c871257b2b9e12cb72096..7a9957f4cc6355bf0d4a34d1f7ab3f4f8e35c6c3 100644 --- a/src/components/common/Tooltip.tsx +++ b/src/components/common/Tooltip.tsx @@ -24,14 +24,16 @@ const PermissionTooltipBase = styled.div` display: flex; align-items: center; flex-direction: column; + span { font-weight: 700; text-transform: uppercase; color: var(--secondary-foreground); font-size: 11px; } + code { - font-family: 'Fira Mono'; + font-family: var(--monoscape-font); } `; diff --git a/src/components/common/messaging/attachments/Attachment.module.scss b/src/components/common/messaging/attachments/Attachment.module.scss index 4f23a898a2e9ebaeef9df8f41c2e9b160decb941..5aa89744d83a997be557862d27105019ed2cfaf2 100644 --- a/src/components/common/messaging/attachments/Attachment.module.scss +++ b/src/components/common/messaging/attachments/Attachment.module.scss @@ -2,6 +2,13 @@ border-radius: 6px; margin: .125rem 0 .125rem; + height: auto; + + max-height: 640px; + max-width: min(480px, 100%); + + object-fit: contain; + &[data-spoiler="true"] { filter: blur(30px); pointer-events: none; @@ -71,7 +78,7 @@ } pre code { - font-family: "Fira Mono", sans-serif; + font-family: var(--monoscape-font), sans-serif; } &[data-loading="true"] { diff --git a/src/components/common/messaging/attachments/Attachment.tsx b/src/components/common/messaging/attachments/Attachment.tsx index eb144ebee6813d21426d49a28a7065899cfe002f..15c4253658859c3a910e4c3407385be13e8a7036 100644 --- a/src/components/common/messaging/attachments/Attachment.tsx +++ b/src/components/common/messaging/attachments/Attachment.tsx @@ -15,60 +15,33 @@ interface Props { } const MAX_ATTACHMENT_WIDTH = 480; -const MAX_ATTACHMENT_HEIGHT = 640; export default function Attachment({ attachment, hasContent }: Props) { const client = useContext(AppContext); const { openScreen } = useIntermediate(); const { filename, metadata } = attachment; const [ spoiler, setSpoiler ] = useState(filename.startsWith("SPOILER_")); - const maxWidth = Math.min(useContext(MessageAreaWidthContext), MAX_ATTACHMENT_WIDTH); const url = client.generateFileURL(attachment, { width: MAX_ATTACHMENT_WIDTH * 1.5 }, true); - let width = 0, - height = 0; - - if (metadata.type === 'Image' || metadata.type === 'Video') { - let limitingWidth = Math.min( - maxWidth, - metadata.width - ); - let limitingHeight = Math.min( - MAX_ATTACHMENT_HEIGHT, - metadata.height - ); - - // Calculate smallest possible WxH. - width = Math.min( - limitingWidth, - limitingHeight * (metadata.width / metadata.height) - ); - - height = Math.min( - limitingHeight, - limitingWidth * (metadata.height / metadata.width) - ); - } switch (metadata.type) { case "Image": { return ( <div - style={{ width }} className={styles.container} onClick={() => spoiler && setSpoiler(false)} > {spoiler && ( <div className={styles.overflow}> - <div style={{ width, height }}> - <span><Text id="app.main.channel.misc.spoiler_attachment" /></span> - </div> + <span><Text id="app.main.channel.misc.spoiler_attachment" /></span> </div> )} <img src={url} alt={filename} + width={metadata.width} + height={metadata.height} data-spoiler={spoiler} data-has-content={hasContent} className={classNames(styles.attachment, styles.image)} @@ -79,7 +52,6 @@ export default function Attachment({ attachment, hasContent }: Props) { ev.button === 1 && window.open(url, "_blank") } - style={{ width, height }} /> </div> ); @@ -102,13 +74,10 @@ export default function Attachment({ attachment, hasContent }: Props) { onClick={() => spoiler && setSpoiler(false)}> {spoiler && ( <div className={styles.overflow}> - <div style={{ width, height }}> - <span><Text id="app.main.channel.misc.spoiler_attachment" /></span> - </div> + <span><Text id="app.main.channel.misc.spoiler_attachment" /></span> </div> )} <div - style={{ width }} data-spoiler={spoiler} data-has-content={hasContent} className={classNames(styles.attachment, styles.video)} @@ -117,7 +86,6 @@ export default function Attachment({ attachment, hasContent }: Props) { <video src={url} controls - style={{ width, height }} onMouseDown={ev => ev.button === 1 && window.open(url, "_blank") diff --git a/src/components/common/messaging/bars/FilePreview.tsx b/src/components/common/messaging/bars/FilePreview.tsx index 9f0a98c4c41e7e812e0c9c4c6cad868df8e505f1..80f9fae594cb8f04aad16e8a1b42710b61322e23 100644 --- a/src/components/common/messaging/bars/FilePreview.tsx +++ b/src/components/common/messaging/bars/FilePreview.tsx @@ -101,6 +101,7 @@ const PreviewBox = styled.div` .icon { height: 100px; + width: 100%; margin-bottom: 4px; object-fit: contain; } diff --git a/src/components/markdown/Markdown.module.scss b/src/components/markdown/Markdown.module.scss index de439a3482daa4d065c74cf02744edb2f2700766..202ce12edde7c4561ce07162fa31584fb698e235 100644 --- a/src/components/markdown/Markdown.module.scss +++ b/src/components/markdown/Markdown.module.scss @@ -1,5 +1,3 @@ -@import "@fontsource/fira-mono/400.css"; - .markdown { :global(.emoji) { height: 1.25em; @@ -89,7 +87,7 @@ font-size: 90%; border-radius: 4px; background: var(--block); - font-family: "Fira Mono", monospace; + font-family: var(--monoscape-font), monospace; } input[type="checkbox"] { @@ -136,7 +134,7 @@ } :global(.code) { - font-family: "Fira Mono", monospace; + font-family: var(--monoscape-font), monospace; :global(.lang) { // height: 8px; diff --git a/src/components/navigation/left/HomeSidebar.tsx b/src/components/navigation/left/HomeSidebar.tsx index cd98bc7e6b7cbcd209118edbfe97129e37206a5d..16e586bbcfe6f93e451479596ac3a81faad160e0 100644 --- a/src/components/navigation/left/HomeSidebar.tsx +++ b/src/components/navigation/left/HomeSidebar.tsx @@ -1,4 +1,4 @@ -import { Localizer, Text } from "preact-i18n"; +import { Text } from "preact-i18n"; import { useContext, useEffect } from "preact/hooks"; import { Home, UserDetail, Wrench, Notepad } from "@styled-icons/boxicons-solid"; @@ -105,13 +105,9 @@ function HomeSidebar(props: Props) { </ButtonItem> </Link> )} - <Localizer> - <Category - text={<Text id="app.main.categories.conversations" />} - /** @ts-ignore : ignored due to conflicting naming between the Category property name and the existing JSX attribute */ - action={() => openScreen({ id: "special_input", type: "create_group" })} - /> - </Localizer> + <Category + text={<Text id="app.main.categories.conversations" />} + action={() => openScreen({ id: "special_input", type: "create_group" })} /> {channelsArr.length === 0 && <img src={placeholderSVG} />} {channelsArr.map(x => { let user; diff --git a/src/components/navigation/left/ServerSidebar.tsx b/src/components/navigation/left/ServerSidebar.tsx index 49e5795951f632cb783c283744640de2dc4ec939..3dd7aff9d37dddba1ef063fa6414b0978d4d8e27 100644 --- a/src/components/navigation/left/ServerSidebar.tsx +++ b/src/components/navigation/left/ServerSidebar.tsx @@ -14,6 +14,7 @@ import ServerHeader from "../../common/ServerHeader"; import { useEffect } from "preact/hooks"; import Category from "../../ui/Category"; import ConditionalLink from "../../../lib/ConditionalLink"; +import CollapsibleSection from "../../common/CollapsibleSection"; interface Props { unreads: Unreads; @@ -69,6 +70,7 @@ function ServerSidebar(props: Props & WithDispatcher) { let uncategorised = new Set(server.channels); let elements = []; + function addChannel(id: string) { const entry = channels.find(x => x._id === id); if (!entry) return; @@ -76,9 +78,8 @@ function ServerSidebar(props: Props & WithDispatcher) { const active = channel?._id === entry._id; return ( - <ConditionalLink active={active} to={`/server/${server!._id}/channel/${entry._id}`}> + <ConditionalLink key={entry._id} active={active} to={`/server/${server!._id}/channel/${entry._id}`}> <ChannelButton - key={entry._id} channel={entry} active={active} alert={entry.unread} @@ -90,16 +91,24 @@ function ServerSidebar(props: Props & WithDispatcher) { if (server.categories) { for (let category of server.categories) { - elements.push(<Category text={category.title} />); - + let channels = []; for (let id of category.channels) { uncategorised.delete(id); - elements.push(addChannel(id)); + channels.push(addChannel(id)); } + + elements.push( + <CollapsibleSection + id={`category_${category.id}`} + defaultValue + summary={<Category text={category.title} />}> + { channels } + </CollapsibleSection> + ); } } - for (let id of uncategorised) { + for (let id of Array.from(uncategorised).reverse()) { elements.unshift(addChannel(id)); } diff --git a/src/components/navigation/right/MemberSidebar.tsx b/src/components/navigation/right/MemberSidebar.tsx index c5bcaf677cb29d47d405246af34b729109cd0e05..03287b15375b39de5919faf944c1111308b7e25b 100644 --- a/src/components/navigation/right/MemberSidebar.tsx +++ b/src/components/navigation/right/MemberSidebar.tsx @@ -2,6 +2,7 @@ import { Text } from "preact-i18n"; import { useContext, useEffect, useState } from "preact/hooks"; import { User } from "revolt.js"; +import Details from "../../../components/ui/Details"; import Category from "../../ui/Category"; import { useParams } from "react-router"; import { UserButton } from "../items/ButtonItem"; diff --git a/src/components/ui/Button.tsx b/src/components/ui/Button.tsx index 8582a19370252b93174386135a652c4b66a6e9ff..870e7ed22a463a87be437f63f97039f21925c89d 100644 --- a/src/components/ui/Button.tsx +++ b/src/components/ui/Button.tsx @@ -10,7 +10,7 @@ export default styled.button<Props>` padding: 8px; font-size: 16px; text-align: center; - font-family: 'Open Sans', sans-serif; + font-family: inherit; transition: 0.2s ease opacity; transition: 0.2s ease background-color; diff --git a/src/components/ui/Category.tsx b/src/components/ui/Category.tsx index 79f1d0bc33de6a025ba65ce260ee39ebf554bbc1..38980b19f5906a4db78c276c78ae32c2a08057cb 100644 --- a/src/components/ui/Category.tsx +++ b/src/components/ui/Category.tsx @@ -31,7 +31,7 @@ const CategoryBase = styled.div<Pick<Props, 'variant'>>` ` } `; -type Props = Omit<JSX.HTMLAttributes<HTMLDivElement>, 'children' | 'as'> & { +type Props = Omit<JSX.HTMLAttributes<HTMLDivElement>, 'children' | 'as' | 'action'> & { text: Children; // TODO: rename from action to prevent type conflicts with the dom action?: () => void; diff --git a/src/components/ui/Checkbox.tsx b/src/components/ui/Checkbox.tsx index bc87c5cc98ac323fc4609c5dd3f62363844a6540..7fad94d8adccb67348effc20a8f7867804b797ab 100644 --- a/src/components/ui/Checkbox.tsx +++ b/src/components/ui/Checkbox.tsx @@ -3,9 +3,9 @@ import { Children } from "../../types/Preact"; import styled, { css } from "styled-components"; const CheckboxBase = styled.label` + margin-top: 20px; gap: 4px; z-index: 1; - padding: 4px; display: flex; border-radius: 4px; align-items: center; @@ -16,25 +16,19 @@ const CheckboxBase = styled.label` transition: 0.2s ease all; - p { - margin: 0; - } - input { display: none; } &:hover { - background: var(--secondary-background); - .check { background: var(--background); } } &[disabled] { - opacity: 0.5; - cursor: unset; + opacity: .5; + cursor: not-allowed; &:hover { background: unset; @@ -43,15 +37,15 @@ const CheckboxBase = styled.label` `; const CheckboxContent = styled.span` - flex-grow: 1; display: flex; + flex-grow: 1; font-size: 1rem; font-weight: 600; flex-direction: column; `; const CheckboxDescription = styled.span` - font-size: 0.8em; + font-size: .75rem; font-weight: 400; color: var(--secondary-foreground); `; diff --git a/src/components/ui/ColourSwatches.tsx b/src/components/ui/ColourSwatches.tsx index cbc13bd284f1d4c12b9b6b58573c061a21cd455a..46a473fc80ca6077b82daf219088d95fad4b49b1 100644 --- a/src/components/ui/ColourSwatches.tsx +++ b/src/components/ui/ColourSwatches.tsx @@ -1,5 +1,6 @@ import { useRef } from "preact/hooks"; -import { Check, Pencil } from "@styled-icons/boxicons-regular"; +import { Check } from "@styled-icons/boxicons-regular"; +import { Palette } from "@styled-icons/boxicons-solid"; import styled, { css } from "styled-components"; interface Props { @@ -98,7 +99,7 @@ export default function ColourSwatches({ value, onChange }: Props) { type="large" onClick={() => ref.current.click()} > - <Pencil size={32} /> + <Palette size={32} /> </Swatch> <input type="color" diff --git a/src/components/ui/ComboBox.tsx b/src/components/ui/ComboBox.tsx index ef9ba9f91c0045cf077ffab655b46077cad7fc26..989f0787b351c5b9e8c632494956de7e14ade81f 100644 --- a/src/components/ui/ComboBox.tsx +++ b/src/components/ui/ComboBox.tsx @@ -2,15 +2,19 @@ import styled from "styled-components"; export default styled.select` padding: 8px; - border-radius: 2px; + border-radius: 6px; + font-family: inherit; color: var(--secondary-foreground); background: var(--secondary-background); - + font-size: .875rem; border: none; outline: 2px solid transparent; transition: outline-color 0.2s ease-in-out; + transition: box-shadow .3s; + cursor: pointer; + width: 100%; &:focus { - outline-color: var(--accent); + box-shadow: 0 0 0 2pt var(--accent); } `; diff --git a/src/components/ui/Details.tsx b/src/components/ui/Details.tsx new file mode 100644 index 0000000000000000000000000000000000000000..f06a67f70a3604faf69a2586d57e35d486d0916c --- /dev/null +++ b/src/components/ui/Details.tsx @@ -0,0 +1,68 @@ +import styled, { css } from "styled-components"; + +export default styled.details<{ sticky?: boolean, large?: boolean }>` + summary { + ${ props => props.sticky && css` + top: -1px; + z-index: 10; + position: sticky; + ` } + + ${ 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; + align-items: center; + transition: .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: .2s ease transform; + } + } + } + + &:not([open]) { + summary { + opacity: .7; + } + + summary svg { + transform: rotateZ(-90deg); + } + } +`; diff --git a/src/components/ui/Header.tsx b/src/components/ui/Header.tsx index 376523970ad399b2ee268c42083e2c4b0b5d710c..d4e0e3f90f605d2857f5c10b2fe1affc81cc3857 100644 --- a/src/components/ui/Header.tsx +++ b/src/components/ui/Header.tsx @@ -46,6 +46,6 @@ export default styled.div<Props>` ` } ${ props => props.borders && css` - border-end-start-radius: 8px; + border-start-start-radius: 8px; ` } `; diff --git a/src/components/ui/InputBox.tsx b/src/components/ui/InputBox.tsx index ac3746468d64539919f3ed1b1f77783bb4c62631..75e3510b9c822161457f4dbfd7cf84019adec716 100644 --- a/src/components/ui/InputBox.tsx +++ b/src/components/ui/InputBox.tsx @@ -9,6 +9,7 @@ export default styled.input<Props>` padding: 8px 16px; border-radius: 6px; + font-family: inherit; color: var(--foreground); background: var(--primary-background); transition: 0.2s ease background-color; diff --git a/src/components/ui/Overline.tsx b/src/components/ui/Overline.tsx index 5a9f9d04ca4af9446550599bcb6de8ebd1b2513f..171590770c6ae480838b913a71f49260a60bea50 100644 --- a/src/components/ui/Overline.tsx +++ b/src/components/ui/Overline.tsx @@ -5,6 +5,7 @@ import { Text } from 'preact-i18n'; type Props = Omit<JSX.HTMLAttributes<HTMLDivElement>, 'children' | 'as'> & { error?: string; block?: boolean; + spaced?: boolean; children?: Children; type?: "default" | "subtle" | "error"; } @@ -12,7 +13,10 @@ type Props = Omit<JSX.HTMLAttributes<HTMLDivElement>, 'children' | 'as'> & { const OverlineBase = styled.div<Omit<Props, "children" | "error">>` display: inline; margin: 0.4em 0; - margin-top: 0.8em; + + ${ props => props.spaced && css` + margin-top: 0.8em; + ` } font-size: 14px; font-weight: 600; diff --git a/src/components/ui/TextArea.tsx b/src/components/ui/TextArea.tsx index d61e43c79eddab2b543f864169fd6fcea0206a68..88f5959dff523b78a3f84c8086e0c10db0dc0e9e 100644 --- a/src/components/ui/TextArea.tsx +++ b/src/components/ui/TextArea.tsx @@ -39,8 +39,10 @@ export default styled.textarea<TextAreaProps>` } ${ props => props.code ? css` - font-family: 'Fira Mono', 'Courier New', Courier, monospace; + font-family: var(--monoscape-font-font), monospace; ` : css` - font-family: 'Open Sans', sans-serif; + font-family: inherit; ` } + + font-variant-ligatures: var(--ligatures); `; diff --git a/src/components/ui/Tip.tsx b/src/components/ui/Tip.tsx index 489572b915f688b63a9101c50e8630b12a8d4ed6..f664a58cac3965f6632a73a437ab295df0ef13f3 100644 --- a/src/components/ui/Tip.tsx +++ b/src/components/ui/Tip.tsx @@ -7,6 +7,13 @@ interface Props { error?: boolean } +export const Separator = styled.div<Props>` + height: 1px; + width: calc(100% - 10px); + background: var(--secondary-header); + margin: 18px auto; +`; + export const TipBase = styled.div<Props>` display: flex; padding: 12px; @@ -46,9 +53,13 @@ export const TipBase = styled.div<Props>` export default function Tip(props: Props & { children: Children }) { const { children, ...tipProps } = props; return ( - <TipBase {...tipProps}> - <InfoCircle size={20} /> - <span>{props.children}</span> - </TipBase> + <> + <Separator /> + <TipBase {...tipProps}> + <InfoCircle size={20} /> + <span>{props.children}</span> + </TipBase> + </> + ); } diff --git a/src/context/Locale.tsx b/src/context/Locale.tsx index af894b40affb3cf2a7a5b4b92aede719943a7b5d..6fdff9e216a3173b2e8e6eedac9b9744d9d70d6e 100644 --- a/src/context/Locale.tsx +++ b/src/context/Locale.tsx @@ -43,7 +43,6 @@ export enum Language { PIRATE = "pr", BOTTOM = "bottom", PIGLATIN = "piglatin", - HARDCORE = "hardcore", } export interface LanguageEntry { @@ -107,13 +106,6 @@ export const Languages: { [key in Language]: LanguageEntry } = { dayjs: "en-gb", alt: true }, - hardcore: { - display: "Hardcore Mode", - emoji: "🔥", - i18n: "hardcore", - dayjs: "en-gb", - alt: true - }, }; interface Props { @@ -126,8 +118,10 @@ function Locale({ children, locale }: Props) { const [defns, setDefinition] = useState<Record<string, unknown>>(definition); const lang = Languages[locale]; - // TOOD: clean this up and use the built in Intl API - function transformLanguage(obj: { [key: string]: any }) { + // TODO: clean this up and use the built in Intl API + function transformLanguage(source: { [key: string]: any }) { + const obj = defaultsDeep(source, definition); + const dayjs = obj.dayjs; const defaults = dayjs.defaults; @@ -151,23 +145,16 @@ function Locale({ children, locale }: Props) { useEffect(() => { if (locale === "en") { - transformLanguage(definition); - setDefinition(definition); + const defn = transformLanguage(definition); + setDefinition(defn); dayjs.locale("en"); - dayjs.updateLocale('en', { calendar: definition.dayjs }); - return; - } - - if (lang.i18n === "hardcore") { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - setDefinition({}); + dayjs.updateLocale('en', { calendar: defn.dayjs }); return; } import(`../../external/lang/${lang.i18n}.json`).then( async (lang_file) => { - const defn = lang_file.default; - transformLanguage(defn); + const defn = transformLanguage(lang_file.default); const target = lang.dayjs ?? lang.i18n; const dayjs_locale = await import(`../../node_modules/dayjs/esm/locale/${target}.js`); @@ -176,7 +163,7 @@ function Locale({ children, locale }: Props) { } dayjs.locale(dayjs_locale.default); - setDefinition(defaultsDeep(defn, definition)); + setDefinition(defn); } ); }, [locale, lang]); diff --git a/src/context/Theme.tsx b/src/context/Theme.tsx index b6131c6f64c15227ac87ba676ff887dc9672c805..02f5ca6611c4673fcb84f6a57d26eb8cad99f211 100644 --- a/src/context/Theme.tsx +++ b/src/context/Theme.tsx @@ -33,18 +33,161 @@ export type Variables = | "status-invisible" | "sidebar-active"; +export type Fonts = 'Open Sans' | 'Inter' | 'Atkinson Hyperlegible' | 'Roboto' | 'Noto Sans' | 'Lato' | 'Bree Serif' | 'Montserrat' | 'Poppins' | 'Raleway' | 'Ubuntu' | 'Comic Neue'; +export type MonoscapeFonts = 'Fira Code' | 'Roboto Mono' | 'Source Code Pro' | 'Space Mono' | 'Ubuntu Mono'; + export type Theme = { [variable in Variables]: string; } & { light?: boolean; + font?: Fonts; css?: string; + monoscapeFont?: MonoscapeFonts; }; export interface ThemeOptions { preset?: string; + ligatures?: boolean; custom?: Partial<Theme>; } +// import aaa from "@fontsource/open-sans/300.css?raw"; +// console.info(aaa); + +export const FONTS: Record<Fonts, { name: string, load: () => void }> = { + "Open Sans": { + name: "Open Sans", + load: async () => { + await import("@fontsource/open-sans/300.css"); + await import("@fontsource/open-sans/400.css"); + await import("@fontsource/open-sans/600.css"); + await import("@fontsource/open-sans/700.css"); + await import("@fontsource/open-sans/400-italic.css"); + } + }, + Inter: { + name: "Inter", + load: async () => { + await import("@fontsource/inter/300.css"); + await import("@fontsource/inter/400.css"); + await import("@fontsource/inter/600.css"); + await import("@fontsource/inter/700.css"); + } + }, + "Atkinson Hyperlegible": { + name: "Atkinson Hyperlegible", + load: async () => { + await import("@fontsource/atkinson-hyperlegible/400.css"); + await import("@fontsource/atkinson-hyperlegible/700.css"); + await import("@fontsource/atkinson-hyperlegible/400-italic.css"); + } + }, + "Roboto": { + name: "Roboto", + load: async () => { + await import("@fontsource/roboto/400.css"); + await import("@fontsource/roboto/700.css"); + await import("@fontsource/roboto/400-italic.css"); + } + }, + "Noto Sans": { + name: "Noto Sans", + load: async () => { + await import("@fontsource/noto-sans/400.css"); + await import("@fontsource/noto-sans/700.css"); + await import("@fontsource/noto-sans/400-italic.css"); + } + }, + "Bree Serif": { + name: "Bree Serif", + load: () => import("@fontsource/bree-serif/400.css") + }, + "Lato": { + name: "Lato", + load: async () => { + await import("@fontsource/lato/300.css"); + await import("@fontsource/lato/400.css"); + await import("@fontsource/lato/700.css"); + await import("@fontsource/lato/400-italic.css"); + } + }, + "Montserrat": { + name: "Montserrat", + load: async () => { + await import("@fontsource/montserrat/300.css"); + await import("@fontsource/montserrat/400.css"); + await import("@fontsource/montserrat/600.css"); + await import("@fontsource/montserrat/700.css"); + await import("@fontsource/montserrat/400-italic.css"); + } + }, + "Poppins": { + name: "Poppins", + load: async () => { + await import("@fontsource/poppins/300.css"); + await import("@fontsource/poppins/400.css"); + await import("@fontsource/poppins/600.css"); + await import("@fontsource/poppins/700.css"); + await import("@fontsource/poppins/400-italic.css"); + } + }, + "Raleway": { + name: "Raleway", + load: async () => { + await import("@fontsource/raleway/300.css"); + await import("@fontsource/raleway/400.css"); + await import("@fontsource/raleway/600.css"); + await import("@fontsource/raleway/700.css"); + await import("@fontsource/raleway/400-italic.css"); + } + }, + "Ubuntu": { + name: "Ubuntu", + load: async () => { + await import("@fontsource/ubuntu/300.css"); + await import("@fontsource/ubuntu/400.css"); + await import("@fontsource/ubuntu/500.css"); + await import("@fontsource/ubuntu/700.css"); + await import("@fontsource/ubuntu/400-italic.css"); + } + }, + "Comic Neue": { + name: "Comic Neue", + load: async () => { + await import("@fontsource/comic-neue/300.css"); + await import("@fontsource/comic-neue/400.css"); + await import("@fontsource/comic-neue/700.css"); + await import("@fontsource/comic-neue/400-italic.css"); + } + } +}; + +export const MONOSCAPE_FONTS: Record<MonoscapeFonts, { name: string, load: () => void }> = { + "Fira Code": { + name: "Fira Code", + load: () => import("@fontsource/fira-code/400.css") + }, + "Roboto Mono": { + name: "Roboto Mono", + load: () => import("@fontsource/roboto-mono/400.css") + }, + "Source Code Pro": { + name: "Source Code Pro", + load: () => import("@fontsource/source-code-pro/400.css") + }, + "Space Mono": { + name: "Space Mono", + load: () => import("@fontsource/space-mono/400.css") + }, + "Ubuntu Mono": { + name: "Ubuntu Mono", + load: () => import("@fontsource/ubuntu-mono/400.css") + } +}; + +export const FONT_KEYS = Object.keys(FONTS).sort(); +export const MONOSCAPE_FONT_KEYS = Object.keys(MONOSCAPE_FONTS).sort(); + // Generated from https://gitlab.insrt.uk/revolt/community/themes export const PRESETS: Record<string, Theme> = { light: { @@ -124,15 +267,32 @@ interface Props { options?: ThemeOptions; } -function Theme(props: Props) { +function Theme({ children, options }: Props) { const theme: Theme = { ...PRESETS["dark"], ...PRESETS[props.options?.preset ?? ''], ...props.options?.custom }; + const root = document.documentElement.style; + useEffect(() => { + const font = theme.font ?? 'Inter'; + root.setProperty('--font', `"${font}"`); + FONTS[font].load(); + }, [ theme.font ]); + + useEffect(() => { + const font = theme.monoscapeFont ?? 'Fira Code'; + root.setProperty('--monoscape-font', `"${font}"`); + MONOSCAPE_FONTS[font].load(); + }, [ theme.monoscapeFont ]); + + useEffect(() => { + root.setProperty('--ligatures', options?.ligatures ? 'normal' : 'none'); + }, [ options?.ligatures ]); + useEffect(() => { - const resize = () => document.documentElement.style.setProperty('--app-height', `${window.innerHeight}px`); + const resize = () => root.setProperty('--app-height', `${window.innerHeight}px`); resize(); window.addEventListener('resize', resize); @@ -155,7 +315,7 @@ function Theme(props: Props) { {theme.css && ( <style dangerouslySetInnerHTML={{ __html: theme.css }} /> )} - {props.children} + { children } </ThemeContext.Provider> ); } diff --git a/src/context/intermediate/modals/Prompt.module.scss b/src/context/intermediate/modals/Prompt.module.scss index a107fcf6aff98f5e39dc739199459064d7b36ee6..a228b4ccaa784449d998faa44c42a1cbe3fb293e 100644 --- a/src/context/intermediate/modals/Prompt.module.scss +++ b/src/context/intermediate/modals/Prompt.module.scss @@ -7,7 +7,7 @@ user-select: all; font-size: 1.4em; text-align: center; - font-family: "Fira Mono"; + font-family: var(--monoscape-font); } } diff --git a/src/context/revoltjs/FileUploads.tsx b/src/context/revoltjs/FileUploads.tsx index 95f50e8cab728d1ddf77500291daf402dfc61231..87781e2f2ef9cb78b2c59dc995010a693446db34 100644 --- a/src/context/revoltjs/FileUploads.tsx +++ b/src/context/revoltjs/FileUploads.tsx @@ -8,7 +8,8 @@ import { useContext, useEffect, useState } from "preact/hooks"; import Preloader from "../../components/ui/Preloader"; import { determineFileSize } from "../../lib/fileSize"; import IconButton from '../../components/ui/IconButton'; -import { Edit, Plus, X, XCircle } from "@styled-icons/boxicons-regular"; +import { Plus, X, XCircle } from "@styled-icons/boxicons-regular"; +import { Pencil } from "@styled-icons/boxicons-solid"; import { useIntermediate } from "../intermediate/Intermediate"; type Props = { @@ -190,7 +191,7 @@ export function FileUploader(props: Props) { <Preloader type="ring" /> </div> : <div className={styles.edit}> - <Edit size={30} /> + <Pencil size={30} /> </div> } </div> <div className={styles.modify}> diff --git a/src/lib/TextAreaAutoSize.tsx b/src/lib/TextAreaAutoSize.tsx index bfc515b071a8dd63f5f3f3709f4c74e0b431368a..c602655489632aa84eee5c96aff38d16dbccb220 100644 --- a/src/lib/TextAreaAutoSize.tsx +++ b/src/lib/TextAreaAutoSize.tsx @@ -23,6 +23,7 @@ export default function TextAreaAutoSize(props: TextAreaAutoSizeProps) { const ref = useRef<HTMLTextAreaElement>(); useEffect(() => { + if (isTouchscreenDevice) return; autoFocus && ref.current.focus(); }, [value]); diff --git a/src/pages/developer/Developer.tsx b/src/pages/developer/Developer.tsx index 1a392bc0119055c4afd45a451ab4ff725d93c889..e8c3e187da029c0aeff7ea6dfad48cf1d5cccc8c 100644 --- a/src/pages/developer/Developer.tsx +++ b/src/pages/developer/Developer.tsx @@ -4,6 +4,7 @@ import Header from "../../components/ui/Header"; import PaintCounter from "../../lib/PaintCounter"; import { AppContext } from "../../context/revoltjs/RevoltClient"; import { useUserPermission } from "../../context/revoltjs/hooks"; +import { Wrench } from "@styled-icons/boxicons-solid"; export default function Developer() { // const voice = useContext(VoiceContext); @@ -12,7 +13,10 @@ export default function Developer() { return ( <div> - <Header placement="primary">Developer Tab</Header> + <Header placement="primary"> + <Wrench size="24" /> + Developer Tab + </Header> <div style={{ padding: "16px" }}> <PaintCounter always /> </div> diff --git a/src/pages/friends/Friend.module.scss b/src/pages/friends/Friend.module.scss index af867b28721f6907074ed08681c09a4df44e6bed..ee9c6f90ef010b31c2b21ecfcdab3ee17473ad95 100644 --- a/src/pages/friends/Friend.module.scss +++ b/src/pages/friends/Friend.module.scss @@ -14,57 +14,6 @@ padding: 0 10px 10px 10px; user-select: none; overflow-y: scroll; - - summary { - position: sticky; - z-index: 10; - top: -1px; - } - - .overline { - display: flex; - align-items: center; - background: var(--primary-background); - padding: 5px 0; - cursor: pointer; - - .title { - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - } - - svg { - margin-inline-end: 4px; - } - } - - details { - summary { - outline: none; - list-style: none; - transition: .2s opacity; - - &::marker, &::-webkit-details-marker { - display: none; - } - - svg { - flex-shrink: 0; - transition: .2s ease transform; - } - } - - &:not([open]) { - summary { - opacity: .7; - } - - summary svg { - transform: rotateZ(-90deg); - } - } - } &[data-empty="true"] { img { @@ -145,7 +94,6 @@ .divider { width: 1px; height: 24px; - margin: 0 8px; background: var(--primary-background); } diff --git a/src/pages/friends/Friends.tsx b/src/pages/friends/Friends.tsx index 7ccf4761974ea253b10efd86f61e4568703f2eaf..d90b5d58781dbe7b70a539bf8c4e1b79b5392991 100644 --- a/src/pages/friends/Friends.tsx +++ b/src/pages/friends/Friends.tsx @@ -14,6 +14,8 @@ import { ChevronDown, ChevronRight, ListPlus } from "@styled-icons/boxicons-regu import { UserDetail, MessageAdd, UserPlus } from "@styled-icons/boxicons-solid"; import { TextReact } from "../../lib/i18n"; import { Children } from "../../types/Preact"; +import Details from "../../components/ui/Details"; +import CollapsibleSection from "../../components/common/CollapsibleSection"; export default function Friends() { const { openScreen } = useIntermediate(); @@ -29,17 +31,17 @@ export default function Friends() { ) ], [ 'app.special.friends.sent', users.filter(x => x.relationship === Users.Relationship.Outgoing - ) ], + ), 'outgoing' ], [ 'app.status.online', friends.filter(x => x.online && x.status?.presence !== Users.Presence.Invisible - ) ], + ), 'online' ], [ 'app.status.offline', friends.filter(x => !x.online || x.status?.presence === Users.Presence.Invisible - ) ], - [ 'app.special.friends.blocked', friends.filter(x => + ), 'offline' ], + [ 'app.special.friends.blocked', users.filter(x => x.relationship === Users.Relationship.Blocked - ) ] - ] as [ string, User[] ][]; + ), 'blocked' ] + ] as [ string, User[], string ][]; const incoming = lists[0][1]; const userlist: Children[] = incoming.map(x => <b>{ x.username }</b>); @@ -56,7 +58,7 @@ export default function Friends() { <div className={styles.actions}> {/*<Tooltip content={"Create Category"} placement="bottom"> <IconButton onClick={() => openScreen({ id: 'special_input', type: 'create_group' })}> - <ListPlus size={24} /> + <ListPlus size={28} /> </IconButton> </Tooltip> <div className={styles.divider} />*/} @@ -107,22 +109,18 @@ export default function Friends() { </div> } { - lists.map(([i18n, list], index) => { + lists.map(([i18n, list, section_id], index) => { if (index === 0) return; if (list.length === 0) return; return ( - <details open> - <summary> - <Overline className={styles.overline} type="subtle"> - <ChevronDown size={20} /> - <div className={styles.title}> - <Text id={i18n} /> — { list.length } - </div> - </Overline> - </summary> + <CollapsibleSection + id={`friends_${section_id}`} + defaultValue={true} + sticky large + summary={<div class="title"><Text id={i18n} /> — { list.length }</div>}> { list.map(x => <Friend key={x._id} user={x} />) } - </details> + </CollapsibleSection> ) }) } diff --git a/src/pages/settings/Settings.module.scss b/src/pages/settings/Settings.module.scss index 82f5ba03420d08ab8afa900e622f7b389b91d6e1..e8648eb9cc7d26f3815e256f721dd8bdb7293325 100644 --- a/src/pages/settings/Settings.module.scss +++ b/src/pages/settings/Settings.module.scss @@ -93,7 +93,7 @@ margin: 1rem 12px 0; font-size: 10px; color: var(--secondary-foreground); - font-family: "Fira Mono", monospace; + font-family: var(--monoscape-font), monospace; user-select: text; display: grid; diff --git a/src/pages/settings/Settings.tsx b/src/pages/settings/Settings.tsx index 0141409dba7720b57c74b3d8f950caa9e87b9e04..4fa2b5827333b1fb9f186fe5176263182f3a0e92 100644 --- a/src/pages/settings/Settings.tsx +++ b/src/pages/settings/Settings.tsx @@ -10,22 +10,19 @@ import { Bell, Palette, Coffee, - Globe, IdCard, - LogOut, - Sync as SyncIcon, - Shield, - Vial, - User -} from "@styled-icons/boxicons-regular"; -import { Brush, Megaphone } from "@styled-icons/boxicons-solid"; + CheckShield, + Flask, + User, + Megaphone +} from "@styled-icons/boxicons-solid"; +import { Sync as SyncIcon, Globe, LogOut } from "@styled-icons/boxicons-regular"; import { Gitlab } from "@styled-icons/boxicons-logos"; import { GIT_BRANCH, GIT_REVISION, REPO_URL } from "../../revision"; import LineDivider from "../../components/ui/LineDivider"; import RequiresOnline from "../../context/revoltjs/RequiresOnline"; import ButtonItem from "../../components/navigation/items/ButtonItem"; import { AppContext, OperationsContext } from "../../context/revoltjs/RevoltClient"; - import { Account } from "./panes/Account"; import { Profile } from "./panes/Profile"; import { Sessions } from "./panes/Sessions"; @@ -64,7 +61,7 @@ export default function Settings() { }, { id: 'sessions', - icon: <Shield size={20} />, + icon: <CheckShield size={20} />, title: <Text id="app.settings.pages.sessions.title" /> }, { @@ -91,7 +88,7 @@ export default function Settings() { { divider: true, id: 'experiments', - icon: <Vial size={20} />, + icon: <Flask size={20} />, title: <Text id="app.settings.pages.experiments.title" /> }, { diff --git a/src/pages/settings/panes/Account.tsx b/src/pages/settings/panes/Account.tsx index 7fd102147ee17d67e46fa69fcc1e825bf81b7423..88a7839c70b7e53a71451de484fb3a5624ed129d 100644 --- a/src/pages/settings/panes/Account.tsx +++ b/src/pages/settings/panes/Account.tsx @@ -5,7 +5,8 @@ import Button from "../../../components/ui/Button"; import { Users } from "revolt.js/dist/api/objects"; import { Link, useHistory } from "react-router-dom"; import Overline from "../../../components/ui/Overline"; -import { At, Key, Envelope } from "@styled-icons/boxicons-regular"; +import { Envelope, Key } from "@styled-icons/boxicons-solid"; +import { At } from "@styled-icons/boxicons-regular"; import { useContext, useEffect, useState } from "preact/hooks"; import UserIcon from "../../../components/common/user/UserIcon"; import { useForceUpdate, useSelf } from "../../../context/revoltjs/hooks"; @@ -47,9 +48,7 @@ export function Account() { return ( <div className={styles.user}> <div className={styles.banner}> - <Link to="/settings/profile"> - <UserIcon target={user} size={72} /> - </Link> + <UserIcon className={styles.avatar} target={user} size={72} onClick={() => switchPage("profile")}/> <div className={styles.username}>@{user.username}</div> </div> <div className={styles.details}> diff --git a/src/pages/settings/panes/Appearance.tsx b/src/pages/settings/panes/Appearance.tsx index fdf209d1da94c23a64b3c8fc982946f5c86401b9..6683021338d9c29d0a3900db568e1ab3ad2e5bb8 100644 --- a/src/pages/settings/panes/Appearance.tsx +++ b/src/pages/settings/panes/Appearance.tsx @@ -2,15 +2,18 @@ import { Text } from "preact-i18n"; import styles from "./Panes.module.scss"; import { debounce } from "../../../lib/debounce"; import Button from "../../../components/ui/Button"; +import Checkbox from "../../../components/ui/Checkbox"; +import ComboBox from "../../../components/ui/ComboBox"; import InputBox from "../../../components/ui/InputBox"; import { connectState } from "../../../redux/connector"; import { WithDispatcher } from "../../../redux/reducers"; import TextAreaAutoSize from "../../../lib/TextAreaAutoSize"; import ColourSwatches from "../../../components/ui/ColourSwatches"; import { EmojiPacks, Settings } from "../../../redux/reducers/settings"; -import { Theme, ThemeContext, ThemeOptions } from "../../../context/Theme"; import { useCallback, useContext, useEffect, useState } from "preact/hooks"; import { useIntermediate } from "../../../context/intermediate/Intermediate"; +import CollapsibleSection from "../../../components/common/CollapsibleSection"; +import { FONTS, FONT_KEYS, MONOSCAPE_FONTS, MONOSCAPE_FONT_KEYS, Theme, ThemeContext, ThemeOptions } from "../../../context/Theme"; // @ts-ignore import pSBC from 'shade-blend-color'; @@ -129,6 +132,25 @@ export function Component(props: Props & WithDispatcher) { </Radio> </div>*/} + <h3> + <Text id="app.settings.pages.appearance.font" /> + </h3> + <ComboBox value={theme.font} onChange={e => setTheme({ custom: { font: e.currentTarget.value as any } })}> + { + FONT_KEYS + .map(key => + <option value={key}>{ FONTS[key as keyof typeof FONTS].name }</option> + ) + } + </ComboBox> + <p> + <Checkbox checked={props.settings.theme?.ligatures === true} + onChange={() => setTheme({ ligatures: !props.settings.theme?.ligatures })} + description={<Text id="app.settings.pages.appearance.ligatures_desc" />}> + <Text id="app.settings.pages.appearance.ligatures" /> + </Checkbox> + </p> + <h3> <Text id="app.settings.pages.appearance.emoji_pack" /> </h3> @@ -171,11 +193,7 @@ export function Component(props: Props & WithDispatcher) { </div> </div> - <details> - <summary> - <Text id="app.settings.pages.appearance.advanced" /> - <div className={styles.divider}></div> - </summary> + <CollapsibleSection id="settings_advanced_appearance" defaultValue={false} summary={<Text id="app.settings.pages.appearance.advanced" />}> <h3> <Text id="app.settings.pages.appearance.overrides" /> </h3> @@ -263,6 +281,19 @@ export function Component(props: Props & WithDispatcher) { </div> ))} </div> + + <h3> + <Text id="app.settings.pages.appearance.mono_font" /> + </h3> + <ComboBox value={theme.monoscapeFont} onChange={e => setTheme({ custom: { monoscapeFont: e.currentTarget.value as any } })}> + { + MONOSCAPE_FONT_KEYS + .map(key => + <option value={key}>{ MONOSCAPE_FONTS[key as keyof typeof MONOSCAPE_FONTS].name }</option> + ) + } + </ComboBox> + <h3> <Text id="app.settings.pages.appearance.custom_css" /> </h3> @@ -272,7 +303,7 @@ export function Component(props: Props & WithDispatcher) { code value={css} onChange={ev => setCSS(ev.currentTarget.value)} /> - </details> + </CollapsibleSection> </div> ); } diff --git a/src/pages/settings/panes/Notifications.tsx b/src/pages/settings/panes/Notifications.tsx index 6122d28453729beb1c9e998386828eb2958b9575..eccd0cead1fef22f4779d8a945aa2817d52bae6f 100644 --- a/src/pages/settings/panes/Notifications.tsx +++ b/src/pages/settings/panes/Notifications.tsx @@ -39,6 +39,7 @@ export function Component({ options, dispatcher }: Props & WithDispatcher) { <Checkbox disabled={!("Notification" in window)} checked={options?.desktopEnabled ?? false} + description={<Text id="app.settings.pages.notifications.descriptions.enable_desktop" />} onChange={async desktopEnabled => { if (desktopEnabled) { let permission = await Notification.requestPermission(); @@ -57,13 +58,11 @@ export function Component({ options, dispatcher }: Props & WithDispatcher) { }} > <Text id="app.settings.pages.notifications.enable_desktop" /> - <p> - <Text id="app.settings.pages.notifications.descriptions.enable_desktop" /> - </p> </Checkbox> <Checkbox disabled={typeof pushEnabled === "undefined"} checked={pushEnabled ?? false} + description={<Text id="app.settings.pages.notifications.descriptions.enable_push" />} onChange={async pushEnabled => { try { const reg = await navigator.serviceWorker?.getRegistration(); @@ -99,9 +98,6 @@ export function Component({ options, dispatcher }: Props & WithDispatcher) { }} > <Text id="app.settings.pages.notifications.enable_push" /> - <p> - <Text id="app.settings.pages.notifications.descriptions.enable_push" /> - </p> </Checkbox> <h3> <Text id="app.settings.pages.notifications.sounds" /> diff --git a/src/pages/settings/panes/Panes.module.scss b/src/pages/settings/panes/Panes.module.scss index 4695af59e7d12ad07838bd4cb25e25811cbb3d49..206810205c814fa4da06b0474ae654d43e9255e1 100644 --- a/src/pages/settings/panes/Panes.module.scss +++ b/src/pages/settings/panes/Panes.module.scss @@ -8,16 +8,18 @@ align-items: center; background: var(--secondary-header); - .username { - font-size: 24px; - } - - a { + .avatar { + cursor: pointer; transition: 0.2s ease filter; + + &:hover { + filter: brightness(80%); + } } - a:hover { - filter: brightness(80%); + .username { + font-size: 24px; + font-weight: 600; } } @@ -32,6 +34,10 @@ display: flex; align-items: center; flex-direction: row; + + > svg { + flex-shrink: 0; + } } .detail { @@ -321,30 +327,16 @@ } } -.notifications { - label { - margin-top: 12px; - } - - p { - margin-top: 0; - font-size: 0.9em; - color: var(--secondary-foreground); - } -} - .languages { .list { margin-bottom: 1em; .entry { - padding: 2px 8px; height: 50px; - border-radius: 4px; } .entry > span > span { - gap: 8px; + gap: 20px; display: flex; align-items: center; flex-direction: row; diff --git a/src/pages/settings/panes/Sync.tsx b/src/pages/settings/panes/Sync.tsx index ad7c84f82ba327c07bcc2845798da099b4e9a647..2055016b2c89dd208841822527e5e992db511fb5 100644 --- a/src/pages/settings/panes/Sync.tsx +++ b/src/pages/settings/panes/Sync.tsx @@ -25,6 +25,7 @@ export function Component(props: Props & WithDispatcher) { ([ key, title ]) => <Checkbox checked={(props.options?.disabled ?? []).indexOf(key) === -1} + description={<Text id={`app.settings.pages.sync.descriptions.${key}`} />} onChange={enabled => { props.dispatcher({ type: enabled ? 'SYNC_ENABLE_KEY' : 'SYNC_DISABLE_KEY', @@ -33,9 +34,6 @@ export function Component(props: Props & WithDispatcher) { }} > <Text id={`app.settings.pages.${title}`} /> - <p> - <Text id={`app.settings.pages.sync.descriptions.${key}`} /> - </p> </Checkbox> ) } diff --git a/src/pages/settings/server/Members.tsx b/src/pages/settings/server/Members.tsx index e7d30a6f0f8ff2eec6931b2d42ee7ed53d0d29ba..8f0c171061e8caf37e54e7ff9ac7335fe824f10b 100644 --- a/src/pages/settings/server/Members.tsx +++ b/src/pages/settings/server/Members.tsx @@ -1,7 +1,6 @@ import styles from './Panes.module.scss'; import { useEffect, useState } from "preact/hooks"; import { Servers } from "revolt.js/dist/api/objects"; -import UserIcon from "../../../components/common/user/UserIcon"; import { useForceUpdate, useUsers } from "../../../context/revoltjs/hooks"; interface Props { @@ -23,7 +22,7 @@ export function Members({ server }: Props) { return ( <div className={styles.members}> <div className={styles.subtitle}> - X Members + { members?.length ?? 0 } Members </div> { members && members.length > 0 && users?.map(x => x && <div className={styles.member}> diff --git a/src/pages/settings/server/Overview.tsx b/src/pages/settings/server/Overview.tsx index fe530276a1736e84f6077ba407b18bacb1254a07..849e0e01517e595f002ae37042e4b787dfd35181 100644 --- a/src/pages/settings/server/Overview.tsx +++ b/src/pages/settings/server/Overview.tsx @@ -1,10 +1,13 @@ import { Text } from "preact-i18n"; +import isEqual from "lodash.isequal"; import styles from './Panes.module.scss'; import Button from "../../../components/ui/Button"; -import { Servers } from "revolt.js/dist/api/objects"; import InputBox from "../../../components/ui/InputBox"; +import ComboBox from "../../../components/ui/ComboBox"; +import { Servers, Server } from "revolt.js/dist/api/objects"; import TextAreaAutoSize from "../../../lib/TextAreaAutoSize"; import { useContext, useEffect, useState } from "preact/hooks"; +import { getChannelName } from "../../../context/revoltjs/util"; import { AppContext } from "../../../context/revoltjs/RevoltClient"; import { FileUploader } from "../../../context/revoltjs/FileUploads"; @@ -17,16 +20,18 @@ export function Overview({ server }: Props) { const [name, setName] = useState(server.name); const [description, setDescription] = useState(server.description ?? ''); + const [systemMessages, setSystemMessages] = useState(server.system_messages); useEffect(() => setName(server.name), [ server.name ]); useEffect(() => setDescription(server.description ?? ''), [ server.description ]); + useEffect(() => setSystemMessages(server.system_messages), [ server.system_messages ]); const [ changed, setChanged ] = useState(false); function save() { - let changes: any = {}; + let changes: Partial<Pick<Servers.Server, 'name' | 'description' | 'system_messages'>> = {}; if (name !== server.name) changes.name = name; - if (description !== server.description) - changes.description = description; + if (description !== server.description) changes.description = description; + if (!isEqual(systemMessages, server.system_messages)) changes.system_messages = systemMessages; client.servers.edit(server._id, changes); setChanged(false); @@ -76,11 +81,6 @@ export function Overview({ server }: Props) { if (!changed) setChanged(true) }} /> - <p> - <Button onClick={save} contrast disabled={!changed}> - <Text id="app.special.modals.actions.save" /> - </Button> - </p> <h3> <Text id="app.main.servers.custom_banner" /> @@ -95,6 +95,48 @@ export function Overview({ server }: Props) { previewURL={client.servers.getBannerURL(server._id, { width: 1000 }, true)} remove={() => client.servers.edit(server._id, { remove: 'Banner' })} /> + + <h3> + <Text id="app.settings.server_pages.overview.system_messages" /> + </h3> + {[ + [ 'User Joined', 'user_joined' ], + [ 'User Left', 'user_left' ], + [ 'User Kicked', 'user_kicked' ], + [ 'User Banned', 'user_banned' ] + ].map(([ i18n, key ]) => + // ! FIXME: temporary code just so we can expose the options + <p style={{ display: 'flex', gap: '8px', alignItems: 'center' }}> + <span style={{ flexShrink: '0', flex: `25%` }}>{i18n}</span> + <ComboBox value={systemMessages?.[key as keyof typeof systemMessages] ?? 'disabled'} + onChange={e => { + if (!changed) setChanged(true) + const v = e.currentTarget.value; + if (v === 'disabled') { + const { [key as keyof typeof systemMessages]: _, ...other } = systemMessages; + setSystemMessages(other); + } else { + setSystemMessages({ + ...systemMessages, + [key]: v + }); + } + }}> + <option value='disabled'><Text id="general.disabled" /></option> + { server.channels.map(id => { + const channel = client.channels.get(id); + if (!channel) return null; + return <option value={id}>{ getChannelName(client, channel, true) }</option>; + }) } + </ComboBox> + </p> + )} + + <p> + <Button onClick={save} contrast disabled={!changed}> + <Text id="app.special.modals.actions.save" /> + </Button> + </p> </div> ); } diff --git a/src/pages/settings/server/Roles.tsx b/src/pages/settings/server/Roles.tsx index cd827fe3bcba0a809f363216ae184cd0d5920a5f..09bf0921da57e1ce4921c3bf911c180900950737 100644 --- a/src/pages/settings/server/Roles.tsx +++ b/src/pages/settings/server/Roles.tsx @@ -49,7 +49,7 @@ export function Roles({ server }: Props) { <div className={styles.list}> <div className={styles.title}> <h1><Text id="app.settings.server_pages.roles.title" /></h1> - <Plus size={16} onClick={() => + <Plus size={22} onClick={() => openScreen({ id: 'special_input', type: 'create_role', server: server._id, callback: id => setRole(id) })} /> </div> { [ 'default', ...Object.keys(roles) ] diff --git a/src/redux/index.ts b/src/redux/index.ts index dc90bec03a461bd90ec4a8730cc41942684a02c6..4bee0dcd667006f406e247a19cb1e8d3c7f1ab35 100644 --- a/src/redux/index.ts +++ b/src/redux/index.ts @@ -14,6 +14,7 @@ import { QueuedMessage } from "./reducers/queue"; import { ExperimentOptions } from "./reducers/experiments"; import { LastOpened } from "./reducers/last_opened"; import { Notifications } from "./reducers/notifications"; +import { SectionToggle } from "./reducers/section_toggle"; export type State = { config: Core.RevoltNodeConfiguration, @@ -28,6 +29,7 @@ export type State = { experiments: ExperimentOptions; lastOpened: LastOpened; notifications: Notifications; + sectionToggle: SectionToggle; }; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -56,7 +58,8 @@ store.subscribe(() => { sync, experiments, lastOpened, - notifications + notifications, + sectionToggle } = store.getState() as State; localForage.setItem("state", { @@ -70,6 +73,7 @@ store.subscribe(() => { sync, experiments, lastOpened, - notifications + notifications, + sectionToggle }); }); diff --git a/src/redux/reducers/index.ts b/src/redux/reducers/index.ts index fe47ccbdc3de006eed1f954b21004518c5e7516c..baf217dcf1dcc9c2252104e10f25309c5914b785 100644 --- a/src/redux/reducers/index.ts +++ b/src/redux/reducers/index.ts @@ -13,6 +13,7 @@ import { sync, SyncAction } from "./sync"; import { experiments, ExperimentsAction } from "./experiments"; import { lastOpened, LastOpenedAction } from "./last_opened"; import { notifications, NotificationsAction } from "./notifications"; +import { sectionToggle, SectionToggleAction } from "./section_toggle"; export default combineReducers({ config, @@ -26,7 +27,8 @@ export default combineReducers({ sync, experiments, lastOpened, - notifications + notifications, + sectionToggle }); export type Action = @@ -42,6 +44,7 @@ export type Action = | ExperimentsAction | LastOpenedAction | NotificationsAction + | SectionToggleAction | { type: "__INIT"; state: State }; export type WithDispatcher = { dispatcher: (action: Action) => void }; diff --git a/src/redux/reducers/section_toggle.ts b/src/redux/reducers/section_toggle.ts new file mode 100644 index 0000000000000000000000000000000000000000..26b23dcac096f90985f5383bb7c64c18de4dfc49 --- /dev/null +++ b/src/redux/reducers/section_toggle.ts @@ -0,0 +1,37 @@ +export interface SectionToggle { + [key: string]: boolean +} + +export type SectionToggleAction = + | { type: undefined } + | { + type: "SECTION_TOGGLE_SET"; + id: string; + state: boolean; + } + | { + type: "SECTION_TOGGLE_UNSET"; + id: string; + } + | { + type: "RESET"; + }; + +export function sectionToggle(state = {} as SectionToggle, action: SectionToggleAction): SectionToggle { + switch (action.type) { + case "SECTION_TOGGLE_SET": { + return { + ...state, + [action.id]: action.state + } + } + case "SECTION_TOGGLE_UNSET": { + const { [action.id]: _, ...newState } = state; + return newState; + } + case "RESET": + return {}; + default: + return state; + } +} diff --git a/src/redux/reducers/settings.ts b/src/redux/reducers/settings.ts index bd7e8220296936c19d5dbbeaeb05339ed110988d..35f5f737edbae93063ad9a3ef145c4403cfe4120 100644 --- a/src/redux/reducers/settings.ts +++ b/src/redux/reducers/settings.ts @@ -65,7 +65,7 @@ export function settings( return { ...state, theme: { - ...filter(state.theme, ["custom", "preset"]), + ...filter(state.theme, ["custom", "preset", "ligatures"]), ...action.theme, }, }; diff --git a/src/styles/_context-menu.scss b/src/styles/_context-menu.scss index 8f33978b788dc889e13ff43c2448fc3ad83009d5..d5f15ee13cb5b84af8b20c876c65353ddd3632ac 100644 --- a/src/styles/_context-menu.scss +++ b/src/styles/_context-menu.scss @@ -51,7 +51,13 @@ } .status { - font-size: .6rem; + max-width: 132px; + font-size: .625rem; + color: var(--secondary-foreground); + + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; } } diff --git a/src/styles/_fonts.scss b/src/styles/_fonts.scss deleted file mode 100644 index 9552f22f5cd612b4cf4a4158d007c48f67808772..0000000000000000000000000000000000000000 --- a/src/styles/_fonts.scss +++ /dev/null @@ -1,6 +0,0 @@ -@import "@fontsource/open-sans/300.css"; -@import "@fontsource/open-sans/400.css"; -@import "@fontsource/open-sans/600.css"; -@import "@fontsource/open-sans/700.css"; - -@import "@fontsource/open-sans/400-italic.css"; diff --git a/src/styles/_page.scss b/src/styles/_page.scss index 124e530abe5ee01968fe711145f576d8048def8d..26b8f34976ff2f5ea265af3ce2742c0ac960050a 100644 --- a/src/styles/_page.scss +++ b/src/styles/_page.scss @@ -18,7 +18,9 @@ html, body { margin: 0; height: 100%; - font-family: "Open Sans", sans-serif; + font-family: var(--font), sans-serif; + font-variant-ligatures: var(--ligatures); + -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; caret-color: var(--accent); diff --git a/src/styles/_variables.scss b/src/styles/_variables.scss index 14024c1681fa7d10df538442187d4bc0b0798642..6f3e02aed1ed229af8f97d3644bd653a3d262a24 100644 --- a/src/styles/_variables.scss +++ b/src/styles/_variables.scss @@ -1,4 +1,7 @@ :root { + --ligatures: none; + --font: "Open Sans"; --app-height: 100vh; + --codeblock-font: "Fira Code"; --sidebar-active: var(--secondary-background); } diff --git a/src/styles/index.scss b/src/styles/index.scss index 4fb88f540a4e863ab855213ec8a8ef44d780f9b3..95c95f1ea77bb63fb8ea74443210cc5cb407a3e3 100644 --- a/src/styles/index.scss +++ b/src/styles/index.scss @@ -1,7 +1,6 @@ @import "variables"; @import "context-menu"; @import "elements"; -@import "fonts"; @import "page"; @import "react-overlapping-panels/dist"; diff --git a/yarn.lock b/yarn.lock index 292ec6d867db8b148850660cb26625e625efe509..c3f2ec5cafca71498fc892def353f04185b20b9d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -912,16 +912,91 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" -"@fontsource/fira-mono@^4.4.5": +"@fontsource/atkinson-hyperlegible@^4.4.5": version "4.4.5" - resolved "https://registry.yarnpkg.com/@fontsource/fira-mono/-/fira-mono-4.4.5.tgz#ceac70967cd3c4262195603aba567cd4582493f8" - integrity sha512-LWbsPhTr1JRV3zUgvMrOxQDn1BG9F4R0FPeBkqWP8/oqPxvVYAhEepg1DN9M1k6L9sRN2I2HWHBpt4QVbDGXpw== + resolved "https://registry.yarnpkg.com/@fontsource/atkinson-hyperlegible/-/atkinson-hyperlegible-4.4.5.tgz#4f2ecf34491d5798ec2a346d7de403d99ca82d39" + integrity sha512-HmhcEIWucTCfvl4qB/ucGIGjp7zMyOfGgfeY+n84Qz7x4sFosOpQlziPFmgO1MHiU1Quw9VGGj2t+zNU5EtBFQ== + +"@fontsource/bree-serif@^4.4.5": + version "4.4.5" + resolved "https://registry.yarnpkg.com/@fontsource/bree-serif/-/bree-serif-4.4.5.tgz#ac5dc36b9b96bcae17820d66eee7d9f64647fc76" + integrity sha512-/a8Ef8O5TTWcKSQKFs7kc/oVZddSFEunxgWJwt9f8q9Z9qSYNeUrBft0q8SGA+V6c4m0svaZSWICqBTg/aeDcQ== + +"@fontsource/comic-neue@^4.4.5": + version "4.4.5" + resolved "https://registry.yarnpkg.com/@fontsource/comic-neue/-/comic-neue-4.4.5.tgz#00cb5546ca2a9fe9cc2e0fa73349e9202c7b28d5" + integrity sha512-0oTyJMmWxiJaf1ZTyyK10RRHBqhYgatAqE0SJPCdithDTzETkHWINEi51lkd95evo418pqmjmjNHGYBgO5CN9w== + +"@fontsource/fira-code@^4.4.5": + version "4.4.5" + resolved "https://registry.yarnpkg.com/@fontsource/fira-code/-/fira-code-4.4.5.tgz#380c964676b1e873b192862ce03a7ad1c7838516" + integrity sha512-ap1UKABzbPrUTgIB376n8CSJ7mbJYxGm52A60BHnGHjU2cSDBqNUAColdERgFWgLHfybWXQT13ZrNhAdynf9rg== + +"@fontsource/inter@^4.4.5": + version "4.4.5" + resolved "https://registry.yarnpkg.com/@fontsource/inter/-/inter-4.4.5.tgz#bce26ff9dd83898bd610924cff1342cc30d0e89a" + integrity sha512-eRUmB54CbyC3MhAuw2VvTPEc3wy8sFi0/1mVHaC7ZXglXcrSWXY+p8Xad/Zy8AcmS9BuqCCEH2qJbOt9XdX65Q== + +"@fontsource/lato@^4.4.5": + version "4.4.5" + resolved "https://registry.yarnpkg.com/@fontsource/lato/-/lato-4.4.5.tgz#82c16ef01ab1a1039f74d774e89ef1f76111def6" + integrity sha512-pm9am2tdLnK+21PKCK060oY8R8OKFVzTVvM6pPS+mS7JOZKe3mmDgfB2DGzzyHG/Azj3Pn6onU72BrQsh3XjNw== + +"@fontsource/montserrat@^4.4.5": + version "4.4.5" + resolved "https://registry.yarnpkg.com/@fontsource/montserrat/-/montserrat-4.4.5.tgz#f97ebea605f4a2d6b9b23a19bc9bf8fc6f4c79d5" + integrity sha512-1F3cR9OIp4Qi1AJHFg/SeFeUWLYemMgI3ldetx0mrlHoWeMuK+p2YentYIIZVkV9ogXOIE2QEw7VnHlGBPo7iw== + +"@fontsource/noto-sans@^4.4.5": + version "4.4.5" + resolved "https://registry.yarnpkg.com/@fontsource/noto-sans/-/noto-sans-4.4.5.tgz#0053bf374f8d5222908c825aa1db6a3078a79cf6" + integrity sha512-1P+AMhUvjfpMwuDsLHG1F95ij1SULGM8IiW/4RmdP7+ZHJuvBsSG465Ujps34IDio5bcv6OaLpGyU1YBVTTe+Q== "@fontsource/open-sans@^4.4.5": version "4.4.5" resolved "https://registry.yarnpkg.com/@fontsource/open-sans/-/open-sans-4.4.5.tgz#07b31617e62ed753c94cabcf552ebaed4de497ce" integrity sha512-PDWEvO1/p8OAHHiielvEmwGXHNbZhrZn96ojV7+/mKgFu+cCUcGVJl9sFs97rCWLe3hKQsYLEsJs4EiLjwa+UQ== +"@fontsource/poppins@^4.4.5": + version "4.4.5" + resolved "https://registry.yarnpkg.com/@fontsource/poppins/-/poppins-4.4.5.tgz#c75dcc453f7a9381de3f9b932366c2457887184c" + integrity sha512-pu675yvBGFhAHD8D7uj7C0NaHqDxD7mlgmLM8ziyYL4kYH79fil7rJ6U5X2oaKxoyRk3rxxb9k1fG0CCc7CquA== + +"@fontsource/raleway@^4.4.5": + version "4.4.5" + resolved "https://registry.yarnpkg.com/@fontsource/raleway/-/raleway-4.4.5.tgz#483871d82aa35e6a981b370b44dbae0b9d59ca6c" + integrity sha512-jUOBbqGDadtgjtxQe8YpaiRVy9WGIBUCfQoKlfyuBuI6BNHg1HdsBwL9qetru99uGTTy9L0fEwYoY9J8lhNXqw== + +"@fontsource/roboto-mono@^4.4.5": + version "4.4.5" + resolved "https://registry.yarnpkg.com/@fontsource/roboto-mono/-/roboto-mono-4.4.5.tgz#63c54719a8d592d793632501a56d570572c17065" + integrity sha512-y9fbKH1eCCkcqtRGhevFlmR5schCRz1GiTT/VYz6z8Ij0WHW0tS26BhMyXhmSgEIiFt+254yS8teqP+cc7Xq0w== + +"@fontsource/roboto@^4.4.5": + version "4.4.5" + resolved "https://registry.yarnpkg.com/@fontsource/roboto/-/roboto-4.4.5.tgz#9ef1b9288ac4a97b97906da920c63c08dba97c89" + integrity sha512-e3s7BF8MDBLpkA2r6lnl5PMnllF0McVvpolK9h2zzvVJw2WPexP1GTgMKHISlglYZRij2lKg/ZjQcIUUYDsAXg== + +"@fontsource/source-code-pro@^4.4.5": + version "4.4.5" + resolved "https://registry.yarnpkg.com/@fontsource/source-code-pro/-/source-code-pro-4.4.5.tgz#927ac48da9a7de6054b44b2e1979abd908f00f47" + integrity sha512-PuoF6JHR+s3IaceWMZB02fHLIOehIAfYQUGHXBEyuPQ+BdonbbYpS+RIyduKsUYt7ajZURysbBFlg/N0cb/Jhw== + +"@fontsource/space-mono@^4.4.5": + version "4.4.5" + resolved "https://registry.yarnpkg.com/@fontsource/space-mono/-/space-mono-4.4.5.tgz#2acc2f08c5060bc983bfbf14f8827770a5ce3c2a" + integrity sha512-QBU55UPj+JOzkwI/ezR1y1qBk+iJtYhjxBTHqXJMTrKCXIuWMD+NIXvVYHIJW8i6Aov+6kOksgVOqrU7rqOK8Q== + +"@fontsource/ubuntu-mono@^4.4.5": + version "4.4.5" + resolved "https://registry.yarnpkg.com/@fontsource/ubuntu-mono/-/ubuntu-mono-4.4.5.tgz#2653f4eedf89f4efa3708b6cdc990c06f90b5faf" + integrity sha512-TvkybOBDDfZwrIE9imcoMMi4xhdZY+0Q1gmmdrGCG2xFxPM2/fuHXBcn6FKLvm5/regKKq93DO6JIPcH2G1r/w== + +"@fontsource/ubuntu@^4.4.5": + version "4.4.5" + resolved "https://registry.yarnpkg.com/@fontsource/ubuntu/-/ubuntu-4.4.5.tgz#4d386755a4f3f88bfbc6a9d517f33dcdf263f992" + integrity sha512-dK/WdBncYJAZrwhANBGYN7wKrB2+NsYX/QomNBkJuze9kel9uijz7cNQPYdUDogink/X8D4MF2PeLEhF565fSA== + "@hapi/address@^2.1.2": version "2.1.4" resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5" @@ -1985,10 +2060,10 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" -esbuild@^0.12.5: - version "0.12.9" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.12.9.tgz#bed4e7087c286cd81d975631f77d47feb1660070" - integrity sha512-MWRhAbMOJ9RJygCrt778rz/qNYgA4ZVj6aXnNPxFjs7PmIpb0fuB9Gmg5uWrr6n++XKwwm/RmSz6RR5JL2Ocsw== +esbuild@^0.11.19: + version "0.11.23" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.11.23.tgz#c42534f632e165120671d64db67883634333b4b8" + integrity sha512-iaiZZ9vUF5wJV8ob1tl+5aJTrwDczlvGP0JoMmnpC2B0ppiMCu8n8gmy5ZTGl5bcG081XBVn+U+jP+mPFm5T5Q== escalade@^3.1.1: version "3.1.1" @@ -3119,7 +3194,7 @@ postcss-value-parser@^4.0.2: resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== -postcss@^8.3.0: +postcss@^8.2.1: version "8.3.5" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.5.tgz#982216b113412bc20a86289e91eb994952a5b709" integrity sha512-NxTuJocUhYGsMiMFHDUkmjSKT3EdH4/WbGF6GCi1NDGk+vbcUTun4fpbOqaPtD8IIsztA2ilZm2DhYCuyN58gA== @@ -3976,13 +4051,13 @@ vite-plugin-pwa@^0.8.1: workbox-build "^6.1.5" workbox-window "^6.1.5" -vite@^2.3.7: - version "2.3.7" - resolved "https://registry.yarnpkg.com/vite/-/vite-2.3.7.tgz#3023892419367465e1af1739578f8663d04243b2" - integrity sha512-Y0xRz11MPYu/EAvzN94+FsOZHbSvO6FUvHv127CyG7mV6oDoay2bw+g5y9wW3Blf8OY3chaz3nc/DcRe1IQ3Nw== +"vite@npm:@insertish/vite@2.2.4-dynamic-import-css-f428476": + version "2.2.4-dynamic-import-css-f428476" + resolved "https://registry.yarnpkg.com/@insertish/vite/-/vite-2.2.4-dynamic-import-css-f428476.tgz#33e0de5a3504c90d900e32c8536e0567dda9de17" + integrity sha512-rUKEbkNbUUNbVt5pb1OiHnkt09d+IiHfEOHGexSYmYKGEIPxQAKiGjrfvjpqH0Dzb0B5BbQ+FI23QmNKbari2Q== dependencies: - esbuild "^0.12.5" - postcss "^8.3.0" + esbuild "^0.11.19" + postcss "^8.2.1" resolve "^1.19.0" rollup "^2.38.5" optionalDependencies: