refactor of typing indicator using graphql

This commit is contained in:
Ramón Souza 2023-04-25 15:37:26 -03:00
parent 13428a99df
commit 6ef4781ad1
4 changed files with 234 additions and 2 deletions

View File

@ -0,0 +1,127 @@
import React from 'react';
import { Meteor } from 'meteor/meteor';
import { useSubscription } from '@apollo/client';
import {
IS_TYPING_SUBSCRIPTION,
} from '../queries';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { User } from '/imports/ui/Types/user';
import Styled from './styles';
interface TypingIndicatorProps {
typingUsers: Array<User>,
indicatorEnabled: boolean,
intl: object,
error: string,
}
const messages = defineMessages({
severalPeople: {
id: 'app.chat.multi.typing',
description: 'displayed when 4 or more users are typing',
},
});
const CHAT_CONFIG = Meteor.settings.public.chat;
const TYPING_INDICATOR_ENABLED = CHAT_CONFIG.typingIndicator.enabled;
const TypingIndicator: React.FC<TypingIndicatorProps> = ({
typingUsers,
indicatorEnabled,
intl,
error,
}) => {
console.log({typingUsers})
if (!indicatorEnabled || !typingUsers) return null;
const { length } = typingUsers;
const isSingleTyper = length === 1;
const isCoupleTyper = length === 2;
const isMultiTypers = length > 2;
let element = null;
if (isSingleTyper) {
const name = typingUsers[0]?.name;
element = (
<FormattedMessage
id="app.chat.one.typing"
description="label used when one user is typing"
values={{
0: <Styled.SingleTyper>
{`${name}`}
&nbsp;
</Styled.SingleTyper>,
}}
/>
);
}
if (isCoupleTyper) {
const name = typingUsers[0]?.name;
const name2 = typingUsers[1]?.name;
element = (
<FormattedMessage
id="app.chat.two.typing"
description="label used when two users are typing"
values={{
0: <Styled.CoupleTyper>
{`${name}`}
&nbsp;
</Styled.CoupleTyper>,
1: <Styled.CoupleTyper>
&nbsp;
{`${name2}`}
&nbsp;
</Styled.CoupleTyper>,
}}
/>
);
}
if (isMultiTypers) {
element = (
<span>
{`${intl.formatMessage(messages.severalPeople)}`}
</span>
);
}
return (
<Styled.TypingIndicatorWrapper
error={!!error}
info={!error}
spacer={!!element}
>
<Styled.TypingIndicator data-test="typingIndicator">{error || element}</Styled.TypingIndicator>
</Styled.TypingIndicatorWrapper>
);
};
const TypingIndicatorContainer: React.FC = ({ userId, isTypingTo, error }) => {
const intl = useIntl();
const {
data: typingUsersData,
} = useSubscription(IS_TYPING_SUBSCRIPTION, {
variables: {
userId,
chatId: isTypingTo,
}
});
const typingUsers = typingUsersData?.user_typing_public || [];
const typingUsersArray = typingUsers.map(user => user.user);
return <TypingIndicator
typingUsers={typingUsersArray}
indicatorEnabled={TYPING_INDICATOR_ENABLED}
intl={intl}
error={error}
/>
};
export default TypingIndicatorContainer;

View File

@ -0,0 +1,73 @@
import styled from 'styled-components';
import { colorDanger, colorGrayDark } from '/imports/ui/stylesheets/styled-components/palette';
import { borderSize } from '/imports/ui/stylesheets/styled-components/general';
import { fontSizeSmaller, fontSizeBase } from '/imports/ui/stylesheets/styled-components/typography';
const SingleTyper = styled.span`
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-weight: bold;
font-size: ${fontSizeSmaller};
max-width: 70%;
`;
const CoupleTyper = styled.span`
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-weight: bold;
font-size: ${fontSizeSmaller};
max-width: 25%;
`;
const TypingIndicator = styled.span`
display: flex;
flex-direction: row;
> span {
display: block;
margin-right: 0.05rem;
margin-left: 0.05rem;
}
text-align: left;
[dir="rtl"] & {
text-align: right;
}
`;
const TypingIndicatorWrapper = styled.div`
${({ error }) => error && `
color: ${colorDanger};
font-size: calc(${fontSizeBase} * .75);
color: ${colorGrayDark};
text-align: left;
padding: ${borderSize} 0;
position: relative;
height: .93rem;
max-height: .93rem;
`}
${({ info }) => info && `
font-size: calc(${fontSizeBase} * .75);
color: ${colorGrayDark};
text-align: left;
padding: ${borderSize} 0;
position: relative;
height: .93rem;
max-height: .93rem;
`}
${({ spacer }) => spacer && `
height: .93rem;
max-height: .93rem;
`}
`;
export default {
SingleTyper,
CoupleTyper,
TypingIndicator,
TypingIndicatorWrapper,
};

View File

@ -0,0 +1,25 @@
import { gql } from '@apollo/client';
export const IS_TYPING_SUBSCRIPTION = gql`subscription IsTyping($userId: String!, $chatId: String!) {
user_typing_public(
limit: 3,
where: {
isCurrentlyTyping: {_eq: true}
chatId: {_eq: $chatId}
userId: {_neq: $userId}
}
) {
meetingId
chatId
userId
typingAt
isCurrentlyTyping
user {
name
}
}
}`;
export default {
IS_TYPING_SUBSCRIPTION,
};

View File

@ -4,7 +4,8 @@ import { checkText } from 'smile2emoji';
import deviceInfo from '/imports/utils/deviceInfo';
import PropTypes from 'prop-types';
import { throttle } from '/imports/utils/throttle';
import TypingIndicatorContainer from './typing-indicator/container';
import TypingIndicatorContainer from '/imports/ui/components/chat/chat-graphql/chat-typing-indicator/component';
import Auth from '/imports/ui/services/auth';
import ClickOutside from '/imports/ui/components/click-outside/component';
import Styled from './styles';
import { escapeHtml } from '/imports/utils/string-utils';
@ -70,6 +71,8 @@ const messages = defineMessages({
const CHAT_CONFIG = Meteor.settings.public.chat;
const AUTO_CONVERT_EMOJI = Meteor.settings.public.chat.autoConvertEmoji;
const ENABLE_EMOJI_PICKER = Meteor.settings.public.chat.emojiPicker.enable;
const PUBLIC_CHAT_KEY = CHAT_CONFIG.public_id;
const PUBLIC_CHAT_GROUP_KEY = CHAT_CONFIG.public_group_id;
class MessageForm extends PureComponent {
constructor(props) {
@ -371,7 +374,11 @@ class MessageForm extends PureComponent {
data-test="sendMessageButton"
/>
</Styled.Wrapper>
<TypingIndicatorContainer {...{ idChatOpen, error }} />
<TypingIndicatorContainer
{...{ idChatOpen, error }}
isTypingTo={idChatOpen === PUBLIC_CHAT_KEY ? PUBLIC_CHAT_GROUP_KEY : Auth.userID}
userId={Auth.userID}
/>
</Styled.Form>
);
}