mirror of
https://github.com/revoltchat/revite.git
synced 2024-11-22 07:00:58 -05:00
feat: add corresponding UI for interactions
reactions
This commit is contained in:
parent
084c90613f
commit
dedc1e0666
1 changed files with 78 additions and 16 deletions
|
@ -2,6 +2,8 @@ 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 { useCallback } from "preact/hooks";
|
||||||
|
|
||||||
import { useClient } from "../../../../controllers/client/ClientController";
|
import { useClient } from "../../../../controllers/client/ClientController";
|
||||||
import { RenderEmoji } from "../../../markdown/plugins/emoji";
|
import { RenderEmoji } from "../../../markdown/plugins/emoji";
|
||||||
|
|
||||||
|
@ -9,18 +11,35 @@ interface Props {
|
||||||
message: Message;
|
message: Message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reaction list element
|
||||||
|
*/
|
||||||
const List = styled.div`
|
const List = styled.div`
|
||||||
gap: 0.4em;
|
gap: 0.4em;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
margin-top: 0.2em;
|
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 }>`
|
const Reaction = styled.div<{ active: boolean }>`
|
||||||
padding: 0.4em;
|
padding: 0.4em;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
border: 1px solid transparent;
|
||||||
color: var(--secondary-foreground);
|
color: var(--secondary-foreground);
|
||||||
border-radius: var(--border-radius);
|
border-radius: var(--border-radius);
|
||||||
background: var(--secondary-background);
|
background: var(--secondary-background);
|
||||||
|
@ -42,30 +61,73 @@ const Reaction = styled.div<{ active: boolean }>`
|
||||||
${(props) =>
|
${(props) =>
|
||||||
props.active &&
|
props.active &&
|
||||||
css`
|
css`
|
||||||
border: 1px solid var(--accent);
|
border-color: var(--accent);
|
||||||
`}
|
`}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render reactions on a message
|
||||||
|
*/
|
||||||
export const Reactions = observer(({ message }: Props) => {
|
export const Reactions = observer(({ message }: Props) => {
|
||||||
const client = useClient();
|
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<string> }) => {
|
||||||
|
const active = user_ids?.has(client.user!._id) || false;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Reaction
|
||||||
|
active={active}
|
||||||
|
onClick={() =>
|
||||||
|
active ? message.unreact(id) : message.react(id)
|
||||||
|
}>
|
||||||
|
<RenderEmoji match={id} /> {user_ids?.size || 0}
|
||||||
|
</Reaction>
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine two lists of 'required' and 'optional' reactions
|
||||||
|
*/
|
||||||
|
const { required, optional } = (() => {
|
||||||
|
const required = new Set<string>();
|
||||||
|
const optional = new Set<string>();
|
||||||
|
|
||||||
|
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 (
|
return (
|
||||||
<List>
|
<List>
|
||||||
{Array.from(message.reactions, ([key, user_ids]) => {
|
{Array.from(required, (id) => (
|
||||||
const active = user_ids.has(client.user!._id);
|
<Entry key={id} id={id} user_ids={message.reactions.get(id)} />
|
||||||
|
))}
|
||||||
return (
|
{required.size !== 0 && optional.size !== 0 && <Divider />}
|
||||||
<Reaction
|
{Array.from(optional, (id) => (
|
||||||
key={key}
|
<Entry key={id} id={id} user_ids={message.reactions.get(id)} />
|
||||||
active={active}
|
))}
|
||||||
onClick={() =>
|
|
||||||
active ? message.unreact(key) : message.react(key)
|
|
||||||
}>
|
|
||||||
<RenderEmoji match={key} /> {user_ids.size}
|
|
||||||
</Reaction>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</List>
|
</List>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue