diff --git a/external/components b/external/components index 5e6e4a09..55709411 160000 --- a/external/components +++ b/external/components @@ -1 +1 @@ -Subproject commit 5e6e4a09c45baf18dcc44925f0113bad1d732e9c +Subproject commit 557094115d17a363eb6fb8f27c6e697e049ce623 diff --git a/external/lang b/external/lang index 8ec1e557..05e7213b 160000 --- a/external/lang +++ b/external/lang @@ -1 +1 @@ -Subproject commit 8ec1e5571e3d29a93500c0199f6823d16056b6f8 +Subproject commit 05e7213b2290dfced3d41d54cb85f49670404cf2 diff --git a/src/components/common/messaging/attachments/Reactions.tsx b/src/components/common/messaging/attachments/Reactions.tsx index 10abd2e6..9d8f0471 100644 --- a/src/components/common/messaging/attachments/Reactions.tsx +++ b/src/components/common/messaging/attachments/Reactions.tsx @@ -4,12 +4,15 @@ import { shift, useFloating, } from "@floating-ui/react-dom-interactions"; +import { Plus } from "@styled-icons/boxicons-regular"; import { observer } from "mobx-react-lite"; import { Message } from "revolt.js"; import styled, { css } from "styled-components"; import { createPortal } from "preact/compat"; -import { useCallback, useRef } from "preact/hooks"; +import { useCallback, useRef, useState } from "preact/hooks"; + +import { IconButton } from "@revoltchat/ui"; import { emojiDictionary } from "../../../../assets/emojis"; import { useClient } from "../../../../controllers/client/ClientController"; @@ -29,6 +32,14 @@ const List = styled.div` flex-wrap: wrap; margin-top: 0.2em; align-items: center; + + .add { + display: none; + } + + &:hover .add { + display: grid; + } `; /** @@ -79,6 +90,7 @@ const Reaction = styled.div<{ active: boolean }>` */ export const Reactions = observer(({ message }: Props) => { const client = useClient(); + const [showPicker, setPicker] = useState(false); /** * Render individual reaction entries @@ -137,6 +149,16 @@ export const Reactions = observer(({ message }: Props) => { {Array.from(optional, (id) => ( ))} + {message.channel?.havePermission("React") && ( + + + + + + )} ); }); diff --git a/src/components/markdown/RemarkRenderer.tsx b/src/components/markdown/RemarkRenderer.tsx index 94280028..c826dbdc 100644 --- a/src/components/markdown/RemarkRenderer.tsx +++ b/src/components/markdown/RemarkRenderer.tsx @@ -184,6 +184,11 @@ const Container = styled.div<{ largeEmoji: boolean }>` */ const RE_QUOTE = /(^(?:>\s?){5})[>\s?]+(.*$)/gm; +/** + * Regex for matching multi-line blockquotes + */ +const RE_BLOCKQUOTE = /^([^\S\r\n]*>[^\n]+\n?)+/gm; + /** * Regex for matching HTML tags */ @@ -214,6 +219,9 @@ function sanitise(content: string) { // because remark renderer is collapsing empty // or otherwise whitespace-only lines of text .replace(RE_EMPTY_LINE, "‎") + + // Ensure empty line after blockquotes for correct rendering + .replace(RE_BLOCKQUOTE, (match) => `${match}\n`) ); } diff --git a/src/components/settings/customisation/EmojiUploader.tsx b/src/components/settings/customisation/EmojiUploader.tsx index 4a1b42c5..108fc27c 100644 --- a/src/components/settings/customisation/EmojiUploader.tsx +++ b/src/components/settings/customisation/EmojiUploader.tsx @@ -1,8 +1,9 @@ import { Server } from "revolt.js"; +import { Text } from "preact-i18n"; import { useState } from "preact/hooks"; -import { Form } from "@revoltchat/ui"; +import { Button, Column, Form, FormElement, Row } from "@revoltchat/ui"; import { FileUploader } from "../../../controllers/client/jsx/legacy/FileUploads"; @@ -15,7 +16,9 @@ export function EmojiUploader({ server }: Props) { return ( <> -

Upload Emoji

+

+ +

{ await server.client.api.put(`/custom/emoji/${fileId}`, { name, @@ -54,8 +52,20 @@ export function EmojiUploader({ server }: Props) { }); setFileId(""); - }} - /> + }}> + + + + + + + +
); } diff --git a/src/pages/settings/server/Emojis.tsx b/src/pages/settings/server/Emojis.tsx index d5f4b3d1..0dd3cf1d 100644 --- a/src/pages/settings/server/Emojis.tsx +++ b/src/pages/settings/server/Emojis.tsx @@ -8,40 +8,24 @@ import { Button, Column, Row, Stacked } from "@revoltchat/ui"; import UserShort from "../../../components/common/user/UserShort"; import { EmojiUploader } from "../../../components/settings/customisation/EmojiUploader"; -import { modalController } from "../../../controllers/modals/ModalController"; interface Props { server: Server; } -const List = styled.div` - gap: 8px; - display: flex; - flex-wrap: wrap; -`; - -const Emoji = styled(Column)` +const Emoji = styled(Row)` padding: 8px; border-radius: var(--border-radius); background: var(--secondary-background); `; const Preview = styled.img` - width: 72px; - height: 72px; + width: 32px; + height: 32px; object-fit: contain; border-radius: var(--border-radius); `; -const UserInfo = styled(Row)` - font-size: 12px; - - svg { - width: 14px; - height: 14px; - } -`; - export const Emojis = observer(({ server }: Props) => { const emoji = [...server.client.emojis.values()].filter( (x) => x.parent.type === "Server" && x.parent.id === server._id, @@ -57,33 +41,22 @@ export const Emojis = observer(({ server }: Props) => { {" – "} {emoji.length} - - {emoji.map((emoji) => ( - - - - - {`:${emoji.name}:`} - - - - - {server.havePermission("ManageCustomisation") && ( - - )} - - ))} - + )} + + ))} ); });