feat(chat): message reactions (#21385)
* feat(chat): message reactions * fix: Revert settings.yml change introduced in #21355
This commit is contained in:
parent
51c763dfc0
commit
93f82e2d90
@ -35,4 +35,12 @@ export interface Message {
|
|||||||
color: string;
|
color: string;
|
||||||
};
|
};
|
||||||
} | null;
|
} | null;
|
||||||
|
reactions: {
|
||||||
|
createdAt: string;
|
||||||
|
reactionEmoji: string;
|
||||||
|
user: {
|
||||||
|
name: string;
|
||||||
|
userId: string;
|
||||||
|
}
|
||||||
|
}[];
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import React, {
|
|||||||
useEffect,
|
useEffect,
|
||||||
useMemo,
|
useMemo,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
import { useMutation } from '@apollo/client';
|
||||||
import { UpdatedEventDetailsForChatMessageDomElements } from 'bigbluebutton-html-plugin-sdk/dist/cjs/dom-element-manipulation/chat/message/types';
|
import { UpdatedEventDetailsForChatMessageDomElements } from 'bigbluebutton-html-plugin-sdk/dist/cjs/dom-element-manipulation/chat/message/types';
|
||||||
import { Message } from '/imports/ui/Types/message';
|
import { Message } from '/imports/ui/Types/message';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
@ -34,6 +35,7 @@ import { Layout } from '/imports/ui/components/layout/layoutTypes';
|
|||||||
import useChat from '/imports/ui/core/hooks/useChat';
|
import useChat from '/imports/ui/core/hooks/useChat';
|
||||||
import { GraphqlDataHookSubscriptionResponse } from '/imports/ui/Types/hook';
|
import { GraphqlDataHookSubscriptionResponse } from '/imports/ui/Types/hook';
|
||||||
import { Chat } from '/imports/ui/Types/chat';
|
import { Chat } from '/imports/ui/Types/chat';
|
||||||
|
import { CHAT_DELETE_REACTION_MUTATION, CHAT_SEND_REACTION_MUTATION } from './mutations';
|
||||||
|
|
||||||
interface ChatMessageProps {
|
interface ChatMessageProps {
|
||||||
message: Message;
|
message: Message;
|
||||||
@ -119,12 +121,34 @@ const ChatMesssage: React.FC<ChatMessageProps> = ({
|
|||||||
}
|
}
|
||||||
}, [message, messageRef]);
|
}, [message, messageRef]);
|
||||||
const messageContentRef = React.createRef<HTMLDivElement>();
|
const messageContentRef = React.createRef<HTMLDivElement>();
|
||||||
const [reactions, setReactions] = React.useState<{ id: string, native: string }[]>([]);
|
|
||||||
const [editing, setEditing] = React.useState(false);
|
const [editing, setEditing] = React.useState(false);
|
||||||
const [isToolbarMenuOpen, setIsToolbarMenuOpen] = React.useState(false);
|
const [isToolbarMenuOpen, setIsToolbarMenuOpen] = React.useState(false);
|
||||||
|
const [isToolbarReactionPopoverOpen, setIsToolbarReactionPopoverOpen] = React.useState(false);
|
||||||
const chatFocusMessageRequest = useStorageKey(ChatEvents.CHAT_FOCUS_MESSAGE_REQUEST, STORAGES.IN_MEMORY);
|
const chatFocusMessageRequest = useStorageKey(ChatEvents.CHAT_FOCUS_MESSAGE_REQUEST, STORAGES.IN_MEMORY);
|
||||||
const containerRef = React.useRef<HTMLDivElement>(null);
|
const containerRef = React.useRef<HTMLDivElement>(null);
|
||||||
const animationInitialTimestamp = React.useRef(0);
|
const animationInitialTimestamp = React.useRef(0);
|
||||||
|
const [chatSendReaction] = useMutation(CHAT_SEND_REACTION_MUTATION);
|
||||||
|
const [chatDeleteReaction] = useMutation(CHAT_DELETE_REACTION_MUTATION);
|
||||||
|
|
||||||
|
const sendReaction = useCallback((reactionEmoji: string) => {
|
||||||
|
chatSendReaction({
|
||||||
|
variables: {
|
||||||
|
chatId: message.chatId,
|
||||||
|
messageId: message.messageId,
|
||||||
|
reactionEmoji,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const deleteReaction = useCallback((reactionEmoji: string) => {
|
||||||
|
chatDeleteReaction({
|
||||||
|
variables: {
|
||||||
|
chatId: message.chatId,
|
||||||
|
messageId: message.messageId,
|
||||||
|
reactionEmoji,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
const CHAT_TOOLBAR_CONFIG = window.meetingClientSettings.public.chat.toolbar;
|
const CHAT_TOOLBAR_CONFIG = window.meetingClientSettings.public.chat.toolbar;
|
||||||
const isModerator = currentUser?.isModerator;
|
const isModerator = currentUser?.isModerator;
|
||||||
@ -379,6 +403,7 @@ const ChatMesssage: React.FC<ChatMessageProps> = ({
|
|||||||
isCustomPluginMessage={isCustomPluginMessage}
|
isCustomPluginMessage={isCustomPluginMessage}
|
||||||
$highlight={hasToolbar}
|
$highlight={hasToolbar}
|
||||||
$toolbarMenuIsOpen={isToolbarMenuOpen}
|
$toolbarMenuIsOpen={isToolbarMenuOpen}
|
||||||
|
$reactionPopoverIsOpen={isToolbarReactionPopoverOpen}
|
||||||
>
|
>
|
||||||
{hasToolbar && (
|
{hasToolbar && (
|
||||||
<ChatMessageToolbar
|
<ChatMessageToolbar
|
||||||
@ -391,21 +416,18 @@ const ChatMesssage: React.FC<ChatMessageProps> = ({
|
|||||||
messageSequence={message.messageSequence}
|
messageSequence={message.messageSequence}
|
||||||
emphasizedMessage={message.chatEmphasizedText}
|
emphasizedMessage={message.chatEmphasizedText}
|
||||||
onEmojiSelected={(emoji) => {
|
onEmojiSelected={(emoji) => {
|
||||||
setReactions((prev) => {
|
sendReaction(emoji.native);
|
||||||
return [
|
setIsToolbarReactionPopoverOpen(false);
|
||||||
...prev,
|
|
||||||
emoji,
|
|
||||||
];
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
onEditRequest={() => {
|
onEditRequest={() => {
|
||||||
setEditing(true);
|
setEditing(true);
|
||||||
}}
|
}}
|
||||||
onMenuOpenChange={setIsToolbarMenuOpen}
|
onMenuOpenChange={setIsToolbarMenuOpen}
|
||||||
menuIsOpen={isToolbarMenuOpen}
|
menuIsOpen={isToolbarMenuOpen}
|
||||||
|
onReactionPopoverOpenChange={setIsToolbarReactionPopoverOpen}
|
||||||
|
reactionPopoverIsOpen={isToolbarReactionPopoverOpen}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<ChatMessageReactions reactions={reactions} />
|
|
||||||
{((!message?.user || !sameSender) && (
|
{((!message?.user || !sameSender) && (
|
||||||
message.messageType !== ChatMessageType.USER_AWAY_STATUS_MSG
|
message.messageType !== ChatMessageType.USER_AWAY_STATUS_MSG
|
||||||
&& message.messageType !== ChatMessageType.API
|
&& message.messageType !== ChatMessageType.API
|
||||||
@ -432,6 +454,7 @@ const ChatMesssage: React.FC<ChatMessageProps> = ({
|
|||||||
data-chat-message-id={message?.messageId}
|
data-chat-message-id={message?.messageId}
|
||||||
$highlight={hasToolbar}
|
$highlight={hasToolbar}
|
||||||
$toolbarMenuIsOpen={isToolbarMenuOpen}
|
$toolbarMenuIsOpen={isToolbarMenuOpen}
|
||||||
|
$reactionPopoverIsOpen={isToolbarReactionPopoverOpen}
|
||||||
>
|
>
|
||||||
{message.messageType !== ChatMessageType.CHAT_CLEAR
|
{message.messageType !== ChatMessageType.CHAT_CLEAR
|
||||||
&& !isCustomPluginMessage
|
&& !isCustomPluginMessage
|
||||||
@ -474,6 +497,13 @@ const ChatMesssage: React.FC<ChatMessageProps> = ({
|
|||||||
{intl.formatMessage(intlMessages.deleteMessage, { 0: message.deletedBy?.name })}
|
{intl.formatMessage(intlMessages.deleteMessage, { 0: message.deletedBy?.name })}
|
||||||
</DeleteMessage>
|
</DeleteMessage>
|
||||||
)}
|
)}
|
||||||
|
{!deleteTime && (
|
||||||
|
<ChatMessageReactions
|
||||||
|
reactions={message.reactions}
|
||||||
|
deleteReaction={deleteReaction}
|
||||||
|
sendReaction={sendReaction}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</ChatContent>
|
</ChatContent>
|
||||||
)}
|
)}
|
||||||
{editing && (
|
{editing && (
|
||||||
@ -497,7 +527,8 @@ function areChatMessagesEqual(prevProps: ChatMessageProps, nextProps: ChatMessag
|
|||||||
return prevMessage?.createdAt === nextMessage?.createdAt
|
return prevMessage?.createdAt === nextMessage?.createdAt
|
||||||
&& prevMessage?.user?.currentlyInMeeting === nextMessage?.user?.currentlyInMeeting
|
&& prevMessage?.user?.currentlyInMeeting === nextMessage?.user?.currentlyInMeeting
|
||||||
&& prevMessage?.recipientHasSeen === nextMessage.recipientHasSeen
|
&& prevMessage?.recipientHasSeen === nextMessage.recipientHasSeen
|
||||||
&& prevMessage?.message === nextMessage.message;
|
&& prevMessage?.message === nextMessage.message
|
||||||
|
&& prevMessage?.reactions?.length === nextMessage?.reactions?.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default memo(ChatMesssage, areChatMessagesEqual);
|
export default memo(ChatMesssage, areChatMessagesEqual);
|
||||||
|
@ -1,29 +1,100 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import Styled from './styles';
|
import Styled from './styles';
|
||||||
|
import useCurrentUser from '/imports/ui/core/hooks/useCurrentUser';
|
||||||
|
import TooltipContainer from '/imports/ui/components/common/tooltip/container';
|
||||||
|
|
||||||
|
const intlMessages = defineMessages({
|
||||||
|
reactedBy: {
|
||||||
|
id: 'app.chat.toolbar.reactions.reactedByLabel',
|
||||||
|
},
|
||||||
|
you: {
|
||||||
|
id: 'app.chat.toolbar.reactions.youLabel',
|
||||||
|
},
|
||||||
|
and: {
|
||||||
|
id: 'app.chat.toolbar.reactions.andLabel',
|
||||||
|
},
|
||||||
|
findAReaction: {
|
||||||
|
id: 'app.chat.toolbar.reactions.findReactionButtonLabel',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
interface ChatMessageReactionsProps {
|
interface ChatMessageReactionsProps {
|
||||||
reactions: {
|
reactions: {
|
||||||
id: string;
|
createdAt: string;
|
||||||
native: string;
|
reactionEmoji: string;
|
||||||
|
user: {
|
||||||
|
name: string;
|
||||||
|
userId: string;
|
||||||
|
};
|
||||||
}[];
|
}[];
|
||||||
|
sendReaction(reactionEmoji: string): void;
|
||||||
|
deleteReaction(reactionEmoji: string): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ChatMessageReactions: React.FC<ChatMessageReactionsProps> = (props) => {
|
const ChatMessageReactions: React.FC<ChatMessageReactionsProps> = (props) => {
|
||||||
const { reactions } = props;
|
const { reactions, sendReaction, deleteReaction } = props;
|
||||||
|
|
||||||
|
const { data: currentUser } = useCurrentUser((u) => ({ userId: u.userId }));
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const reactionItems: Record<string, { count: number; userNames: string[]; reactedByMe: boolean }> = {};
|
||||||
|
|
||||||
|
reactions.forEach((reaction) => {
|
||||||
|
if (!reactionItems[reaction.reactionEmoji]) {
|
||||||
|
reactionItems[reaction.reactionEmoji] = {
|
||||||
|
count: 0,
|
||||||
|
userNames: [],
|
||||||
|
reactedByMe: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
reactionItems[reaction.reactionEmoji].count += 1;
|
||||||
|
if (reaction.user.userId === currentUser?.userId) {
|
||||||
|
reactionItems[reaction.reactionEmoji].reactedByMe = true;
|
||||||
|
} else {
|
||||||
|
reactionItems[reaction.reactionEmoji].userNames.push(reaction.user.name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Styled.ReactionsWrapper>
|
<Styled.ReactionsWrapper>
|
||||||
{reactions.map((emoji) => {
|
{Object.keys(reactionItems).map((emoji) => {
|
||||||
|
const details = reactionItems[emoji];
|
||||||
|
let label = intl.formatMessage(intlMessages.reactedBy);
|
||||||
|
if (details.userNames.length) {
|
||||||
|
const users = details.userNames.join(', ');
|
||||||
|
label += ` ${users}`;
|
||||||
|
|
||||||
|
if (details.reactedByMe) {
|
||||||
|
label += ` ${intl.formatMessage(intlMessages.and)} ${intl.formatMessage(intlMessages.you)}`;
|
||||||
|
}
|
||||||
|
} else if (details.reactedByMe) {
|
||||||
|
label += ` ${intl.formatMessage(intlMessages.you)}`;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Styled.EmojiWrapper highlighted={false}>
|
<TooltipContainer title={label}>
|
||||||
<em-emoji
|
<Styled.EmojiWrapper
|
||||||
emoji={emoji}
|
highlighted={details.reactedByMe}
|
||||||
size={parseFloat(
|
onClick={() => {
|
||||||
window.getComputedStyle(document.documentElement).fontSize,
|
if (details.reactedByMe) {
|
||||||
)}
|
deleteReaction(emoji);
|
||||||
native={emoji.native}
|
} else {
|
||||||
/>
|
sendReaction(emoji);
|
||||||
</Styled.EmojiWrapper>
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* @ts-ignore */}
|
||||||
|
<em-emoji
|
||||||
|
size={parseFloat(
|
||||||
|
window.getComputedStyle(document.documentElement).fontSize,
|
||||||
|
)}
|
||||||
|
native={emoji}
|
||||||
|
/>
|
||||||
|
<span>{details.count}</span>
|
||||||
|
</Styled.EmojiWrapper>
|
||||||
|
</TooltipContainer>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</Styled.ReactionsWrapper>
|
</Styled.ReactionsWrapper>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { colorBlueLighter, colorBlueLightest, colorGray } from '/imports/ui/stylesheets/styled-components/palette';
|
import { colorBlueLighter, colorBlueLightest, colorGray } from '/imports/ui/stylesheets/styled-components/palette';
|
||||||
|
|
||||||
const EmojiWrapper = styled.div<{ highlighted: boolean }>`
|
const EmojiWrapper = styled.button<{ highlighted: boolean }>`
|
||||||
background-color: ${colorBlueLightest};
|
background-color: ${colorBlueLightest};
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
margin-left: 3px;
|
margin-left: 3px;
|
||||||
@ -24,9 +24,6 @@ const EmojiWrapper = styled.div<{ highlighted: boolean }>`
|
|||||||
const ReactionsWrapper = styled.div`
|
const ReactionsWrapper = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -50,12 +50,15 @@ interface ChatMessageToolbarProps {
|
|||||||
onEditRequest(): void;
|
onEditRequest(): void;
|
||||||
onMenuOpenChange(open: boolean): void;
|
onMenuOpenChange(open: boolean): void;
|
||||||
menuIsOpen: boolean;
|
menuIsOpen: boolean;
|
||||||
|
onReactionPopoverOpenChange(open: boolean): void;
|
||||||
|
reactionPopoverIsOpen: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ChatMessageToolbar: React.FC<ChatMessageToolbarProps> = (props) => {
|
const ChatMessageToolbar: React.FC<ChatMessageToolbarProps> = (props) => {
|
||||||
const {
|
const {
|
||||||
messageId, chatId, message, username, onEmojiSelected, onMenuOpenChange,
|
messageId, chatId, message, username, onEmojiSelected, onMenuOpenChange,
|
||||||
messageSequence, emphasizedMessage, onEditRequest, own, amIModerator, menuIsOpen,
|
messageSequence, emphasizedMessage, onEditRequest, own, amIModerator, menuIsOpen,
|
||||||
|
onReactionPopoverOpenChange, reactionPopoverIsOpen,
|
||||||
} = props;
|
} = props;
|
||||||
const [reactionsAnchor, setReactionsAnchor] = React.useState<Element | null>(
|
const [reactionsAnchor, setReactionsAnchor] = React.useState<Element | null>(
|
||||||
null,
|
null,
|
||||||
@ -118,7 +121,12 @@ const ChatMessageToolbar: React.FC<ChatMessageToolbarProps> = (props) => {
|
|||||||
].every((config) => !config)) return null;
|
].every((config) => !config)) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container className="chat-message-toolbar" $sequence={messageSequence} $menuIsOpen={menuIsOpen}>
|
<Container
|
||||||
|
className="chat-message-toolbar"
|
||||||
|
$sequence={messageSequence}
|
||||||
|
$menuIsOpen={menuIsOpen}
|
||||||
|
$reactionPopoverIsOpen={reactionPopoverIsOpen}
|
||||||
|
>
|
||||||
{CHAT_REPLIES_ENABLED && (
|
{CHAT_REPLIES_ENABLED && (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
@ -154,8 +162,9 @@ const ChatMessageToolbar: React.FC<ChatMessageToolbarProps> = (props) => {
|
|||||||
<EmojiButton
|
<EmojiButton
|
||||||
onClick={(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
onClick={(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setReactionsAnchor(e.currentTarget);
|
onReactionPopoverOpenChange(true);
|
||||||
}}
|
}}
|
||||||
|
setRef={setReactionsAnchor}
|
||||||
size="sm"
|
size="sm"
|
||||||
icon="happy"
|
icon="happy"
|
||||||
color="light"
|
color="light"
|
||||||
@ -204,10 +213,11 @@ const ChatMessageToolbar: React.FC<ChatMessageToolbarProps> = (props) => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Popover
|
<Popover
|
||||||
open={Boolean(reactionsAnchor)}
|
open={reactionPopoverIsOpen}
|
||||||
anchorEl={reactionsAnchor}
|
anchorEl={reactionsAnchor}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
setReactionsAnchor(null);
|
setReactionsAnchor(null);
|
||||||
|
onReactionPopoverOpenChange(false);
|
||||||
}}
|
}}
|
||||||
anchorOrigin={{
|
anchorOrigin={{
|
||||||
vertical: 'top',
|
vertical: 'top',
|
||||||
|
@ -8,7 +8,7 @@ import { borderRadius, smPaddingX } from '/imports/ui/stylesheets/styled-compone
|
|||||||
import EmojiPickerComponent from '/imports/ui/components/emoji-picker/component';
|
import EmojiPickerComponent from '/imports/ui/components/emoji-picker/component';
|
||||||
import Button from '/imports/ui/components/common/button/component';
|
import Button from '/imports/ui/components/common/button/component';
|
||||||
|
|
||||||
const Container = styled.div<{ $sequence: number, $menuIsOpen: boolean }>`
|
const Container = styled.div<{ $sequence: number, $menuIsOpen: boolean, $reactionPopoverIsOpen: boolean }>`
|
||||||
height: calc(1.5rem + 12px);
|
height: calc(1.5rem + 12px);
|
||||||
line-height: calc(1.5rem + 8px);
|
line-height: calc(1.5rem + 8px);
|
||||||
max-width: 184px;
|
max-width: 184px;
|
||||||
@ -26,7 +26,7 @@ const Container = styled.div<{ $sequence: number, $menuIsOpen: boolean }>`
|
|||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
${({ $menuIsOpen }) => ($menuIsOpen && css`
|
${({ $menuIsOpen, $reactionPopoverIsOpen }) => (($menuIsOpen || $reactionPopoverIsOpen) && css`
|
||||||
display: flex;
|
display: flex;
|
||||||
`)}
|
`)}
|
||||||
|
|
||||||
|
@ -12,12 +12,28 @@ const CHAT_DELETE_MESSAGE_MUTATION = gql`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const CHAT_SEND_REACTION_MUTATION = gql`
|
||||||
|
mutation($chatId: String!, $messageId: String!, $reactionEmoji: String!) {
|
||||||
|
chatSendMessageReaction(chatId: $chatId, messageId: $messageId, reactionEmoji: $reactionEmoji)
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const CHAT_DELETE_REACTION_MUTATION = gql`
|
||||||
|
mutation($chatId: String!, $messageId: String!, $reactionEmoji: String!) {
|
||||||
|
chatDeleteMessageReaction(chatId: $chatId, messageId: $messageId, reactionEmoji: $reactionEmoji)
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
CHAT_EDIT_MESSAGE_MUTATION,
|
CHAT_EDIT_MESSAGE_MUTATION,
|
||||||
CHAT_DELETE_MESSAGE_MUTATION,
|
CHAT_DELETE_MESSAGE_MUTATION,
|
||||||
|
CHAT_DELETE_REACTION_MUTATION,
|
||||||
|
CHAT_SEND_REACTION_MUTATION,
|
||||||
};
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
CHAT_EDIT_MESSAGE_MUTATION,
|
CHAT_EDIT_MESSAGE_MUTATION,
|
||||||
CHAT_DELETE_MESSAGE_MUTATION,
|
CHAT_DELETE_MESSAGE_MUTATION,
|
||||||
|
CHAT_DELETE_REACTION_MUTATION,
|
||||||
|
CHAT_SEND_REACTION_MUTATION,
|
||||||
};
|
};
|
||||||
|
@ -28,6 +28,7 @@ interface ChatWrapperProps {
|
|||||||
isCustomPluginMessage: boolean;
|
isCustomPluginMessage: boolean;
|
||||||
$highlight: boolean;
|
$highlight: boolean;
|
||||||
$toolbarMenuIsOpen: boolean;
|
$toolbarMenuIsOpen: boolean;
|
||||||
|
$reactionPopoverIsOpen: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ChatContentProps {
|
interface ChatContentProps {
|
||||||
@ -35,6 +36,7 @@ interface ChatContentProps {
|
|||||||
isCustomPluginMessage: boolean;
|
isCustomPluginMessage: boolean;
|
||||||
$highlight: boolean;
|
$highlight: boolean;
|
||||||
$toolbarMenuIsOpen: boolean;
|
$toolbarMenuIsOpen: boolean;
|
||||||
|
$reactionPopoverIsOpen: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ChatAvatarProps {
|
interface ChatAvatarProps {
|
||||||
@ -87,7 +89,9 @@ export const ChatWrapper = styled.div<ChatWrapperProps>`
|
|||||||
}
|
}
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
`}
|
`}
|
||||||
${({ sameSender, $toolbarMenuIsOpen }) => !sameSender && $toolbarMenuIsOpen && `
|
${({ sameSender, $toolbarMenuIsOpen, $reactionPopoverIsOpen }) => !sameSender
|
||||||
|
&& ($toolbarMenuIsOpen || $reactionPopoverIsOpen)
|
||||||
|
&& `
|
||||||
background-color: ${colorOffWhite};
|
background-color: ${colorOffWhite};
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
`}
|
`}
|
||||||
@ -110,7 +114,9 @@ export const ChatContent = styled.div<ChatContentProps>`
|
|||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
`}
|
`}
|
||||||
|
|
||||||
${({ sameSender, $toolbarMenuIsOpen }) => sameSender && $toolbarMenuIsOpen && `
|
${({ sameSender, $toolbarMenuIsOpen, $reactionPopoverIsOpen }) => sameSender
|
||||||
|
&& ($toolbarMenuIsOpen || $reactionPopoverIsOpen)
|
||||||
|
&& `
|
||||||
background-color: ${colorOffWhite};
|
background-color: ${colorOffWhite};
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
`}
|
`}
|
||||||
|
@ -51,6 +51,7 @@ const areChatPagesEqual = (prevProps: ChatListPageProps, nextProps: ChatListPage
|
|||||||
&& prevMessage?.user?.currentlyInMeeting === nextMessage?.user?.currentlyInMeeting
|
&& prevMessage?.user?.currentlyInMeeting === nextMessage?.user?.currentlyInMeeting
|
||||||
&& prevMessage?.recipientHasSeen === nextMessage?.recipientHasSeen
|
&& prevMessage?.recipientHasSeen === nextMessage?.recipientHasSeen
|
||||||
&& prevMessage?.message === nextMessage?.message
|
&& prevMessage?.message === nextMessage?.message
|
||||||
|
&& prevMessage?.reactions?.length === nextMessage?.reactions?.length
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -25,6 +25,14 @@ export const CHAT_MESSAGE_PUBLIC_SUBSCRIPTION = gql`
|
|||||||
color
|
color
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
reactions {
|
||||||
|
createdAt
|
||||||
|
reactionEmoji
|
||||||
|
user {
|
||||||
|
name
|
||||||
|
userId
|
||||||
|
}
|
||||||
|
}
|
||||||
messageType
|
messageType
|
||||||
chatEmphasizedText
|
chatEmphasizedText
|
||||||
chatId
|
chatId
|
||||||
@ -73,6 +81,14 @@ export const CHAT_MESSAGE_PRIVATE_SUBSCRIPTION = gql`
|
|||||||
color
|
color
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
reactions {
|
||||||
|
createdAt
|
||||||
|
reactionEmoji
|
||||||
|
user {
|
||||||
|
name
|
||||||
|
userId
|
||||||
|
}
|
||||||
|
}
|
||||||
chatId
|
chatId
|
||||||
message
|
message
|
||||||
messageType
|
messageType
|
||||||
|
@ -59,7 +59,7 @@ public:
|
|||||||
askForFeedbackOnLogout: false
|
askForFeedbackOnLogout: false
|
||||||
# the default logoutUrl matches window.location.origin i.e. bigbluebutton.org for demo.bigbluebutton.org
|
# the default logoutUrl matches window.location.origin i.e. bigbluebutton.org for demo.bigbluebutton.org
|
||||||
# in some cases we want only custom logoutUrl to be used when provided on meeting create. Default value: true
|
# in some cases we want only custom logoutUrl to be used when provided on meeting create. Default value: true
|
||||||
askForConfirmationOnLeave: false
|
askForConfirmationOnLeave: true
|
||||||
wakeLock:
|
wakeLock:
|
||||||
enabled: true
|
enabled: true
|
||||||
allowDefaultLogoutUrl: true
|
allowDefaultLogoutUrl: true
|
||||||
@ -768,7 +768,7 @@ public:
|
|||||||
# e.g.: disableEmojis: ['grin','laughing']
|
# e.g.: disableEmojis: ['grin','laughing']
|
||||||
disableEmojis: []
|
disableEmojis: []
|
||||||
allowedElements: ['a', 'code', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'li', 'ol', 'ul', 'p', 'strong']
|
allowedElements: ['a', 'code', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'li', 'ol', 'ul', 'p', 'strong']
|
||||||
# available options for toolbar: reply, edit, delete
|
# available options for toolbar: reply, edit, delete, reactions
|
||||||
# example: ['reply', 'delete']
|
# example: ['reply', 'delete']
|
||||||
toolbar: []
|
toolbar: []
|
||||||
userReaction:
|
userReaction:
|
||||||
|
@ -45,6 +45,10 @@
|
|||||||
"app.chat.toolbar.reply": "Reply to message {0}",
|
"app.chat.toolbar.reply": "Reply to message {0}",
|
||||||
"app.chat.toolbar.edit": "Edit",
|
"app.chat.toolbar.edit": "Edit",
|
||||||
"app.chat.toolbar.delete": "Delete",
|
"app.chat.toolbar.delete": "Delete",
|
||||||
|
"app.chat.toolbar.reactions.reactedByLabel": "Reacted by",
|
||||||
|
"app.chat.toolbar.reactions.youLabel": "you",
|
||||||
|
"app.chat.toolbar.reactions.andLabel": "and",
|
||||||
|
"app.chat.toolbar.reactions.findReactionButtonLabel": "Find a reaction",
|
||||||
"app.timer.toolTipTimerStopped": "The timer has stopped.",
|
"app.timer.toolTipTimerStopped": "The timer has stopped.",
|
||||||
"app.timer.toolTipTimerRunning": "The timer is running.",
|
"app.timer.toolTipTimerRunning": "The timer is running.",
|
||||||
"app.timer.toolTipStopwatchStopped": "The stopwatch has stopped.",
|
"app.timer.toolTipStopwatchStopped": "The stopwatch has stopped.",
|
||||||
|
Loading…
Reference in New Issue
Block a user