merge: branch 'master' into production

This commit is contained in:
Paul Makles 2022-09-02 14:42:47 +01:00
commit 8608257066
6 changed files with 70 additions and 57 deletions

2
external/components vendored

@ -1 +1 @@
Subproject commit 5e6e4a09c45baf18dcc44925f0113bad1d732e9c Subproject commit 557094115d17a363eb6fb8f27c6e697e049ce623

2
external/lang vendored

@ -1 +1 @@
Subproject commit 8ec1e5571e3d29a93500c0199f6823d16056b6f8 Subproject commit 05e7213b2290dfced3d41d54cb85f49670404cf2

View file

@ -4,12 +4,15 @@ import {
shift, shift,
useFloating, useFloating,
} from "@floating-ui/react-dom-interactions"; } from "@floating-ui/react-dom-interactions";
import { Plus } from "@styled-icons/boxicons-regular";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { Message } from "revolt.js"; import { Message } from "revolt.js";
import styled, { css } from "styled-components"; import styled, { css } from "styled-components";
import { createPortal } from "preact/compat"; 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 { emojiDictionary } from "../../../../assets/emojis";
import { useClient } from "../../../../controllers/client/ClientController"; import { useClient } from "../../../../controllers/client/ClientController";
@ -29,6 +32,14 @@ const List = styled.div`
flex-wrap: wrap; flex-wrap: wrap;
margin-top: 0.2em; margin-top: 0.2em;
align-items: center; 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) => { export const Reactions = observer(({ message }: Props) => {
const client = useClient(); const client = useClient();
const [showPicker, setPicker] = useState(false);
/** /**
* Render individual reaction entries * Render individual reaction entries
@ -137,6 +149,16 @@ export const Reactions = observer(({ message }: Props) => {
{Array.from(optional, (id) => ( {Array.from(optional, (id) => (
<Entry key={id} id={id} user_ids={message.reactions.get(id)} /> <Entry key={id} id={id} user_ids={message.reactions.get(id)} />
))} ))}
{message.channel?.havePermission("React") && (
<ReactionWrapper
message={message}
open={showPicker}
setOpen={setPicker}>
<IconButton className={showPicker ? "" : "add"}>
<Plus size={20} />
</IconButton>
</ReactionWrapper>
)}
</List> </List>
); );
}); });

View file

@ -184,6 +184,11 @@ const Container = styled.div<{ largeEmoji: boolean }>`
*/ */
const RE_QUOTE = /(^(?:>\s?){5})[>\s?]+(.*$)/gm; 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 * Regex for matching HTML tags
*/ */
@ -214,6 +219,9 @@ function sanitise(content: string) {
// because remark renderer is collapsing empty // because remark renderer is collapsing empty
// or otherwise whitespace-only lines of text // or otherwise whitespace-only lines of text
.replace(RE_EMPTY_LINE, "") .replace(RE_EMPTY_LINE, "")
// Ensure empty line after blockquotes for correct rendering
.replace(RE_BLOCKQUOTE, (match) => `${match}\n`)
); );
} }

View file

@ -1,8 +1,9 @@
import { Server } from "revolt.js"; import { Server } from "revolt.js";
import { Text } from "preact-i18n";
import { useState } from "preact/hooks"; 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"; import { FileUploader } from "../../../controllers/client/jsx/legacy/FileUploads";
@ -15,7 +16,9 @@ export function EmojiUploader({ server }: Props) {
return ( return (
<> <>
<h3>Upload Emoji</h3> <h3>
<Text id="app.settings.server_pages.emojis.upload" />
</h3>
<Form <Form
schema={{ schema={{
name: "text", name: "text",
@ -42,11 +45,6 @@ export function EmojiUploader({ server }: Props) {
), ),
}, },
}} }}
submitBtn={{
children: "Save",
palette: "secondary",
disabled: !fileId,
}}
onSubmit={async ({ name }) => { onSubmit={async ({ name }) => {
await server.client.api.put(`/custom/emoji/${fileId}`, { await server.client.api.put(`/custom/emoji/${fileId}`, {
name, name,
@ -54,8 +52,20 @@ export function EmojiUploader({ server }: Props) {
}); });
setFileId(""); setFileId("");
}} }}>
/> <Row>
<FormElement id="file" />
<Column>
<FormElement id="name" />
<Button
type="submit"
palette="secondary"
disabled={!fileId}>
<Text id="app.special.modals.actions.save" />
</Button>
</Column>
</Row>
</Form>
</> </>
); );
} }

View file

@ -8,40 +8,24 @@ import { Button, Column, Row, Stacked } from "@revoltchat/ui";
import UserShort from "../../../components/common/user/UserShort"; import UserShort from "../../../components/common/user/UserShort";
import { EmojiUploader } from "../../../components/settings/customisation/EmojiUploader"; import { EmojiUploader } from "../../../components/settings/customisation/EmojiUploader";
import { modalController } from "../../../controllers/modals/ModalController";
interface Props { interface Props {
server: Server; server: Server;
} }
const List = styled.div` const Emoji = styled(Row)`
gap: 8px;
display: flex;
flex-wrap: wrap;
`;
const Emoji = styled(Column)`
padding: 8px; padding: 8px;
border-radius: var(--border-radius); border-radius: var(--border-radius);
background: var(--secondary-background); background: var(--secondary-background);
`; `;
const Preview = styled.img` const Preview = styled.img`
width: 72px; width: 32px;
height: 72px; height: 32px;
object-fit: contain; object-fit: contain;
border-radius: var(--border-radius); border-radius: var(--border-radius);
`; `;
const UserInfo = styled(Row)`
font-size: 12px;
svg {
width: 14px;
height: 14px;
}
`;
export const Emojis = observer(({ server }: Props) => { export const Emojis = observer(({ server }: Props) => {
const emoji = [...server.client.emojis.values()].filter( const emoji = [...server.client.emojis.values()].filter(
(x) => x.parent.type === "Server" && x.parent.id === server._id, (x) => x.parent.type === "Server" && x.parent.id === server._id,
@ -57,33 +41,22 @@ export const Emojis = observer(({ server }: Props) => {
{" "} {" "}
{emoji.length} {emoji.length}
</h3> </h3>
<List>
{emoji.map((emoji) => ( {emoji.map((emoji) => (
<Emoji key={emoji._id} centred> <Emoji key={emoji._id} centred>
<Stacked> <Stacked>
<Preview src={emoji.imageURL} /> <Preview src={emoji.imageURL} />
</Stacked> </Stacked>
<span>{`:${emoji.name}:`}</span> <span>{`:${emoji.name}:`}</span>
<UserInfo centred> <Row centred>
<UserShort user={emoji.creator} /> <UserShort user={emoji.creator} />
</UserInfo> </Row>
<Button
palette="plain"
onClick={() =>
modalController.writeText(emoji._id)
}>
<Text id="app.context_menu.copy_id" />
</Button>
{server.havePermission("ManageCustomisation") && ( {server.havePermission("ManageCustomisation") && (
<Button <Button palette="plain" onClick={() => emoji.delete()}>
palette="plain"
onClick={() => emoji.delete()}>
<Text id="app.special.modals.actions.delete" /> <Text id="app.special.modals.actions.delete" />
</Button> </Button>
)} )}
</Emoji> </Emoji>
))} ))}
</List>
</Column> </Column>
); );
}); });