Chore: Evaluate Message - TypeScript (#3944)

Co-authored-by: GleidsonDaniel <gleidson10daniel@hotmail.com>
This commit is contained in:
Alex Junior 2022-04-01 18:52:38 -03:00 committed by GitHub
parent 6ad4ccd4bd
commit 488074b4ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 271 additions and 358 deletions

View File

@ -1,8 +1,7 @@
import { Action } from 'redux';
import { MESSAGES } from './actionsTypes';
type IMessage = Record<string, string>;
import { IMessage } from '../definitions';
interface IReplyBroadcast extends Action {
message: IMessage;

View File

@ -1,6 +1,6 @@
export interface IUserMention {
_id: string;
username: string;
username?: string;
name?: string;
type?: string;
}

View File

@ -2,7 +2,7 @@ import React, { useContext } from 'react';
import { dequal } from 'dequal';
import { Text } from 'react-native';
import { IMessageAttachments, IMessageAttachedActions } from './interfaces';
import { IMessageAttachments } from './interfaces';
import Image from './Image';
import Audio from './Audio';
import Video from './Video';
@ -23,7 +23,7 @@ export type TElement = {
text: string;
};
const AttachedActions = ({ attachment }: IMessageAttachedActions) => {
const AttachedActions = ({ attachment }: { attachment: IAttachment }) => {
if (!attachment.actions) {
return null;
}
@ -55,8 +55,7 @@ const AttachedActions = ({ attachment }: IMessageAttachedActions) => {
);
};
const Attachments = React.memo(
// @ts-ignore
const Attachments: React.FC<IMessageAttachments> = React.memo(
({ attachments, timeFormat, showAttachment, style, getCustomEmoji, isReply }: IMessageAttachments) => {
if (!attachments || attachments.length === 0) {
return null;
@ -64,7 +63,7 @@ const Attachments = React.memo(
const { theme } = useTheme();
return attachments.map((file: IAttachment, index: number) => {
const attachmentsElements = attachments.map((file: IAttachment, index: number) => {
if (file && file.image_url) {
return (
<Image
@ -93,7 +92,6 @@ const Attachments = React.memo(
getCustomEmoji={getCustomEmoji}
style={style}
isReply={isReply}
theme={theme}
/>
);
}
@ -109,6 +107,7 @@ const Attachments = React.memo(
return <Reply key={index} index={index} attachment={file} timeFormat={timeFormat} getCustomEmoji={getCustomEmoji} />;
});
return <>{attachmentsElements}</>;
},
(prevProps, nextProps) => dequal(prevProps.attachments, nextProps.attachments)
);

View File

@ -1,10 +1,11 @@
import React from 'react';
import { StyleProp, StyleSheet, Text, TextStyle, View } from 'react-native';
import { Audio } from 'expo-av';
import { Audio, AVPlaybackStatus } from 'expo-av';
import Slider from '@react-native-community/slider';
import moment from 'moment';
import { dequal } from 'dequal';
import { activateKeepAwake, deactivateKeepAwake } from 'expo-keep-awake';
import { Sound } from 'expo-av/build/Audio/Sound';
import Touchable from './Touchable';
import Markdown from '../markdown';
@ -23,7 +24,7 @@ interface IButton {
paused: boolean;
theme: string;
disabled?: boolean;
onPress: Function;
onPress: () => void;
}
interface IMessageAudioProps {
@ -108,7 +109,7 @@ Button.displayName = 'MessageAudioButton';
class MessageAudio extends React.Component<IMessageAudioProps, IMessageAudioState> {
static contextType = MessageContext;
private sound: any;
private sound: Sound;
constructor(props: IMessageAudioProps) {
super(props);
@ -141,7 +142,7 @@ class MessageAudio extends React.Component<IMessageAudioProps, IMessageAudioStat
this.setState({ loading: false });
}
shouldComponentUpdate(nextProps: any, nextState: any) {
shouldComponentUpdate(nextProps: IMessageAudioProps, nextState: IMessageAudioState) {
const { currentTime, duration, paused, loading } = this.state;
const { file, theme } = this.props;
if (nextProps.theme !== theme) {
@ -182,7 +183,7 @@ class MessageAudio extends React.Component<IMessageAudioProps, IMessageAudioStat
}
}
onPlaybackStatusUpdate = (status: any) => {
onPlaybackStatusUpdate = (status: AVPlaybackStatus) => {
if (status) {
this.onLoad(status);
this.onProgress(status);
@ -190,20 +191,25 @@ class MessageAudio extends React.Component<IMessageAudioProps, IMessageAudioStat
}
};
onLoad = (data: any) => {
onLoad = (data: AVPlaybackStatus) => {
if (data.isLoaded && data.durationMillis) {
const duration = data.durationMillis / 1000;
this.setState({ duration: duration > 0 ? duration : 0 });
}
};
onProgress = (data: any) => {
onProgress = (data: AVPlaybackStatus) => {
if (data.isLoaded) {
const { duration } = this.state;
const currentTime = data.positionMillis / 1000;
if (currentTime <= duration) {
this.setState({ currentTime });
}
}
};
onEnd = async (data: any) => {
onEnd = async (data: AVPlaybackStatus) => {
if (data.isLoaded) {
if (data.didJustFinish) {
try {
await this.sound.stopAsync();
@ -212,6 +218,7 @@ class MessageAudio extends React.Component<IMessageAudioProps, IMessageAudioStat
// do nothing
}
}
}
};
get duration() {
@ -238,7 +245,7 @@ class MessageAudio extends React.Component<IMessageAudioProps, IMessageAudioStat
}
};
onValueChange = async (value: any) => {
onValueChange = async (value: number) => {
try {
this.setState({ currentTime: value });
await this.sound.setPositionAsync(value * 1000);

View File

@ -8,7 +8,8 @@ const Blocks = React.memo(({ blocks, id: mid, rid, blockAction }: IMessageBlocks
const appId = blocks[0]?.appId || '';
return React.createElement(
messageBlockWithContext({
action: async ({ actionId, value, blockId }: any) => {
action: async ({ actionId, value, blockId }: { actionId: string; value: string; blockId: string }) => {
if (blockAction) {
await blockAction({
actionId,
appId,
@ -17,6 +18,7 @@ const Blocks = React.memo(({ blocks, id: mid, rid, blockAction }: IMessageBlocks
rid,
mid
});
}
},
appId,
rid

View File

@ -9,10 +9,13 @@ import I18n from '../../i18n';
import { themes } from '../../constants/colors';
import MessageContext from './Context';
import { IMessageBroadcast } from './interfaces';
import { useTheme } from '../../theme';
const Broadcast = React.memo(({ author, broadcast, theme }: IMessageBroadcast) => {
const Broadcast = React.memo(({ author, broadcast }: IMessageBroadcast) => {
const { user, replyBroadcast } = useContext(MessageContext);
const isOwn = author._id === user.id;
const { theme } = useTheme();
const isOwn = author?._id === user.id;
if (broadcast && !isOwn) {
return (
<View style={styles.buttonContainer}>

View File

@ -8,8 +8,11 @@ import I18n from '../../i18n';
import { CustomIcon } from '../../lib/Icons';
import { themes } from '../../constants/colors';
import { IMessageCallButton } from './interfaces';
import { useTheme } from '../../theme';
const CallButton = React.memo(({ theme, callJitsi }: IMessageCallButton) => (
const CallButton = React.memo(({ callJitsi }: IMessageCallButton) => {
const { theme } = useTheme();
return (
<View style={styles.buttonContainer}>
<Touchable
onPress={callJitsi}
@ -22,7 +25,8 @@ const CallButton = React.memo(({ theme, callJitsi }: IMessageCallButton) => (
</>
</Touchable>
</View>
));
);
});
CallButton.displayName = 'CallButton';

View File

@ -12,15 +12,17 @@ import MessageContext from './Context';
import Encrypted from './Encrypted';
import { E2E_MESSAGE_TYPE } from '../../lib/encryption/constants';
import { IMessageContent } from './interfaces';
import { useTheme } from '../../theme';
const Content = React.memo(
(props: IMessageContent) => {
const { theme } = useTheme();
if (props.isInfo) {
// @ts-ignore
const infoMessage = getInfoMessage({ ...props });
const renderMessageContent = (
<Text style={[styles.textInfo, { color: themes[props.theme].auxiliaryText }]} accessibilityLabel={infoMessage}>
<Text style={[styles.textInfo, { color: themes[theme].auxiliaryText }]} accessibilityLabel={infoMessage}>
{infoMessage}
</Text>
);
@ -36,14 +38,12 @@ const Content = React.memo(
return renderMessageContent;
}
const isPreview: any = props.tmid && !props.isThreadRoom;
const isPreview = props.tmid && !props.isThreadRoom;
let content = null;
if (props.isEncrypted) {
content = (
<Text
style={[styles.textInfo, { color: themes[props.theme].auxiliaryText }]}
accessibilityLabel={I18n.t('Encrypted_message')}>
<Text style={[styles.textInfo, { color: themes[theme].auxiliaryText }]} accessibilityLabel={I18n.t('Encrypted_message')}>
{I18n.t('Encrypted_message')}
</Text>
);
@ -65,7 +65,7 @@ const Content = React.memo(
navToRoomInfo={props.navToRoomInfo}
tmid={props.tmid}
useRealName={props.useRealName}
theme={props.theme}
theme={theme}
onLinkPress={onLinkPress}
/>
);
@ -76,13 +76,13 @@ const Content = React.memo(
content = (
<View style={styles.flex}>
<View style={styles.contentContainer}>{content}</View>
<Encrypted type={props.type} theme={props.theme} />
<Encrypted type={props.type} />
</View>
);
}
if (props.isIgnored) {
content = <Text style={[styles.textInfo, { color: themes[props.theme].auxiliaryText }]}>{I18n.t('Message_Ignored')}</Text>;
content = <Text style={[styles.textInfo, { color: themes[theme].auxiliaryText }]}>{I18n.t('Message_Ignored')}</Text>;
}
return <View style={props.isTemp && styles.temp}>{content}</View>;
@ -97,9 +97,6 @@ const Content = React.memo(
if (prevProps.type !== nextProps.type) {
return false;
}
if (prevProps.theme !== nextProps.theme) {
return false;
}
if (prevProps.isEncrypted !== nextProps.isEncrypted) {
return false;
}

View File

@ -10,10 +10,12 @@ import { DISCUSSION } from './constants';
import { themes } from '../../constants/colors';
import MessageContext from './Context';
import { formatDateThreads } from '../../utils/room';
import { IMessageDiscussion } from './interfaces';
import { IMessage } from '../../definitions';
import { useTheme } from '../../theme';
const Discussion = React.memo(
({ msg, dcount, dlm, theme }: IMessageDiscussion) => {
({ msg, dcount, dlm }: Pick<IMessage, 'msg' | 'dcount' | 'dlm'>) => {
const { theme } = useTheme();
let time;
if (dlm) {
time = formatDateThreads(dlm);
@ -50,9 +52,6 @@ const Discussion = React.memo(
if (prevProps.dlm !== nextProps.dlm) {
return false;
}
if (prevProps.theme !== nextProps.theme) {
return false;
}
return true;
}
);

View File

@ -7,13 +7,10 @@ import { themes } from '../../constants/colors';
import { BUTTON_HIT_SLOP } from './utils';
import MessageContext from './Context';
import styles from './styles';
import { useTheme } from '../../theme';
interface IMessageEncrypted {
type: string;
theme: string;
}
const Encrypted = React.memo(({ type, theme }: IMessageEncrypted) => {
const Encrypted = React.memo(({ type }: { type: string }) => {
const { theme } = useTheme();
if (type !== E2E_MESSAGE_TYPE) {
return null;
}

View File

@ -12,25 +12,20 @@ import { formatAttachmentUrl } from '../../lib/utils';
import { themes } from '../../constants/colors';
import MessageContext from './Context';
import { TGetCustomEmoji } from '../../definitions/IEmoji';
import { useTheme } from '../../theme';
import { IAttachment } from '../../definitions';
import { useTheme } from '../../theme';
type TMessageButton = {
children: JSX.Element;
interface IMessageButton {
children: React.ReactElement;
disabled?: boolean;
onPress: Function;
onPress: () => void;
theme: string;
};
type TMessageImage = {
img: string;
theme: string;
};
}
interface IMessageImage {
file: IAttachment;
imageUrl?: string;
showAttachment?: Function;
showAttachment?: (file: IAttachment) => void;
style?: StyleProp<TextStyle>[];
isReply?: boolean;
getCustomEmoji?: TGetCustomEmoji;
@ -38,7 +33,7 @@ interface IMessageImage {
const ImageProgress = createImageProgress(FastImage);
const Button = React.memo(({ children, onPress, disabled, theme }: TMessageButton) => (
const Button = React.memo(({ children, onPress, disabled, theme }: IMessageButton) => (
<Touchable
disabled={disabled}
onPress={onPress}
@ -48,10 +43,10 @@ const Button = React.memo(({ children, onPress, disabled, theme }: TMessageButto
</Touchable>
));
export const MessageImage = React.memo(({ img, theme }: TMessageImage) => (
export const MessageImage = React.memo(({ imgUri, theme }: { imgUri: string; theme: string }) => (
<ImageProgress
style={[styles.image, { borderColor: themes[theme].borderColor }]}
source={{ uri: encodeURI(img) }}
source={{ uri: encodeURI(imgUri) }}
resizeMode={FastImage.resizeMode.cover}
indicator={Progress.Pie}
indicatorProps={{
@ -65,6 +60,7 @@ const ImageContainer = React.memo(
const { theme } = useTheme();
const { baseUrl, user } = useContext(MessageContext);
const img = imageUrl || formatAttachmentUrl(file.image_url, user.id, user.token, baseUrl);
if (!img) {
return null;
}
@ -89,7 +85,7 @@ const ImageContainer = React.memo(
getCustomEmoji={getCustomEmoji}
theme={theme}
/>
<MessageImage img={img} theme={theme} />
<MessageImage imgUri={img} theme={theme} />
</View>
</Button>
);
@ -97,7 +93,7 @@ const ImageContainer = React.memo(
return (
<Button disabled={isReply} theme={theme} onPress={onPress}>
<MessageImage img={img} theme={theme} />
<MessageImage imgUri={img} theme={theme} />
</Button>
);
},

View File

@ -19,6 +19,7 @@ import ReadReceipt from './ReadReceipt';
import CallButton from './CallButton';
import { themes } from '../../constants/colors';
import { IMessage, IMessageInner, IMessageTouchable } from './interfaces';
import { useTheme } from '../../theme';
const MessageInner = React.memo((props: IMessageInner) => {
const { attachments } = props;
@ -85,7 +86,6 @@ const Message = React.memo((props: IMessage) => {
<View style={[styles.container, props.style]}>
{thread}
<View style={styles.flex}>
{/* @ts-ignore */}
<MessageAvatar small {...props} />
<View style={[styles.messageContent, props.isHeader && styles.messageContentWithHeader]}>
<Content {...props} />
@ -98,12 +98,11 @@ const Message = React.memo((props: IMessage) => {
return (
<View style={[styles.container, props.style]}>
<View style={styles.flex}>
{/* @ts-ignore */}
<MessageAvatar {...props} />
<View style={[styles.messageContent, props.isHeader && styles.messageContentWithHeader]}>
<MessageInner {...props} />
</View>
<ReadReceipt isReadReceiptEnabled={props.isReadReceiptEnabled} unread={props.unread || false} theme={props.theme} />
<ReadReceipt isReadReceiptEnabled={props.isReadReceiptEnabled} unread={props.unread || false} />
</View>
</View>
);
@ -119,12 +118,14 @@ const MessageTouchable = React.memo((props: IMessageTouchable & IMessage) => {
);
}
const { onPress, onLongPress } = useContext(MessageContext);
const { theme } = useTheme();
return (
<Touchable
onLongPress={onLongPress}
onPress={onPress}
disabled={(props.isInfo && !props.isThreadReply) || props.archived || props.isTemp || props.type === 'jitsi_call_started'}
style={{ backgroundColor: props.highlighted ? themes[props.theme].headerBackground : null }}>
style={{ backgroundColor: props.highlighted ? themes[theme].headerBackground : null }}>
<View>
<Message {...props} />
</View>

View File

@ -6,14 +6,12 @@ import styles from './styles';
import { BUTTON_HIT_SLOP } from './utils';
import { themes } from '../../constants/colors';
import MessageContext from './Context';
interface IMessageError {
hasError: boolean;
theme: string;
}
import { useTheme } from '../../theme';
const MessageError = React.memo(
({ hasError, theme }: IMessageError) => {
({ hasError }: { hasError: boolean }) => {
const { theme } = useTheme();
if (!hasError) {
return null;
}
@ -24,7 +22,7 @@ const MessageError = React.memo(
</Touchable>
);
},
(prevProps, nextProps) => prevProps.hasError === nextProps.hasError && prevProps.theme === nextProps.theme
(prevProps, nextProps) => prevProps.hasError === nextProps.hasError
);
MessageError.displayName = 'MessageError';

View File

@ -7,30 +7,28 @@ import styles from './styles';
import Emoji from './Emoji';
import { BUTTON_HIT_SLOP } from './utils';
import { themes } from '../../constants/colors';
import { withTheme } from '../../theme';
import { useTheme } from '../../theme';
import MessageContext from './Context';
import { TGetCustomEmoji } from '../../definitions/IEmoji';
interface IMessageAddReaction {
theme: string;
interface IReaction {
_id: string;
emoji: string;
usernames: string[];
}
interface IMessageReaction {
reaction: {
usernames: [];
emoji: object;
};
reaction: IReaction;
getCustomEmoji: TGetCustomEmoji;
theme: string;
}
interface IMessageReactions {
reactions?: object[];
reactions?: IReaction[];
getCustomEmoji: TGetCustomEmoji;
theme: string;
}
const AddReaction = React.memo(({ theme }: IMessageAddReaction) => {
const AddReaction = React.memo(({ theme }: { theme: string }) => {
const { reactionInit } = useContext(MessageContext);
return (
<Touchable
@ -49,7 +47,7 @@ const AddReaction = React.memo(({ theme }: IMessageAddReaction) => {
const Reaction = React.memo(({ reaction, getCustomEmoji, theme }: IMessageReaction) => {
const { onReactionPress, onReactionLongPress, baseUrl, user } = useContext(MessageContext);
const reacted = reaction.usernames.findIndex((item: IMessageReaction) => item === user.username) !== -1;
const reacted = reaction.usernames.findIndex((item: string) => item === user.username) !== -1;
return (
<Touchable
onPress={() => onReactionPress(reaction.emoji)}
@ -76,13 +74,15 @@ const Reaction = React.memo(({ reaction, getCustomEmoji, theme }: IMessageReacti
);
});
const Reactions = React.memo(({ reactions, getCustomEmoji, theme }: IMessageReactions) => {
const Reactions = React.memo(({ reactions, getCustomEmoji }: IMessageReactions) => {
const { theme } = useTheme();
if (!Array.isArray(reactions) || reactions.length === 0) {
return null;
}
return (
<View style={styles.reactionsContainer}>
{reactions.map((reaction: any) => (
{reactions.map(reaction => (
<Reaction key={reaction.emoji} reaction={reaction} getCustomEmoji={getCustomEmoji} theme={theme} />
))}
<AddReaction theme={theme} />
@ -94,4 +94,4 @@ Reaction.displayName = 'MessageReaction';
Reactions.displayName = 'MessageReactions';
AddReaction.displayName = 'MessageAddReaction';
export default withTheme(Reactions);
export default Reactions;

View File

@ -3,14 +3,10 @@ import React from 'react';
import { themes } from '../../constants/colors';
import { CustomIcon } from '../../lib/Icons';
import styles from './styles';
import { useTheme } from '../../theme';
interface IMessageReadReceipt {
isReadReceiptEnabled: boolean;
unread: boolean;
theme: string;
}
const ReadReceipt = React.memo(({ isReadReceiptEnabled, unread, theme }: IMessageReadReceipt) => {
const ReadReceipt = React.memo(({ isReadReceiptEnabled, unread }: { isReadReceiptEnabled?: boolean; unread: boolean }) => {
const { theme } = useTheme();
if (isReadReceiptEnabled && !unread && unread !== null) {
return <CustomIcon name='check' color={themes[theme].tintColor} size={15} style={styles.readReceipt} />;
}

View File

@ -7,15 +7,18 @@ import { themes } from '../../constants/colors';
import I18n from '../../i18n';
import { MarkdownPreview } from '../markdown';
import { IMessageRepliedThread } from './interfaces';
import { useTheme } from '../../theme';
const RepliedThread = memo(({ tmid, tmsg, isHeader, fetchThreadName, id, isEncrypted }: IMessageRepliedThread) => {
const { theme } = useTheme();
const RepliedThread = memo(({ tmid, tmsg, isHeader, fetchThreadName, id, isEncrypted, theme }: IMessageRepliedThread) => {
if (!tmid || !isHeader) {
return null;
}
const [msg, setMsg] = useState(isEncrypted ? I18n.t('Encrypted_message') : tmsg);
const fetch = async () => {
const threadName = await fetchThreadName(tmid, id);
const threadName = fetchThreadName ? await fetchThreadName(tmid, id) : '';
setMsg(threadName);
};

View File

@ -77,7 +77,7 @@ const styles = StyleSheet.create({
marginBottom: 4
},
image: {
// @ts-ignore
// @ts-ignore TODO - check with the team, change this to undefined
width: null,
height: 200,
flex: 1,
@ -93,24 +93,6 @@ const styles = StyleSheet.create({
}
});
interface IMessageTitle {
attachment: IAttachment;
timeFormat?: string;
theme: string;
}
interface IMessageDescription {
attachment: IAttachment;
getCustomEmoji: TGetCustomEmoji;
theme: string;
}
interface IMessageFields {
attachment: IAttachment;
theme: string;
getCustomEmoji: TGetCustomEmoji;
}
interface IMessageReply {
attachment: IAttachment;
timeFormat?: string;
@ -118,7 +100,7 @@ interface IMessageReply {
getCustomEmoji: TGetCustomEmoji;
}
const Title = React.memo(({ attachment, timeFormat, theme }: IMessageTitle) => {
const Title = React.memo(({ attachment, timeFormat, theme }: { attachment: IAttachment; timeFormat?: string; theme: string }) => {
const time = attachment.message_link && attachment.ts ? moment(attachment.ts).format(timeFormat) : null;
return (
<View style={styles.authorContainer}>
@ -132,7 +114,7 @@ const Title = React.memo(({ attachment, timeFormat, theme }: IMessageTitle) => {
});
const Description = React.memo(
({ attachment, getCustomEmoji, theme }: IMessageDescription) => {
({ attachment, getCustomEmoji, theme }: { attachment: IAttachment; getCustomEmoji: TGetCustomEmoji; theme: string }) => {
const text = attachment.text || attachment.title;
if (!text) {
return null;
@ -164,7 +146,7 @@ const Description = React.memo(
);
const UrlImage = React.memo(
({ image }: any) => {
({ image }: { image?: string }) => {
if (!image) {
return null;
}
@ -176,7 +158,7 @@ const UrlImage = React.memo(
);
const Fields = React.memo(
({ attachment, theme, getCustomEmoji }: IMessageFields) => {
({ attachment, theme, getCustomEmoji }: { attachment: IAttachment; theme: string; getCustomEmoji: TGetCustomEmoji }) => {
if (!attachment.fields) {
return null;
}
@ -206,12 +188,12 @@ const Fields = React.memo(
const Reply = React.memo(
({ attachment, timeFormat, index, getCustomEmoji }: IMessageReply) => {
const [loading, setLoading] = useState(false);
const { theme } = useTheme();
if (!attachment) {
return null;
}
const { theme } = useTheme();
const { baseUrl, user, jumpToMessage } = useContext(MessageContext);
const onPress = async () => {

View File

@ -7,9 +7,12 @@ import MessageContext from './Context';
import ThreadDetails from '../ThreadDetails';
import I18n from '../../i18n';
import { IMessageThread } from './interfaces';
import { useTheme } from '../../theme';
const Thread = React.memo(
({ msg, tcount, tlm, isThreadRoom, theme, id }: IMessageThread) => {
({ msg, tcount, tlm, isThreadRoom, id }: IMessageThread) => {
const { theme } = useTheme();
if (!tlm || isThreadRoom || tcount === 0) {
return null;
}
@ -38,9 +41,6 @@ const Thread = React.memo(
if (prevProps.tcount !== nextProps.tcount) {
return false;
}
if (prevProps.theme !== nextProps.theme) {
return false;
}
return true;
}
);

View File

@ -8,11 +8,12 @@ import Touchable from './Touchable';
import openLink from '../../utils/openLink';
import sharedStyles from '../../views/Styles';
import { themes } from '../../constants/colors';
import { withTheme } from '../../theme';
import { useTheme, withTheme } from '../../theme';
import { LISTENER } from '../Toast';
import EventEmitter from '../../utils/events';
import I18n from '../../i18n';
import MessageContext from './Context';
import { IUrl } from '../../definitions';
const styles = StyleSheet.create({
button: {
@ -50,29 +51,6 @@ const styles = StyleSheet.create({
}
});
interface IMessageUrlContent {
title: string;
description: string;
theme: string;
}
interface IMessageUrl {
url: {
ignoreParse: boolean;
url: string;
image: string;
title: string;
description: string;
};
index: number;
theme: string;
}
interface IMessageUrls {
urls?: any;
theme?: string;
}
const UrlImage = React.memo(
({ image }: { image: string }) => {
if (!image) {
@ -86,7 +64,7 @@ const UrlImage = React.memo(
);
const UrlContent = React.memo(
({ title, description, theme }: IMessageUrlContent) => (
({ title, description, theme }: { title: string; description: string; theme: string }) => (
<View style={styles.textContainer}>
{title ? (
<Text style={[styles.title, { color: themes[theme].tintColor }]} numberOfLines={2}>
@ -115,7 +93,7 @@ const UrlContent = React.memo(
);
const Url = React.memo(
({ url, index, theme }: IMessageUrl) => {
({ url, index, theme }: { url: IUrl; index: number; theme: string }) => {
if (!url || url?.ignoreParse) {
return null;
}
@ -152,14 +130,17 @@ const Url = React.memo(
);
const Urls = React.memo(
({ urls, theme }: IMessageUrls) => {
// TODO - didn't work - (React.ReactElement | null)[] | React.ReactElement | null
({ urls }: { urls?: IUrl[] }): any => {
const { theme } = useTheme();
if (!urls || urls.length === 0) {
return null;
}
return urls.map((url: any, index: number) => <Url url={url} key={url.url} index={index} theme={theme!} />);
return urls.map((url: IUrl, index: number) => <Url url={url} key={url.url} index={index} theme={theme} />);
},
(oldProps, newProps) => dequal(oldProps.urls, newProps.urls) && oldProps.theme === newProps.theme
(oldProps, newProps) => dequal(oldProps.urls, newProps.urls)
);
UrlImage.displayName = 'MessageUrlImage';

View File

@ -3,12 +3,14 @@ import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
import moment from 'moment';
import { themes } from '../../constants/colors';
import { withTheme } from '../../theme';
import { useTheme } from '../../theme';
import MessageError from './MessageError';
import sharedStyles from '../../views/Styles';
import messageStyles from './styles';
import MessageContext from './Context';
import { SYSTEM_MESSAGE_TYPES_WITH_AUTHOR_NAME } from './utils';
import { SubscriptionType } from '../../definitions';
import { IRoomInfoParam } from '../../views/SearchMessagesView';
const styles = StyleSheet.create({
container: {
@ -49,15 +51,15 @@ interface IMessageUser {
alias?: string;
ts?: Date;
timeFormat?: string;
theme: string;
navToRoomInfo?: Function;
navToRoomInfo?: (navParam: IRoomInfoParam) => void;
type: string;
}
const User = React.memo(
({ isHeader, useRealName, author, alias, ts, timeFormat, hasError, theme, navToRoomInfo, type, ...props }: IMessageUser) => {
({ isHeader, useRealName, author, alias, ts, timeFormat, hasError, navToRoomInfo, type, ...props }: IMessageUser) => {
if (isHeader || hasError) {
const { user } = useContext(MessageContext);
const { theme } = useTheme();
const username = (useRealName && author?.name) || author?.username;
const aliasUsername = alias ? (
<Text style={[styles.alias, { color: themes[theme].auxiliaryText }]}> @{username}</Text>
@ -65,8 +67,8 @@ const User = React.memo(
const time = moment(ts).format(timeFormat);
const onUserPress = () => {
navToRoomInfo?.({
t: 'd',
rid: author?._id
t: SubscriptionType.DIRECT,
rid: author?._id || ''
});
};
const isDisabled = author?._id === user.id;
@ -83,7 +85,7 @@ const User = React.memo(
<Text
style={[styles.usernameInfoMessage, { color: themes[theme].titleText }]}
onPress={onUserPress}
// @ts-ignore
// @ts-ignore // TODO - check this prop
disabled={isDisabled}>
{textContent}
</Text>
@ -98,7 +100,7 @@ const User = React.memo(
</Text>
</TouchableOpacity>
<Text style={[messageStyles.time, { color: themes[theme].auxiliaryTintColor }]}>{time}</Text>
{hasError && <MessageError hasError={hasError} theme={theme} {...props} />}
{hasError ? <MessageError hasError={hasError} {...props} /> : null}
</View>
);
}
@ -108,4 +110,4 @@ const User = React.memo(
User.displayName = 'MessageUser';
export default withTheme(User);
export default User;

View File

@ -16,9 +16,10 @@ import I18n from '../../i18n';
import { IAttachment } from '../../definitions/IAttachment';
import RCActivityIndicator from '../ActivityIndicator';
import { TGetCustomEmoji } from '../../definitions/IEmoji';
import { useTheme } from '../../theme';
const SUPPORTED_TYPES = ['video/quicktime', 'video/mp4', ...(isIOS ? [] : ['video/3gp', 'video/mkv'])];
const isTypeSupported = (type: any) => SUPPORTED_TYPES.indexOf(type) !== -1;
const isTypeSupported = (type: string) => SUPPORTED_TYPES.indexOf(type) !== -1;
const styles = StyleSheet.create({
button: {
@ -33,23 +34,24 @@ const styles = StyleSheet.create({
interface IMessageVideo {
file: IAttachment;
showAttachment?: Function;
showAttachment?: (file: IAttachment) => void;
getCustomEmoji: TGetCustomEmoji;
style?: StyleProp<TextStyle>[];
isReply?: boolean;
theme: string;
}
const Video = React.memo(
({ file, showAttachment, getCustomEmoji, style, isReply, theme }: IMessageVideo) => {
({ file, showAttachment, getCustomEmoji, style, isReply }: IMessageVideo) => {
const { baseUrl, user } = useContext(MessageContext);
const [loading, setLoading] = useState(false);
const { theme } = useTheme();
if (!baseUrl) {
return null;
}
const onPress = async () => {
if (isTypeSupported(file.video_type) && showAttachment) {
if (file.video_type && isTypeSupported(file.video_type) && showAttachment) {
return showAttachment(file);
}
@ -93,7 +95,7 @@ const Video = React.memo(
</>
);
},
(prevProps, nextProps) => dequal(prevProps.file, nextProps.file) && prevProps.theme === nextProps.theme
(prevProps, nextProps) => dequal(prevProps.file, nextProps.file)
);
export default Video;

View File

@ -1,5 +1,6 @@
import React from 'react';
import { Keyboard, ViewStyle } from 'react-native';
import { Subscription } from 'rxjs';
import Message from './Message';
import MessageContext from './Context';
@ -7,10 +8,11 @@ import debounce from '../../utils/debounce';
import { SYSTEM_MESSAGES, getMessageTranslation } from './utils';
import { E2E_MESSAGE_TYPE, E2E_STATUS } from '../../lib/encryption/constants';
import messagesStatus from '../../constants/messagesStatus';
import { withTheme } from '../../theme';
import { useTheme, withTheme } from '../../theme';
import openLink from '../../utils/openLink';
import { TGetCustomEmoji } from '../../definitions/IEmoji';
import { TAnyMessageModel } from '../../definitions';
import { IAttachment, TAnyMessageModel } from '../../definitions';
import { IRoomInfoParam } from '../../views/SearchMessagesView';
interface IMessageContainerProps {
item: TAnyMessageModel;
@ -20,7 +22,7 @@ interface IMessageContainerProps {
token: string;
};
msg?: string;
rid?: string;
rid: string;
timeFormat?: string;
style?: ViewStyle;
archived?: boolean;
@ -37,44 +39,35 @@ interface IMessageContainerProps {
isIgnored?: boolean;
highlighted?: boolean;
getCustomEmoji: TGetCustomEmoji;
onLongPress?: Function;
onReactionPress?: Function;
onEncryptedPress?: Function;
onDiscussionPress?: Function;
onThreadPress?: Function;
errorActionsShow?: Function;
replyBroadcast?: Function;
reactionInit?: Function;
fetchThreadName?: Function;
showAttachment?: Function;
onReactionLongPress?: Function;
navToRoomInfo?: Function;
callJitsi?: Function;
blockAction?: Function;
onAnswerButtonPress?: Function;
theme?: string;
onLongPress?: (item: TAnyMessageModel) => void;
onReactionPress?: (emoji: string, id: string) => void;
onEncryptedPress?: () => void;
onDiscussionPress?: (item: TAnyMessageModel) => void;
onThreadPress?: (item: TAnyMessageModel) => void;
errorActionsShow?: (item: TAnyMessageModel) => void;
replyBroadcast?: (item: TAnyMessageModel) => void;
reactionInit?: (item: TAnyMessageModel) => void;
fetchThreadName?: (tmid: string, id: string) => Promise<string | undefined>;
showAttachment: (file: IAttachment) => void;
onReactionLongPress?: (item: TAnyMessageModel) => void;
navToRoomInfo: (navParam: IRoomInfoParam) => void;
callJitsi?: () => void;
blockAction?: (params: { actionId: string; appId: string; value: string; blockId: string; rid: string; mid: string }) => void;
onAnswerButtonPress?: (message: string, tmid?: string, tshow?: boolean) => void;
threadBadgeColor?: string;
toggleFollowThread?: Function;
jumpToMessage?: Function;
onPress?: Function;
toggleFollowThread?: (isFollowingThread: boolean, tmid?: string) => Promise<void>;
jumpToMessage?: (link: string) => void;
onPress?: () => void;
}
class MessageContainer extends React.Component<IMessageContainerProps> {
interface IMessageContainerState {
isManualUnignored: boolean;
}
class MessageContainer extends React.Component<IMessageContainerProps, IMessageContainerState> {
static defaultProps = {
getCustomEmoji: () => null,
onLongPress: () => {},
onReactionPress: () => {},
onEncryptedPress: () => {},
onDiscussionPress: () => {},
onThreadPress: () => {},
onAnswerButtonPress: () => {},
errorActionsShow: () => {},
replyBroadcast: () => {},
reactionInit: () => {},
fetchThreadName: () => {},
showAttachment: () => {},
onReactionLongPress: () => {},
navToRoomInfo: () => {},
callJitsi: () => {},
blockAction: () => {},
archived: false,
@ -85,7 +78,7 @@ class MessageContainer extends React.Component<IMessageContainerProps> {
state = { isManualUnignored: false };
private subscription: any;
private subscription?: Subscription;
componentDidMount() {
const { item } = this.props;
@ -97,12 +90,9 @@ class MessageContainer extends React.Component<IMessageContainerProps> {
}
}
shouldComponentUpdate(nextProps: any, nextState: any) {
shouldComponentUpdate(nextProps: IMessageContainerProps, nextState: IMessageContainerState) {
const { isManualUnignored } = this.state;
const { theme, threadBadgeColor, isIgnored, highlighted } = this.props;
if (nextProps.theme !== theme) {
return true;
}
const { threadBadgeColor, isIgnored, highlighted } = this.props;
if (nextProps.highlighted !== highlighted) {
return true;
}
@ -169,7 +159,7 @@ class MessageContainer extends React.Component<IMessageContainerProps> {
}
};
onReactionPress = (emoji: any) => {
onReactionPress = (emoji: string) => {
const { onReactionPress, item } = this.props;
if (onReactionPress) {
onReactionPress(emoji, item.id);
@ -228,7 +218,7 @@ class MessageContainer extends React.Component<IMessageContainerProps> {
previousItem.u.username === item.u.username &&
!(previousItem.groupable === false || item.groupable === false || broadcast === true) &&
// @ts-ignore TODO: IMessage vs IMessageFromServer non-sense
item.ts - previousItem.ts < Message_GroupingPeriod! * 1000 &&
item.ts - previousItem.ts < Message_GroupingPeriod * 1000 &&
previousItem.tmid === item.tmid
) {
return false;
@ -303,10 +293,11 @@ class MessageContainer extends React.Component<IMessageContainerProps> {
};
onLinkPress = (link: string): void => {
const { item, theme, jumpToMessage } = this.props;
const isMessageLink = item?.attachments?.findIndex((att: any) => att?.message_link === link) !== -1;
if (isMessageLink) {
return jumpToMessage!(link);
const { theme } = useTheme();
const { item, jumpToMessage } = this.props;
const isMessageLink = item?.attachments?.findIndex((att: IAttachment) => att?.message_link === link) !== -1;
if (isMessageLink && jumpToMessage) {
return jumpToMessage(link);
}
openLink(link, theme);
};
@ -332,7 +323,6 @@ class MessageContainer extends React.Component<IMessageContainerProps> {
callJitsi,
blockAction,
rid,
theme,
threadBadgeColor,
toggleFollowThread,
jumpToMessage,
@ -371,8 +361,8 @@ class MessageContainer extends React.Component<IMessageContainerProps> {
let message = msg;
// "autoTranslateRoom" and "autoTranslateLanguage" are properties from the subscription
// "autoTranslateMessage" is a toggle between "View Original" and "Translate" state
if (autoTranslateRoom && autoTranslateMessage) {
message = getMessageTranslation(item, autoTranslateLanguage!) || message;
if (autoTranslateRoom && autoTranslateMessage && autoTranslateLanguage) {
message = getMessageTranslation(item, autoTranslateLanguage) || message;
}
return (
@ -396,14 +386,15 @@ class MessageContainer extends React.Component<IMessageContainerProps> {
toggleFollowThread,
replies
}}>
{/* @ts-ignore*/}
<Message
id={id}
msg={message}
md={md}
rid={rid!}
rid={rid}
author={u}
ts={ts}
type={t as any}
type={t}
attachments={attachments}
blocks={blocks}
urls={urls}
@ -413,23 +404,20 @@ class MessageContainer extends React.Component<IMessageContainerProps> {
emoji={emoji}
timeFormat={timeFormat}
style={style}
archived={archived!}
broadcast={broadcast!}
archived={archived}
broadcast={broadcast}
useRealName={useRealName}
isReadReceiptEnabled={isReadReceiptEnabled!}
isReadReceiptEnabled={isReadReceiptEnabled}
unread={unread}
role={role}
drid={drid}
dcount={dcount}
// @ts-ignore
dlm={dlm}
tmid={tmid}
tcount={tcount}
// @ts-ignore
tlm={tlm}
tmsg={tmsg}
fetchThreadName={fetchThreadName!}
// @ts-ignore
fetchThreadName={fetchThreadName}
mentions={mentions}
channels={channels}
isIgnored={this.isIgnored}
@ -442,13 +430,12 @@ class MessageContainer extends React.Component<IMessageContainerProps> {
isTemp={this.isTemp}
isEncrypted={this.isEncrypted}
hasError={this.hasError}
showAttachment={showAttachment!}
showAttachment={showAttachment}
getCustomEmoji={getCustomEmoji}
navToRoomInfo={navToRoomInfo!}
callJitsi={callJitsi!}
blockAction={blockAction!}
theme={theme as string}
highlighted={highlighted!}
navToRoomInfo={navToRoomInfo}
callJitsi={callJitsi}
blockAction={blockAction}
highlighted={highlighted}
/>
</MessageContext.Provider>
);

View File

@ -1,63 +1,45 @@
import { MarkdownAST } from '@rocket.chat/message-parser';
import { StyleProp, TextStyle } from 'react-native';
import { ImageStyle } from '@rocket.chat/react-native-fast-image';
import { IUserChannel, IUserMention } from '../markdown/interfaces';
import { IUserChannel } from '../markdown/interfaces';
import { TGetCustomEmoji } from '../../definitions/IEmoji';
import { IAttachment } from '../../definitions';
export type TMessageType = 'discussion-created' | 'jitsi_call_started';
import { IAttachment, IThread, IUrl, IUserMention, IUserMessage, MessageType, TAnyMessageModel } from '../../definitions';
import { IRoomInfoParam } from '../../views/SearchMessagesView';
export interface IMessageAttachments {
attachments?: IAttachment[];
timeFormat?: string;
style?: StyleProp<TextStyle>[];
isReply?: boolean;
showAttachment?: Function;
showAttachment?: (file: IAttachment) => void;
getCustomEmoji: TGetCustomEmoji;
}
export interface IMessageAttachedActions {
attachment: IAttachment;
}
export interface IMessageAvatar {
isHeader: boolean;
avatar: string;
emoji: string;
author: {
username: string;
_id: string;
};
avatar?: string;
emoji?: string;
author?: IUserMessage;
small?: boolean;
navToRoomInfo: Function;
navToRoomInfo: (navParam: IRoomInfoParam) => void;
getCustomEmoji: TGetCustomEmoji;
}
export interface IMessageBlocks {
blocks: any;
blocks: { appId?: string }[];
id: string;
rid: string;
blockAction: Function;
blockAction?: (params: { actionId: string; appId: string; value: string; blockId: string; rid: string; mid: string }) => void;
}
export interface IMessageBroadcast {
author: {
_id: string;
};
broadcast: boolean;
theme: string;
author?: IUserMessage;
broadcast?: boolean;
}
export interface IMessageCallButton {
theme: string;
callJitsi: Function;
}
export interface IUser {
id: string;
username: string;
token: string;
name: string;
callJitsi?: () => void;
}
export interface IMessageContent {
@ -68,40 +50,27 @@ export interface IMessageContent {
isThreadRoom: boolean;
msg?: string;
md?: MarkdownAST;
theme: string;
isEdited: boolean;
isEncrypted: boolean;
getCustomEmoji: TGetCustomEmoji;
channels?: IUserChannel[];
mentions?: IUserMention[];
navToRoomInfo?: Function;
navToRoomInfo: (navParam: IRoomInfoParam) => void;
useRealName?: boolean;
isIgnored: boolean;
type: string;
}
export interface IMessageDiscussion {
msg?: string;
dcount?: number;
dlm?: Date;
theme: string;
}
export interface IMessageEmoji {
content: any;
content: string;
baseUrl: string;
standardEmojiStyle: object;
customEmojiStyle: object;
standardEmojiStyle: { fontSize: number };
customEmojiStyle: StyleProp<ImageStyle>;
getCustomEmoji: TGetCustomEmoji;
}
export interface IMessageThread {
msg?: string;
tcount?: number | null;
theme: string;
tlm?: Date;
export interface IMessageThread extends Pick<IThread, 'msg' | 'tcount' | 'tlm' | 'id'> {
isThreadRoom: boolean;
id: string;
}
export interface IMessageTouchable {
@ -109,40 +78,35 @@ export interface IMessageTouchable {
isInfo: boolean;
isThreadReply: boolean;
isTemp: boolean;
archived: boolean;
highlighted: boolean;
theme: string;
ts?: any;
urls?: any;
archived?: boolean;
highlighted?: boolean;
ts?: string | Date;
urls?: IUrl[];
reactions?: any;
alias?: any;
role?: any;
drid?: any;
alias?: string;
role?: string;
drid?: string;
}
export interface IMessageRepliedThread {
tmid?: string;
tmsg?: string;
id: string;
export interface IMessageRepliedThread extends Pick<IThread, 'tmid' | 'tmsg' | 'id'> {
isHeader: boolean;
theme: string;
fetchThreadName: Function;
fetchThreadName?: (tmid: string, id: string) => Promise<string | undefined>;
isEncrypted: boolean;
}
export interface IMessageInner
extends IMessageDiscussion,
IMessageContent,
extends IMessageContent,
IMessageCallButton,
IMessageBlocks,
IMessageThread,
IMessageAttachments,
IMessageBroadcast {
type: TMessageType;
type: MessageType;
blocks: [];
urls?: IUrl[];
}
export interface IMessage extends IMessageRepliedThread, IMessageInner {
export interface IMessage extends IMessageRepliedThread, IMessageInner, IMessageAvatar {
isThreadReply: boolean;
isThreadSequential: boolean;
isInfo: boolean;
@ -150,9 +114,11 @@ export interface IMessage extends IMessageRepliedThread, IMessageInner {
isHeader: boolean;
hasError: boolean;
style: any;
onLongPress: Function;
isReadReceiptEnabled: boolean;
// style: ViewStyle;
onLongPress?: (item: TAnyMessageModel) => void;
isReadReceiptEnabled?: boolean;
unread?: boolean;
theme: string;
isIgnored: boolean;
dcount: number | undefined;
dlm: string | Date | undefined;
}

View File

@ -19,7 +19,7 @@ export interface IAttachment {
image_size?: number;
author_name?: string;
author_icon?: string;
actions?: [];
actions?: { type: string; msg: string; text: string }[];
message_link?: string;
text?: string;
short?: boolean;

View File

@ -6,7 +6,7 @@ import { IAttachment } from './IAttachment';
import { IReaction } from './IReaction';
import { TThreadMessageModel } from './IThreadMessage';
import { TThreadModel } from './IThread';
import { IUrlFromServer } from './IUrl';
import { IUrl, IUrlFromServer } from './IUrl';
export type MessageType = 'jitsi_call_started' | 'discussion-created' | 'e2e' | 'load_more' | 'rm' | 'uj' | MessageTypeLoad;
@ -75,7 +75,7 @@ export interface IMessageFromServer {
ts: string | Date; // wm date issue
u: IUserMessage;
_updatedAt: string | Date;
urls?: IUrlFromServer[];
urls?: IUrl[];
mentions?: IUserMention[];
channels?: IUserChannel[];
md?: MarkdownAST;
@ -111,7 +111,7 @@ export interface ILoadMoreMessage {
export interface IMessage extends IMessageFromServer {
id: string;
t?: MessageType;
t: MessageType;
alias?: string;
parseUrls?: boolean;
avatar?: string;

View File

@ -45,5 +45,4 @@ export interface IUrl extends IUrlFromServer {
title: string;
description: string;
image: string;
url: string;
}

View File

@ -54,7 +54,7 @@ export type ChatsStackParamList = {
};
RoomInfoView: {
room?: ISubscription;
member: any;
member?: any;
rid: string;
t: SubscriptionType;
showCloseModal?: boolean;

View File

@ -22,6 +22,7 @@ import styles from './styles';
import { ChatsStackParamList } from '../../stacks/types';
import { ISubscription, SubscriptionType } from '../../definitions/ISubscription';
import { IEmoji } from '../../definitions/IEmoji';
import { IRoomInfoParam } from '../SearchMessagesView';
import { TMessageModel } from '../../definitions';
interface IMessagesViewProps {
@ -43,14 +44,6 @@ interface IMessagesViewProps {
isMasterDetail: boolean;
}
interface IRoomInfoParam {
room: ISubscription;
member: any;
rid: string;
t: SubscriptionType;
joined: boolean;
}
interface IMessagesViewState {
loading: boolean;
messages: [];
@ -188,7 +181,8 @@ class MessagesView extends React.Component<IMessagesViewProps, any> {
showAttachment: this.showAttachment,
getCustomEmoji: this.getCustomEmoji,
navToRoomInfo: this.navToRoomInfo,
onPress: () => this.jumpToMessage({ item })
onPress: () => this.jumpToMessage({ item }),
rid: this.rid
});
return {
@ -219,7 +213,6 @@ class MessagesView extends React.Component<IMessagesViewProps, any> {
}
]
}}
theme={theme}
/>
)
},

View File

@ -867,7 +867,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
}
};
replyBroadcast = (message: Record<string, string>) => {
replyBroadcast = (message: IMessage) => {
const { dispatch } = this.props;
dispatch(replyBroadcast(message));
};

View File

@ -42,12 +42,12 @@ interface ISearchMessagesViewState {
searchText: string;
}
interface IRoomInfoParam {
room: ISubscription;
member: any;
export interface IRoomInfoParam {
room?: ISubscription;
member?: any;
rid: string;
t: SubscriptionType;
joined: boolean;
joined?: boolean;
}
interface INavigationOption {