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 { Action } from 'redux';
import { MESSAGES } from './actionsTypes'; import { MESSAGES } from './actionsTypes';
import { IMessage } from '../definitions';
type IMessage = Record<string, string>;
interface IReplyBroadcast extends Action { interface IReplyBroadcast extends Action {
message: IMessage; message: IMessage;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,21 +8,25 @@ import I18n from '../../i18n';
import { CustomIcon } from '../../lib/Icons'; import { CustomIcon } from '../../lib/Icons';
import { themes } from '../../constants/colors'; import { themes } from '../../constants/colors';
import { IMessageCallButton } from './interfaces'; import { IMessageCallButton } from './interfaces';
import { useTheme } from '../../theme';
const CallButton = React.memo(({ theme, callJitsi }: IMessageCallButton) => ( const CallButton = React.memo(({ callJitsi }: IMessageCallButton) => {
<View style={styles.buttonContainer}> const { theme } = useTheme();
<Touchable return (
onPress={callJitsi} <View style={styles.buttonContainer}>
background={Touchable.Ripple(themes[theme].bannerBackground)} <Touchable
style={[styles.button, { backgroundColor: themes[theme].tintColor }]} onPress={callJitsi}
hitSlop={BUTTON_HIT_SLOP}> background={Touchable.Ripple(themes[theme].bannerBackground)}
<> style={[styles.button, { backgroundColor: themes[theme].tintColor }]}
<CustomIcon name='camera' size={16} style={styles.buttonIcon} color={themes[theme].buttonText} /> hitSlop={BUTTON_HIT_SLOP}>
<Text style={[styles.buttonText, { color: themes[theme].buttonText }]}>{I18n.t('Click_to_join')}</Text> <>
</> <CustomIcon name='camera' size={16} style={styles.buttonIcon} color={themes[theme].buttonText} />
</Touchable> <Text style={[styles.buttonText, { color: themes[theme].buttonText }]}>{I18n.t('Click_to_join')}</Text>
</View> </>
)); </Touchable>
</View>
);
});
CallButton.displayName = 'CallButton'; CallButton.displayName = 'CallButton';

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,30 +7,28 @@ import styles from './styles';
import Emoji from './Emoji'; import Emoji from './Emoji';
import { BUTTON_HIT_SLOP } from './utils'; import { BUTTON_HIT_SLOP } from './utils';
import { themes } from '../../constants/colors'; import { themes } from '../../constants/colors';
import { withTheme } from '../../theme'; import { useTheme } from '../../theme';
import MessageContext from './Context'; import MessageContext from './Context';
import { TGetCustomEmoji } from '../../definitions/IEmoji'; import { TGetCustomEmoji } from '../../definitions/IEmoji';
interface IMessageAddReaction { interface IReaction {
theme: string; _id: string;
emoji: string;
usernames: string[];
} }
interface IMessageReaction { interface IMessageReaction {
reaction: { reaction: IReaction;
usernames: [];
emoji: object;
};
getCustomEmoji: TGetCustomEmoji; getCustomEmoji: TGetCustomEmoji;
theme: string; theme: string;
} }
interface IMessageReactions { interface IMessageReactions {
reactions?: object[]; reactions?: IReaction[];
getCustomEmoji: TGetCustomEmoji; getCustomEmoji: TGetCustomEmoji;
theme: string;
} }
const AddReaction = React.memo(({ theme }: IMessageAddReaction) => { const AddReaction = React.memo(({ theme }: { theme: string }) => {
const { reactionInit } = useContext(MessageContext); const { reactionInit } = useContext(MessageContext);
return ( return (
<Touchable <Touchable
@ -49,7 +47,7 @@ const AddReaction = React.memo(({ theme }: IMessageAddReaction) => {
const Reaction = React.memo(({ reaction, getCustomEmoji, theme }: IMessageReaction) => { const Reaction = React.memo(({ reaction, getCustomEmoji, theme }: IMessageReaction) => {
const { onReactionPress, onReactionLongPress, baseUrl, user } = useContext(MessageContext); 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 ( return (
<Touchable <Touchable
onPress={() => onReactionPress(reaction.emoji)} 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) { if (!Array.isArray(reactions) || reactions.length === 0) {
return null; return null;
} }
return ( return (
<View style={styles.reactionsContainer}> <View style={styles.reactionsContainer}>
{reactions.map((reaction: any) => ( {reactions.map(reaction => (
<Reaction key={reaction.emoji} reaction={reaction} getCustomEmoji={getCustomEmoji} theme={theme} /> <Reaction key={reaction.emoji} reaction={reaction} getCustomEmoji={getCustomEmoji} theme={theme} />
))} ))}
<AddReaction theme={theme} /> <AddReaction theme={theme} />
@ -94,4 +94,4 @@ Reaction.displayName = 'MessageReaction';
Reactions.displayName = 'MessageReactions'; Reactions.displayName = 'MessageReactions';
AddReaction.displayName = 'MessageAddReaction'; 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 { themes } from '../../constants/colors';
import { CustomIcon } from '../../lib/Icons'; import { CustomIcon } from '../../lib/Icons';
import styles from './styles'; import styles from './styles';
import { useTheme } from '../../theme';
interface IMessageReadReceipt { const ReadReceipt = React.memo(({ isReadReceiptEnabled, unread }: { isReadReceiptEnabled?: boolean; unread: boolean }) => {
isReadReceiptEnabled: boolean; const { theme } = useTheme();
unread: boolean;
theme: string;
}
const ReadReceipt = React.memo(({ isReadReceiptEnabled, unread, theme }: IMessageReadReceipt) => {
if (isReadReceiptEnabled && !unread && unread !== null) { if (isReadReceiptEnabled && !unread && unread !== null) {
return <CustomIcon name='check' color={themes[theme].tintColor} size={15} style={styles.readReceipt} />; 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 I18n from '../../i18n';
import { MarkdownPreview } from '../markdown'; import { MarkdownPreview } from '../markdown';
import { IMessageRepliedThread } from './interfaces'; 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) { if (!tmid || !isHeader) {
return null; return null;
} }
const [msg, setMsg] = useState(isEncrypted ? I18n.t('Encrypted_message') : tmsg); const [msg, setMsg] = useState(isEncrypted ? I18n.t('Encrypted_message') : tmsg);
const fetch = async () => { const fetch = async () => {
const threadName = await fetchThreadName(tmid, id); const threadName = fetchThreadName ? await fetchThreadName(tmid, id) : '';
setMsg(threadName); setMsg(threadName);
}; };

View File

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

View File

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

View File

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

View File

@ -3,12 +3,14 @@ import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
import moment from 'moment'; import moment from 'moment';
import { themes } from '../../constants/colors'; import { themes } from '../../constants/colors';
import { withTheme } from '../../theme'; import { useTheme } from '../../theme';
import MessageError from './MessageError'; import MessageError from './MessageError';
import sharedStyles from '../../views/Styles'; import sharedStyles from '../../views/Styles';
import messageStyles from './styles'; import messageStyles from './styles';
import MessageContext from './Context'; import MessageContext from './Context';
import { SYSTEM_MESSAGE_TYPES_WITH_AUTHOR_NAME } from './utils'; import { SYSTEM_MESSAGE_TYPES_WITH_AUTHOR_NAME } from './utils';
import { SubscriptionType } from '../../definitions';
import { IRoomInfoParam } from '../../views/SearchMessagesView';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
@ -49,15 +51,15 @@ interface IMessageUser {
alias?: string; alias?: string;
ts?: Date; ts?: Date;
timeFormat?: string; timeFormat?: string;
theme: string; navToRoomInfo?: (navParam: IRoomInfoParam) => void;
navToRoomInfo?: Function;
type: string; type: string;
} }
const User = React.memo( 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) { if (isHeader || hasError) {
const { user } = useContext(MessageContext); const { user } = useContext(MessageContext);
const { theme } = useTheme();
const username = (useRealName && author?.name) || author?.username; const username = (useRealName && author?.name) || author?.username;
const aliasUsername = alias ? ( const aliasUsername = alias ? (
<Text style={[styles.alias, { color: themes[theme].auxiliaryText }]}> @{username}</Text> <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 time = moment(ts).format(timeFormat);
const onUserPress = () => { const onUserPress = () => {
navToRoomInfo?.({ navToRoomInfo?.({
t: 'd', t: SubscriptionType.DIRECT,
rid: author?._id rid: author?._id || ''
}); });
}; };
const isDisabled = author?._id === user.id; const isDisabled = author?._id === user.id;
@ -83,7 +85,7 @@ const User = React.memo(
<Text <Text
style={[styles.usernameInfoMessage, { color: themes[theme].titleText }]} style={[styles.usernameInfoMessage, { color: themes[theme].titleText }]}
onPress={onUserPress} onPress={onUserPress}
// @ts-ignore // @ts-ignore // TODO - check this prop
disabled={isDisabled}> disabled={isDisabled}>
{textContent} {textContent}
</Text> </Text>
@ -98,7 +100,7 @@ const User = React.memo(
</Text> </Text>
</TouchableOpacity> </TouchableOpacity>
<Text style={[messageStyles.time, { color: themes[theme].auxiliaryTintColor }]}>{time}</Text> <Text style={[messageStyles.time, { color: themes[theme].auxiliaryTintColor }]}>{time}</Text>
{hasError && <MessageError hasError={hasError} theme={theme} {...props} />} {hasError ? <MessageError hasError={hasError} {...props} /> : null}
</View> </View>
); );
} }
@ -108,4 +110,4 @@ const User = React.memo(
User.displayName = 'MessageUser'; 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 { IAttachment } from '../../definitions/IAttachment';
import RCActivityIndicator from '../ActivityIndicator'; import RCActivityIndicator from '../ActivityIndicator';
import { TGetCustomEmoji } from '../../definitions/IEmoji'; import { TGetCustomEmoji } from '../../definitions/IEmoji';
import { useTheme } from '../../theme';
const SUPPORTED_TYPES = ['video/quicktime', 'video/mp4', ...(isIOS ? [] : ['video/3gp', 'video/mkv'])]; 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({ const styles = StyleSheet.create({
button: { button: {
@ -33,23 +34,24 @@ const styles = StyleSheet.create({
interface IMessageVideo { interface IMessageVideo {
file: IAttachment; file: IAttachment;
showAttachment?: Function; showAttachment?: (file: IAttachment) => void;
getCustomEmoji: TGetCustomEmoji; getCustomEmoji: TGetCustomEmoji;
style?: StyleProp<TextStyle>[]; style?: StyleProp<TextStyle>[];
isReply?: boolean; isReply?: boolean;
theme: string;
} }
const Video = React.memo( const Video = React.memo(
({ file, showAttachment, getCustomEmoji, style, isReply, theme }: IMessageVideo) => { ({ file, showAttachment, getCustomEmoji, style, isReply }: IMessageVideo) => {
const { baseUrl, user } = useContext(MessageContext); const { baseUrl, user } = useContext(MessageContext);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const { theme } = useTheme();
if (!baseUrl) { if (!baseUrl) {
return null; return null;
} }
const onPress = async () => { const onPress = async () => {
if (isTypeSupported(file.video_type) && showAttachment) { if (file.video_type && isTypeSupported(file.video_type) && showAttachment) {
return showAttachment(file); 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; export default Video;

View File

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

View File

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

View File

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

View File

@ -6,7 +6,7 @@ import { IAttachment } from './IAttachment';
import { IReaction } from './IReaction'; import { IReaction } from './IReaction';
import { TThreadMessageModel } from './IThreadMessage'; import { TThreadMessageModel } from './IThreadMessage';
import { TThreadModel } from './IThread'; 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; 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 ts: string | Date; // wm date issue
u: IUserMessage; u: IUserMessage;
_updatedAt: string | Date; _updatedAt: string | Date;
urls?: IUrlFromServer[]; urls?: IUrl[];
mentions?: IUserMention[]; mentions?: IUserMention[];
channels?: IUserChannel[]; channels?: IUserChannel[];
md?: MarkdownAST; md?: MarkdownAST;
@ -111,7 +111,7 @@ export interface ILoadMoreMessage {
export interface IMessage extends IMessageFromServer { export interface IMessage extends IMessageFromServer {
id: string; id: string;
t?: MessageType; t: MessageType;
alias?: string; alias?: string;
parseUrls?: boolean; parseUrls?: boolean;
avatar?: string; avatar?: string;

View File

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

View File

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

View File

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

View File

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