diff --git a/external/lang b/external/lang index cf0b1893..d4244046 160000 --- a/external/lang +++ b/external/lang @@ -1 +1 @@ -Subproject commit cf0b1893b66bbe100580ed2f2950d1ce125aee10 +Subproject commit d42440461f1c661e4f341a81e253021d45d1d526 diff --git a/src/components/common/messaging/attachments/Reactions.tsx b/src/components/common/messaging/attachments/Reactions.tsx index b9c7380a..d53ca835 100644 --- a/src/components/common/messaging/attachments/Reactions.tsx +++ b/src/components/common/messaging/attachments/Reactions.tsx @@ -2,6 +2,8 @@ import { observer } from "mobx-react-lite"; import { Message } from "revolt.js"; import styled, { css } from "styled-components"; +import { useCallback } from "preact/hooks"; + import { useClient } from "../../../../controllers/client/ClientController"; import { RenderEmoji } from "../../../markdown/plugins/emoji"; @@ -9,18 +11,35 @@ interface Props { message: Message; } +/** + * Reaction list element + */ const List = styled.div` gap: 0.4em; display: flex; flex-wrap: wrap; margin-top: 0.2em; + align-items: center; `; +/** + * List divider + */ +const Divider = styled.div` + width: 1px; + height: 14px; + background: var(--tertiary-foreground); +`; + +/** + * Reaction styling + */ const Reaction = styled.div<{ active: boolean }>` padding: 0.4em; cursor: pointer; user-select: none; vertical-align: middle; + border: 1px solid transparent; color: var(--secondary-foreground); border-radius: var(--border-radius); background: var(--secondary-background); @@ -42,30 +61,73 @@ const Reaction = styled.div<{ active: boolean }>` ${(props) => props.active && css` - border: 1px solid var(--accent); + border-color: var(--accent); `} `; +/** + * Render reactions on a message + */ export const Reactions = observer(({ message }: Props) => { const client = useClient(); - if (message.reactions.size === 0) return null; + + /** + * Render individual reaction entries + */ + const Entry = useCallback( + observer(({ id, user_ids }: { id: string; user_ids?: Set }) => { + const active = user_ids?.has(client.user!._id) || false; + + return ( + + active ? message.unreact(id) : message.react(id) + }> + {user_ids?.size || 0} + + ); + }), + [], + ); + + /** + * Determine two lists of 'required' and 'optional' reactions + */ + const { required, optional } = (() => { + const required = new Set(); + const optional = new Set(); + + if (message.interactions?.reactions) { + for (const reaction of message.interactions.reactions) { + required.add(reaction); + } + } + + for (const key of message.reactions.keys()) { + if (!required.has(key)) { + optional.add(key); + } + } + + return { + required, + optional, + }; + })(); + + // Don't render list if nothing is going to show anyways + if (required.size === 0 && optional.size === 0) return null; return ( - {Array.from(message.reactions, ([key, user_ids]) => { - const active = user_ids.has(client.user!._id); - - return ( - - active ? message.unreact(key) : message.react(key) - }> - {user_ids.size} - - ); - })} + {Array.from(required, (id) => ( + + ))} + {required.size !== 0 && optional.size !== 0 && } + {Array.from(optional, (id) => ( + + ))} ); });