[IMPROVE] Rewrite read receipts to react. #23455

pull/23474/head^2
gabriellsh 4 years ago committed by GitHub
parent d343604820
commit 363ea0137c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      app/message-read-receipt/client/index.js
  2. 16
      app/message-read-receipt/client/views/readReceipts.html
  3. 39
      app/message-read-receipt/client/views/readReceipts.js
  4. 2
      app/ui-utils/client/lib/MessageAction.js
  5. 2
      app/ui/client/lib/fileUpload.js
  6. 2
      client/components/Omnichannel/modals/ForwardChatModal.js
  7. 0
      client/components/Omnichannel/modals/ModalSeparator/ModalSeparator.tsx
  8. 0
      client/components/Omnichannel/modals/ModalSeparator/index.ts
  9. 0
      client/components/Omnichannel/modals/ModalSeparator/style.css
  10. 1
      client/components/modals/ReactionList/index.ts
  11. 2
      client/contexts/ServerContext/methods.ts
  12. 4
      client/contexts/ServerContext/methods/getReadReceipts.ts
  13. 12
      client/hooks/useUserDisplayName.ts
  14. 1
      client/importPackages.ts
  15. 7
      client/lib/getUserDisplayName.ts
  16. 17
      client/startup/readReceipt.ts
  17. 2
      client/views/room/modals/FileUploadModal/FilePreview.tsx
  18. 0
      client/views/room/modals/FileUploadModal/FileUploadModal.stories.js
  19. 4
      client/views/room/modals/FileUploadModal/FileUploadModal.tsx
  20. 2
      client/views/room/modals/FileUploadModal/GenericPreview.tsx
  21. 0
      client/views/room/modals/FileUploadModal/ImagePreview.tsx
  22. 2
      client/views/room/modals/FileUploadModal/MediaPreview.tsx
  23. 0
      client/views/room/modals/FileUploadModal/PreviewSkeleton.tsx
  24. 0
      client/views/room/modals/FileUploadModal/index.ts
  25. 8
      client/views/room/modals/ReactionListModal/ReactionListModal.tsx
  26. 2
      client/views/room/modals/ReactionListModal/ReactionUserTag.tsx
  27. 6
      client/views/room/modals/ReactionListModal/Reactions.tsx
  28. 1
      client/views/room/modals/ReactionListModal/index.ts
  29. 45
      client/views/room/modals/ReadReceiptsModal/ReadReceiptRow.tsx
  30. 52
      client/views/room/modals/ReadReceiptsModal/ReadReceiptsModal.tsx
  31. 1
      client/views/room/modals/ReadReceiptsModal/index.ts
  32. 12
      definition/ReadReceipt.ts

@ -1 +0,0 @@
import './views/readReceipts';

@ -1,16 +0,0 @@
<template name="readReceipts">
{{#if isLoading}}
{{> loading class="loading-animation--primary"}}
{{else}}
<p>{{_ "Read_by"}}:</p>
<ul class="read-receipts">
{{#each receipts}}
<li class="read-receipts__user background-transparent-dark-hover">
{{> avatar username=user.username}}
<div class="read-receipts__name color-primary-font-color">{{displayName}}</div>
<span class="read-receipts__time color-info-font-color" title="{{dateTime}}">{{time}}</span>
</li>
{{/each}}
</ul>
{{/if}}
</template>

@ -1,39 +0,0 @@
import { Meteor } from 'meteor/meteor';
import { ReactiveVar } from 'meteor/reactive-var';
import { Template } from 'meteor/templating';
import moment from 'moment';
import { settings } from '../../../settings';
import './readReceipts.html';
Template.readReceipts.helpers({
receipts() {
return Template.instance().readReceipts.get();
},
displayName() {
return (settings.get('UI_Use_Real_Name') && this.user.name) || this.user.username;
},
time() {
return moment(this.ts).format('L LTS');
},
isLoading() {
return Template.instance().loading.get();
},
});
Template.readReceipts.onCreated(function readReceiptsOnCreated() {
this.loading = new ReactiveVar(false);
this.readReceipts = new ReactiveVar([]);
});
Template.readReceipts.onRendered(function readReceiptsOnRendered() {
this.loading.set(true);
Meteor.call('getReadReceipts', { messageId: this.data.messageId }, (error, result) => {
if (!error) {
this.readReceipts.set(result);
}
this.loading.set(false);
});
});

@ -14,7 +14,7 @@ import { Messages, Rooms, Subscriptions } from '../../../models/client';
import { hasAtLeastOnePermission, hasPermission } from '../../../authorization/client';
import { modal } from './modal';
import { imperativeModal } from '../../../../client/lib/imperativeModal';
import ReactionList from '../../../../client/components/modals/ReactionList';
import ReactionList from '../../../../client/views/room/modals/ReactionListModal';
import { call } from '../../../../client/lib/utils/call';
import { canDeleteMessage } from '../../../../client/lib/utils/canDeleteMessage';
import { dispatchToastMessage } from '../../../../client/lib/toast';

@ -6,7 +6,7 @@ import { settings } from '../../../settings/client';
import { UserAction, USER_ACTIVITIES } from '../index';
import { fileUploadIsValidContentType, APIClient } from '../../../utils';
import { imperativeModal } from '../../../../client/lib/imperativeModal';
import FileUploadModal from '../../../../client/components/modals/FileUploadModal';
import FileUploadModal from '../../../../client/views/room/modals/FileUploadModal';
import { prependReplies } from '../../../../client/lib/utils/prependReplies';
export const uploadFileWithMessage = async (rid, tmid, { description, fileName, msg, file }) => {

@ -16,9 +16,9 @@ import { useTranslation } from '../../../contexts/TranslationContext';
import { useRecordList } from '../../../hooks/lists/useRecordList';
import { AsyncStatePhase } from '../../../hooks/useAsyncState';
import { useForm } from '../../../hooks/useForm';
import ModalSeparator from '../../ModalSeparator';
import UserAutoComplete from '../../UserAutoComplete';
import { useDepartmentsList } from '../hooks/useDepartmentsList';
import ModalSeparator from './ModalSeparator';
const ForwardChatModal = ({ onForward, onCancel, room, ...props }) => {
const t = useTranslation();

@ -1 +0,0 @@
export { default } from './ReactionList';

@ -1,6 +1,7 @@
import { IRoom } from '../../../definition/IRoom';
import { IUser } from '../../../definition/IUser';
import { FollowMessageMethod } from './methods/followMessage';
import { GetReadReceiptsMethod } from './methods/getReadReceipts';
import { UnsubscribeMethod as MailerUnsubscribeMethod } from './methods/mailer/unsubscribe';
import { RoomNameExistsMethod } from './methods/roomNameExists';
import { SaveRoomSettingsMethod } from './methods/saveRoomSettings';
@ -137,6 +138,7 @@ export type ServerMethods = {
'uploadCustomSound': (...args: any[]) => any;
'Mailer:unsubscribe': MailerUnsubscribeMethod;
'getRoomById': (rid: IRoom['_id']) => IRoom;
'getReadReceipts': GetReadReceiptsMethod;
};
export type ServerMethodName = keyof ServerMethods;

@ -0,0 +1,4 @@
import type { IMessage } from '../../../../definition/IMessage';
import type { ReadReceipt } from '../../../../definition/ReadReceipt';
export type GetReadReceiptsMethod = (options: { mid: IMessage['_id'] }) => Array<ReadReceipt>;

@ -0,0 +1,12 @@
import { IUser } from '../../definition/IUser';
import { useSetting } from '../contexts/SettingsContext';
import { getUserDisplayName } from '../lib/getUserDisplayName';
export const useUserDisplayName = ({
name,
username,
}: Pick<IUser, 'name' | 'username'>): string | undefined => {
const useRealName = useSetting('UI_Use_Real_Name');
return getUserDisplayName(name, username, !!useRealName);
};

@ -92,4 +92,3 @@ import '../app/livechat/client';
import '../app/meteor-autocomplete/client';
import '../app/theme/client';
import '../app/custom/client';
import '../app/message-read-receipt/client';

@ -0,0 +1,7 @@
import { IUser } from '../../definition/IUser';
export const getUserDisplayName = (
name: IUser['name'],
username: IUser['username'],
useRealName: boolean,
): string | undefined => (useRealName ? name || username : username);

@ -2,8 +2,9 @@ import { Meteor } from 'meteor/meteor';
import { Tracker } from 'meteor/tracker';
import { settings } from '../../app/settings/client';
import { modal, MessageAction, messageArgs } from '../../app/ui-utils/client';
import { t } from '../../app/utils/client';
import { MessageAction, messageArgs } from '../../app/ui-utils/client';
import { imperativeModal } from '../lib/imperativeModal';
import ReadReceiptsModal from '../views/room/modals/ReadReceiptsModal';
Meteor.startup(() => {
Tracker.autorun(() => {
@ -20,15 +21,9 @@ Meteor.startup(() => {
context: ['starred', 'message', 'message-mobile', 'threads'],
action() {
const { msg: message } = messageArgs(this);
modal.open({
title: t('Info'),
content: 'readReceipts',
data: {
messageId: message._id,
},
showConfirmButton: true,
showCancelButton: false,
confirmButtonText: t('Close'),
imperativeModal.open({
component: ReadReceiptsModal,
props: { messageId: message._id, onClose: imperativeModal.close },
});
},
order: 10,

@ -1,6 +1,6 @@
import React, { ReactElement } from 'react';
import { isIE11 } from '../../../lib/utils/isIE11';
import { isIE11 } from '../../../../lib/utils/isIE11';
import GenericPreview from './GenericPreview';
import MediaPreview from './MediaPreview';

@ -17,8 +17,8 @@ import React, {
useEffect,
} from 'react';
import { useToastMessageDispatch } from '../../../contexts/ToastMessagesContext';
import { useTranslation } from '../../../contexts/TranslationContext';
import { useToastMessageDispatch } from '../../../../contexts/ToastMessagesContext';
import { useTranslation } from '../../../../contexts/TranslationContext';
import FilePreview from './FilePreview';
type FilePreviewModalProps = {

@ -1,7 +1,7 @@
import { Box, Icon } from '@rocket.chat/fuselage';
import React, { ReactElement } from 'react';
import { formatBytes } from '../../../lib/utils/formatBytes';
import { formatBytes } from '../../../../lib/utils/formatBytes';
const GenericPreview = ({ file }: { file: File }): ReactElement => (
<Box display='flex' alignItems='center' w='full' fontScale='s2'>

@ -1,7 +1,7 @@
import { Box, Icon } from '@rocket.chat/fuselage';
import React, { ReactElement, useEffect, useState, memo } from 'react';
import { useTranslation } from '../../../contexts/TranslationContext';
import { useTranslation } from '../../../../contexts/TranslationContext';
import { FilePreviewType } from './FilePreview';
import ImagePreview from './ImagePreview';
import PreviewSkeleton from './PreviewSkeleton';

@ -1,10 +1,10 @@
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import React, { ReactElement } from 'react';
import { openUserCard } from '../../../../app/ui/client/lib/UserCard';
import { IUser } from '../../../../definition/IUser';
import { useTranslation } from '../../../contexts/TranslationContext';
import GenericModal from '../../GenericModal';
import { openUserCard } from '../../../../../app/ui/client/lib/UserCard';
import { IUser } from '../../../../../definition/IUser';
import GenericModal from '../../../../components/GenericModal';
import { useTranslation } from '../../../../contexts/TranslationContext';
import Reactions from './Reactions';
type ReactionListProps = {

@ -1,7 +1,7 @@
import { Box, Tag } from '@rocket.chat/fuselage';
import React, { ReactElement } from 'react';
import { IUser } from '../../../../definition/IUser';
import { IUser } from '../../../../../definition/IUser';
type ReactionUserTag = {
username: IUser['username'];

@ -1,9 +1,9 @@
import { Box } from '@rocket.chat/fuselage';
import React, { ReactElement } from 'react';
import { IUser } from '../../../../definition/IUser';
import { useSetting } from '../../../contexts/SettingsContext';
import Emoji from '../../Emoji';
import { IUser } from '../../../../../definition/IUser';
import Emoji from '../../../../components/Emoji';
import { useSetting } from '../../../../contexts/SettingsContext';
import ReactionUserTag from './ReactionUserTag';
type ReactionsProps = {

@ -0,0 +1 @@
export { default } from './ReactionListModal';

@ -0,0 +1,45 @@
import { css } from '@rocket.chat/css-in-js';
import { Box } from '@rocket.chat/fuselage';
import colors from '@rocket.chat/fuselage-tokens/colors';
import React, { ReactElement } from 'react';
import type { ReadReceipt } from '../../../../../definition/ReadReceipt';
import UserAvatar from '../../../../components/avatar/UserAvatar';
import { useFormatDateAndTime } from '../../../../hooks/useFormatDateAndTime';
import { useUserDisplayName } from '../../../../hooks/useUserDisplayName';
const hoverStyle = css`
&:hover {
background-color: ${colors.n400};
}
`;
const ReadReceiptRow = ({ user, ts }: ReadReceipt): ReactElement => {
const displayName = useUserDisplayName(user);
const formatDateAndTime = useFormatDateAndTime();
return (
<Box
display='flex'
flexDirection='row'
justifyContent='space-between'
alignItems='center'
p='x4'
pi='x32'
mi='neg-x32'
className={hoverStyle}
>
<Box>
<UserAvatar username={user.username || ''} size='x24' />
<Box is='span' mis='x8'>
{displayName}
</Box>
</Box>
<Box is='span' fontScale='c1' color='info'>
{formatDateAndTime(ts)}
</Box>
</Box>
);
};
export default ReadReceiptRow;

@ -0,0 +1,52 @@
import { Skeleton } from '@rocket.chat/fuselage';
import React, { ReactElement, useMemo, useEffect } from 'react';
import type { IMessage } from '../../../../../definition/IMessage/IMessage';
import type { ReadReceipt } from '../../../../../definition/ReadReceipt';
import GenericModal from '../../../../components/GenericModal';
import { useToastMessageDispatch } from '../../../../contexts/ToastMessagesContext';
import { useTranslation } from '../../../../contexts/TranslationContext';
import { useMethodData } from '../../../../hooks/useMethodData';
import { AsyncStatePhase } from '../../../../lib/asyncState';
import ReadReceiptRow from './ReadReceiptRow';
type ReadReceiptsModalProps = {
messageId: IMessage['_id'];
onClose: () => void;
};
const ReadReceiptsModal = ({ messageId, onClose }: ReadReceiptsModalProps): ReactElement => {
const t = useTranslation();
const dispatchToastMessage = useToastMessageDispatch();
const { phase, value, error } = useMethodData<Array<ReadReceipt>>(
'getReadReceipts',
useMemo(() => [{ messageId }], [messageId]),
);
useEffect(() => {
if (error) {
dispatchToastMessage({ type: 'error', message: error });
onClose();
}
}, [error, dispatchToastMessage, t, onClose]);
if (phase === AsyncStatePhase.LOADING || !value || error) {
return (
<GenericModal title={t('Read_by')} onConfirm={onClose} onClose={onClose}>
<Skeleton type='rect' w='full' h='x120' />
</GenericModal>
);
}
return (
<GenericModal title={t('Read_by')} onConfirm={onClose} onClose={onClose}>
{value.length < 1 && t('No_results_found')}
{value.map((receipt) => (
<ReadReceiptRow {...receipt} key={receipt._id} />
))}
</GenericModal>
);
};
export default ReadReceiptsModal;

@ -0,0 +1 @@
export { default } from './ReadReceiptsModal';

@ -0,0 +1,12 @@
import type { IMessage } from './IMessage/IMessage';
import type { IRoom } from './IRoom';
import type { IUser } from './IUser';
export type ReadReceipt = {
messageId: IMessage['_id'];
roomId: IRoom['_id'];
ts: Date;
user: Pick<IUser, '_id' | 'name' | 'username'>;
userId: IUser['_id'];
_id: string;
}
Loading…
Cancel
Save