Chore: Evaluate MessageBox - TypeScript (#3936)

* chore: migrate buttons

* chore: migrate CommandsPreview

* chore: migrate Mention

* chore: migrate Some files

* chore: migrate RecordAudio

* fix lint

* fix context value

* fix MessageBox types
This commit is contained in:
Gleidson Daniel Silva 2022-03-31 19:39:24 -03:00 committed by GitHub
parent 64b594f9ae
commit e0459aed89
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 213 additions and 256 deletions

View File

@ -1,12 +1,13 @@
import FastImage from '@rocket.chat/react-native-fast-image';
import React, { useContext, useState } from 'react'; import React, { useContext, useState } from 'react';
import { TouchableOpacity } from 'react-native'; import { TouchableOpacity } from 'react-native';
import FastImage from '@rocket.chat/react-native-fast-image';
import styles from '../styles';
import { CustomIcon } from '../../../lib/Icons';
import { themes } from '../../../constants/colors'; import { themes } from '../../../constants/colors';
import MessageboxContext from '../Context'; import { CustomIcon } from '../../../lib/Icons';
import { useTheme } from '../../../theme';
import ActivityIndicator from '../../ActivityIndicator'; import ActivityIndicator from '../../ActivityIndicator';
import MessageboxContext from '../Context';
import styles from '../styles';
interface IMessageBoxCommandsPreviewItem { interface IMessageBoxCommandsPreviewItem {
item: { item: {
@ -14,13 +15,13 @@ interface IMessageBoxCommandsPreviewItem {
id: string; id: string;
value: string; value: string;
}; };
theme?: string;
} }
const Item = ({ item, theme }: IMessageBoxCommandsPreviewItem) => { const Item = ({ item }: IMessageBoxCommandsPreviewItem) => {
const context = useContext(MessageboxContext); const context = useContext(MessageboxContext);
const { onPressCommandPreview } = context; const { onPressCommandPreview } = context;
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const { theme } = useTheme();
return ( return (
<TouchableOpacity <TouchableOpacity
@ -37,7 +38,7 @@ const Item = ({ item, theme }: IMessageBoxCommandsPreviewItem) => {
{loading ? <ActivityIndicator /> : null} {loading ? <ActivityIndicator /> : null}
</FastImage> </FastImage>
) : ( ) : (
<CustomIcon name='attach' size={36} color={themes[theme!].actionTintColor} /> <CustomIcon name='attach' size={36} color={themes[theme].actionTintColor} />
)} )}
</TouchableOpacity> </TouchableOpacity>
); );

View File

@ -1,30 +1,30 @@
import { dequal } from 'dequal';
import React from 'react'; import React from 'react';
import { FlatList } from 'react-native'; import { FlatList } from 'react-native';
import { dequal } from 'dequal';
import Item from './Item';
import styles from '../styles';
import { themes } from '../../../constants/colors'; import { themes } from '../../../constants/colors';
import { withTheme } from '../../../theme';
import { IPreviewItem } from '../../../definitions'; import { IPreviewItem } from '../../../definitions';
import { useTheme } from '../../../theme';
import styles from '../styles';
import Item from './Item';
interface IMessageBoxCommandsPreview { interface IMessageBoxCommandsPreview {
commandPreview: IPreviewItem[]; commandPreview: IPreviewItem[];
showCommandPreview: boolean; showCommandPreview: boolean;
theme?: string;
} }
const CommandsPreview = React.memo( const CommandsPreview = React.memo(
({ theme, commandPreview, showCommandPreview }: IMessageBoxCommandsPreview) => { ({ commandPreview, showCommandPreview }: IMessageBoxCommandsPreview) => {
if (!showCommandPreview) { if (!showCommandPreview) {
return null; return null;
} }
const { theme } = useTheme();
return ( return (
<FlatList <FlatList
testID='commandbox-container' testID='commandbox-container'
style={[styles.mentionList, { backgroundColor: themes[theme!].messageboxBackground }]} style={[styles.mentionList, { backgroundColor: themes[theme].messageboxBackground }]}
data={commandPreview} data={commandPreview}
renderItem={({ item }) => <Item item={item} theme={theme} />} renderItem={({ item }) => <Item item={item} />}
keyExtractor={(item: any) => item.id} keyExtractor={(item: any) => item.id}
keyboardShouldPersistTaps='always' keyboardShouldPersistTaps='always'
horizontal horizontal
@ -33,9 +33,6 @@ const CommandsPreview = React.memo(
); );
}, },
(prevProps, nextProps) => { (prevProps, nextProps) => {
if (prevProps.theme !== nextProps.theme) {
return false;
}
if (prevProps.showCommandPreview !== nextProps.showCommandPreview) { if (prevProps.showCommandPreview !== nextProps.showCommandPreview) {
return false; return false;
} }
@ -46,4 +43,4 @@ const CommandsPreview = React.memo(
} }
); );
export default withTheme(CommandsPreview); export default CommandsPreview;

View File

@ -1,5 +1,4 @@
import React from 'react'; import React from 'react';
// @ts-ignore const MessageboxContext = React.createContext<any>(null);
const MessageboxContext = React.createContext<any>();
export default MessageboxContext; export default MessageboxContext;

View File

@ -7,6 +7,7 @@ import EmojiPicker from '../EmojiPicker';
import styles from './styles'; import styles from './styles';
import { themes } from '../../constants/colors'; import { themes } from '../../constants/colors';
import { withTheme } from '../../theme'; import { withTheme } from '../../theme';
import { IEmoji } from '../../definitions/IEmoji';
interface IMessageBoxEmojiKeyboard { interface IMessageBoxEmojiKeyboard {
theme: string; theme: string;
@ -21,7 +22,7 @@ export default class EmojiKeyboard extends React.PureComponent<IMessageBoxEmojiK
this.baseUrl = state.share.server.server || state.server.server; this.baseUrl = state.share.server.server || state.server.server;
} }
onEmojiSelected = (emoji: any) => { onEmojiSelected = (emoji: IEmoji) => {
KeyboardRegistry.onItemSelected('EmojiKeyboard', { emoji }); KeyboardRegistry.onItemSelected('EmojiKeyboard', { emoji });
}; };

View File

@ -3,7 +3,6 @@ import React from 'react';
import { CancelEditingButton, ToggleEmojiButton } from './buttons'; import { CancelEditingButton, ToggleEmojiButton } from './buttons';
interface IMessageBoxLeftButtons { interface IMessageBoxLeftButtons {
theme: string;
showEmojiKeyboard: boolean; showEmojiKeyboard: boolean;
openEmoji(): void; openEmoji(): void;
closeEmoji(): void; closeEmoji(): void;
@ -11,13 +10,11 @@ interface IMessageBoxLeftButtons {
editCancel(): void; editCancel(): void;
} }
const LeftButtons = React.memo( const LeftButtons = React.memo(({ showEmojiKeyboard, editing, editCancel, openEmoji, closeEmoji }: IMessageBoxLeftButtons) => {
({ theme, showEmojiKeyboard, editing, editCancel, openEmoji, closeEmoji }: IMessageBoxLeftButtons) => {
if (editing) { if (editing) {
return <CancelEditingButton onPress={editCancel} theme={theme} />; return <CancelEditingButton onPress={editCancel} />;
} }
return <ToggleEmojiButton show={showEmojiKeyboard} open={openEmoji} close={closeEmoji} theme={theme} />; return <ToggleEmojiButton show={showEmojiKeyboard} open={openEmoji} close={closeEmoji} />;
} });
);
export default LeftButtons; export default LeftButtons;

View File

@ -5,23 +5,20 @@ import { ActionsButton, CancelEditingButton } from './buttons';
import styles from './styles'; import styles from './styles';
interface IMessageBoxLeftButtons { interface IMessageBoxLeftButtons {
theme: string;
showMessageBoxActions(): void; showMessageBoxActions(): void;
editing: boolean; editing: boolean;
editCancel(): void; editCancel(): void;
isActionsEnabled: boolean; isActionsEnabled: boolean;
} }
const LeftButtons = React.memo( const LeftButtons = React.memo(({ showMessageBoxActions, editing, editCancel, isActionsEnabled }: IMessageBoxLeftButtons) => {
({ theme, showMessageBoxActions, editing, editCancel, isActionsEnabled }: IMessageBoxLeftButtons) => {
if (editing) { if (editing) {
return <CancelEditingButton onPress={editCancel} theme={theme} />; return <CancelEditingButton onPress={editCancel} />;
} }
if (isActionsEnabled) { if (isActionsEnabled) {
return <ActionsButton onPress={showMessageBoxActions} theme={theme} />; return <ActionsButton onPress={showMessageBoxActions} />;
} }
return <View style={styles.buttonsWhitespace} />; return <View style={styles.buttonsWhitespace} />;
} });
);
export default LeftButtons; export default LeftButtons;

View File

@ -4,16 +4,18 @@ import { Text, TouchableOpacity } from 'react-native';
import styles from '../styles'; import styles from '../styles';
import I18n from '../../../i18n'; import I18n from '../../../i18n';
import { themes } from '../../../constants/colors'; import { themes } from '../../../constants/colors';
import { useTheme } from '../../../theme';
interface IMessageBoxFixedMentionItem { interface IMessageBoxFixedMentionItem {
item: { item: {
username: string; username: string;
}; };
onPress: Function; onPress: Function;
theme: string;
} }
const FixedMentionItem = ({ item, onPress, theme }: IMessageBoxFixedMentionItem) => ( const FixedMentionItem = ({ item, onPress }: IMessageBoxFixedMentionItem) => {
const { theme } = useTheme();
return (
<TouchableOpacity <TouchableOpacity
style={[ style={[
styles.mentionItem, styles.mentionItem,
@ -28,6 +30,7 @@ const FixedMentionItem = ({ item, onPress, theme }: IMessageBoxFixedMentionItem)
{item.username === 'here' ? I18n.t('Notify_active_in_this_room') : I18n.t('Notify_all_in_this_room')} {item.username === 'here' ? I18n.t('Notify_active_in_this_room') : I18n.t('Notify_all_in_this_room')}
</Text> </Text>
</TouchableOpacity> </TouchableOpacity>
); );
};
export default FixedMentionItem; export default FixedMentionItem;

View File

@ -1,12 +1,11 @@
import React, { useContext } from 'react'; import React, { useContext } from 'react';
import { Text } from 'react-native'; import { Text } from 'react-native';
import PropTypes from 'prop-types';
import shortnameToUnicode from '../../../utils/shortnameToUnicode';
import styles from '../styles';
import MessageboxContext from '../Context';
import CustomEmoji from '../../EmojiPicker/CustomEmoji';
import { IEmoji } from '../../../definitions/IEmoji'; import { IEmoji } from '../../../definitions/IEmoji';
import shortnameToUnicode from '../../../utils/shortnameToUnicode';
import CustomEmoji from '../../EmojiPicker/CustomEmoji';
import MessageboxContext from '../Context';
import styles from '../styles';
interface IMessageBoxMentionEmoji { interface IMessageBoxMentionEmoji {
item: IEmoji; item: IEmoji;
@ -22,8 +21,4 @@ const MentionEmoji = ({ item }: IMessageBoxMentionEmoji) => {
return <Text style={styles.mentionItemEmoji}>{shortnameToUnicode(`:${item}:`)}</Text>; return <Text style={styles.mentionItemEmoji}>{shortnameToUnicode(`:${item}:`)}</Text>;
}; };
MentionEmoji.propTypes = {
item: PropTypes.object
};
export default MentionEmoji; export default MentionEmoji;

View File

@ -1,16 +1,23 @@
import React, { useContext } from 'react'; import React, { useContext } from 'react';
import { View, Text, ActivityIndicator, TouchableOpacity } from 'react-native'; import { ActivityIndicator, Text, TouchableOpacity, View } from 'react-native';
import PropTypes from 'prop-types';
import { MENTIONS_TRACKING_TYPE_CANNED } from '../constants';
import styles from '../styles';
import sharedStyles from '../../../views/Styles';
import I18n from '../../../i18n';
import { themes } from '../../../constants/colors'; import { themes } from '../../../constants/colors';
import I18n from '../../../i18n';
import { CustomIcon } from '../../../lib/Icons'; import { CustomIcon } from '../../../lib/Icons';
import { useTheme } from '../../../theme';
import sharedStyles from '../../../views/Styles';
import { MENTIONS_TRACKING_TYPE_CANNED } from '../constants';
import MessageboxContext from '../Context'; import MessageboxContext from '../Context';
import styles from '../styles';
const MentionHeaderList = ({ trackingType, hasMentions, theme, loading }) => { interface IMentionHeaderList {
trackingType: string;
hasMentions: boolean;
loading: boolean;
}
const MentionHeaderList = ({ trackingType, hasMentions, loading }: IMentionHeaderList) => {
const { theme } = useTheme();
const context = useContext(MessageboxContext); const context = useContext(MessageboxContext);
const { onPressNoMatchCanned } = context; const { onPressNoMatchCanned } = context;
@ -39,11 +46,4 @@ const MentionHeaderList = ({ trackingType, hasMentions, theme, loading }) => {
return null; return null;
}; };
MentionHeaderList.propTypes = {
trackingType: PropTypes.string,
hasMentions: PropTypes.bool,
theme: PropTypes.string,
loading: PropTypes.bool
};
export default MentionHeaderList; export default MentionHeaderList;

View File

@ -1,14 +1,15 @@
import React, { useContext } from 'react'; import React, { useContext } from 'react';
import { Text, TouchableOpacity } from 'react-native'; import { Text, TouchableOpacity } from 'react-native';
import styles from '../styles';
import Avatar from '../../Avatar';
import MessageboxContext from '../Context';
import FixedMentionItem from './FixedMentionItem';
import MentionEmoji from './MentionEmoji';
import { MENTIONS_TRACKING_TYPE_EMOJIS, MENTIONS_TRACKING_TYPE_COMMANDS, MENTIONS_TRACKING_TYPE_CANNED } from '../constants';
import { themes } from '../../../constants/colors'; import { themes } from '../../../constants/colors';
import { IEmoji } from '../../../definitions/IEmoji'; import { IEmoji } from '../../../definitions/IEmoji';
import { useTheme } from '../../../theme';
import Avatar from '../../Avatar';
import { MENTIONS_TRACKING_TYPE_CANNED, MENTIONS_TRACKING_TYPE_COMMANDS, MENTIONS_TRACKING_TYPE_EMOJIS } from '../constants';
import MessageboxContext from '../Context';
import styles from '../styles';
import FixedMentionItem from './FixedMentionItem';
import MentionEmoji from './MentionEmoji';
interface IMessageBoxMentionItem { interface IMessageBoxMentionItem {
item: { item: {
@ -21,11 +22,48 @@ interface IMessageBoxMentionItem {
text: string; text: string;
} & IEmoji; } & IEmoji;
trackingType: string; trackingType: string;
theme: string;
} }
const MentionItem = ({ item, trackingType, theme }: IMessageBoxMentionItem) => { const MentionItemContent = React.memo(({ trackingType, item }: IMessageBoxMentionItem) => {
const { theme } = useTheme();
switch (trackingType) {
case MENTIONS_TRACKING_TYPE_EMOJIS:
return (
<>
<MentionEmoji item={item} />
<Text style={[styles.mentionText, { color: themes[theme].titleText }]}>:{item.name || item}:</Text>
</>
);
case MENTIONS_TRACKING_TYPE_COMMANDS:
return (
<>
<Text style={[styles.slash, { backgroundColor: themes[theme].borderColor, color: themes[theme].tintColor }]}>/</Text>
<Text style={[styles.mentionText, { color: themes[theme].titleText }]}>{item.id}</Text>
</>
);
case MENTIONS_TRACKING_TYPE_CANNED:
return (
<>
<Text style={[styles.cannedItem, { color: themes[theme].titleText }]}>!{item.shortcut}</Text>
<Text numberOfLines={1} style={[styles.cannedMentionText, { color: themes[theme].auxiliaryTintColor }]}>
{item.text}
</Text>
</>
);
default:
return (
<>
<Avatar style={styles.avatar} text={item.username || item.name} size={30} type={item.t} />
<Text style={[styles.mentionText, { color: themes[theme].titleText }]}>{item.username || item.name || item}</Text>
</>
);
}
});
const MentionItem = ({ item, trackingType }: IMessageBoxMentionItem) => {
const context = useContext(MessageboxContext); const context = useContext(MessageboxContext);
const { theme } = useTheme();
const { onPressMention } = context; const { onPressMention } = context;
const defineTestID = (type: string) => { const defineTestID = (type: string) => {
@ -44,43 +82,7 @@ const MentionItem = ({ item, trackingType, theme }: IMessageBoxMentionItem) => {
const testID = defineTestID(trackingType); const testID = defineTestID(trackingType);
if (item.username === 'all' || item.username === 'here') { if (item.username === 'all' || item.username === 'here') {
return <FixedMentionItem item={item} onPress={onPressMention} theme={theme} />; return <FixedMentionItem item={item} onPress={onPressMention} />;
}
let content = (
<>
<Avatar style={styles.avatar} text={item.username || item.name} size={30} type={item.t} />
<Text style={[styles.mentionText, { color: themes[theme].titleText }]}>{item.username || item.name || item}</Text>
</>
);
if (trackingType === MENTIONS_TRACKING_TYPE_EMOJIS) {
content = (
<>
<MentionEmoji item={item} />
<Text style={[styles.mentionText, { color: themes[theme].titleText }]}>:{item.name || item}:</Text>
</>
);
}
if (trackingType === MENTIONS_TRACKING_TYPE_COMMANDS) {
content = (
<>
<Text style={[styles.slash, { backgroundColor: themes[theme].borderColor, color: themes[theme].tintColor }]}>/</Text>
<Text style={[styles.mentionText, { color: themes[theme].titleText }]}>{item.id}</Text>
</>
);
}
if (trackingType === MENTIONS_TRACKING_TYPE_CANNED) {
content = (
<>
<Text style={[styles.cannedItem, { color: themes[theme].titleText }]}>!{item.shortcut}</Text>
<Text numberOfLines={1} style={[styles.cannedMentionText, { color: themes[theme].auxiliaryTintColor }]}>
{item.text}
</Text>
</>
);
} }
return ( return (
@ -94,7 +96,7 @@ const MentionItem = ({ item, trackingType, theme }: IMessageBoxMentionItem) => {
]} ]}
onPress={() => onPressMention(item)} onPress={() => onPressMention(item)}
testID={testID}> testID={testID}>
{content} <MentionItemContent item={item} trackingType={trackingType} />
</TouchableOpacity> </TouchableOpacity>
); );
}; };

View File

@ -6,29 +6,30 @@ import MentionHeaderList from './MentionHeaderList';
import styles from '../styles'; import styles from '../styles';
import MentionItem from './MentionItem'; import MentionItem from './MentionItem';
import { themes } from '../../../constants/colors'; import { themes } from '../../../constants/colors';
import { useTheme } from '../../../theme';
interface IMessageBoxMentions { interface IMessageBoxMentions {
mentions: any[]; mentions: any[];
trackingType: string; trackingType: string;
theme: string;
loading: boolean; loading: boolean;
} }
const Mentions = React.memo( const Mentions = React.memo(
({ mentions, trackingType, theme, loading }: IMessageBoxMentions) => { ({ mentions, trackingType, loading }: IMessageBoxMentions) => {
if (!trackingType) { if (!trackingType) {
return null; return null;
} }
const { theme } = useTheme();
return ( return (
<View testID='messagebox-container'> <View testID='messagebox-container'>
<FlatList <FlatList
style={[styles.mentionList, { backgroundColor: themes[theme].auxiliaryBackground }]} style={[styles.mentionList, { backgroundColor: themes[theme].auxiliaryBackground }]}
ListHeaderComponent={() => ( ListHeaderComponent={() => (
<MentionHeaderList trackingType={trackingType} hasMentions={mentions.length > 0} theme={theme} loading={loading} /> <MentionHeaderList trackingType={trackingType} hasMentions={mentions.length > 0} loading={loading} />
)} )}
data={mentions} data={mentions}
extraData={mentions} extraData={mentions}
renderItem={({ item }) => <MentionItem item={item} trackingType={trackingType} theme={theme} />} renderItem={({ item }) => <MentionItem item={item} trackingType={trackingType} />}
keyExtractor={item => item.rid || item.name || item.command || item.shortcut || item} keyExtractor={item => item.rid || item.name || item.command || item.shortcut || item}
keyboardShouldPersistTaps='always' keyboardShouldPersistTaps='always'
/> />
@ -39,9 +40,6 @@ const Mentions = React.memo(
if (prevProps.loading !== nextProps.loading) { if (prevProps.loading !== nextProps.loading) {
return false; return false;
} }
if (prevProps.theme !== nextProps.theme) {
return false;
}
if (prevProps.trackingType !== nextProps.trackingType) { if (prevProps.trackingType !== nextProps.trackingType) {
return false; return false;
} }

View File

@ -47,23 +47,17 @@ const RECORDING_MODE = {
interruptionModeAndroid: Audio.INTERRUPTION_MODE_ANDROID_DO_NOT_MIX interruptionModeAndroid: Audio.INTERRUPTION_MODE_ANDROID_DO_NOT_MIX
}; };
const formatTime = function (seconds: any) { const formatTime = function (time: number) {
let minutes: any = Math.floor(seconds / 60); const minutes = Math.floor(time / 60);
seconds %= 60; const seconds = time % 60;
if (minutes < 10) { const min = minutes < 10 ? `0${minutes}` : minutes;
minutes = `0${minutes}`; const sec = seconds < 10 ? `0${seconds}` : seconds;
} return `${min}:${sec}`;
if (seconds < 10) {
seconds = `0${seconds}`;
}
return `${minutes}:${seconds}`;
}; };
export default class RecordAudio extends React.PureComponent<IMessageBoxRecordAudioProps, any> { export default class RecordAudio extends React.PureComponent<IMessageBoxRecordAudioProps, any> {
private isRecorderBusy: boolean; private isRecorderBusy: boolean;
private recording!: Audio.Recording;
private recording: any;
private LastDuration: number; private LastDuration: number;
constructor(props: IMessageBoxRecordAudioProps) { constructor(props: IMessageBoxRecordAudioProps) {
@ -112,7 +106,7 @@ export default class RecordAudio extends React.PureComponent<IMessageBoxRecordAu
return false; return false;
}; };
onRecordingStatusUpdate = (status: any) => { onRecordingStatusUpdate = (status: Audio.RecordingStatus) => {
this.setState({ this.setState({
isRecording: status.isRecording, isRecording: status.isRecording,
recordingDurationMillis: status.durationMillis recordingDurationMillis: status.durationMillis
@ -157,7 +151,7 @@ export default class RecordAudio extends React.PureComponent<IMessageBoxRecordAu
await this.recording.stopAndUnloadAsync(); await this.recording.stopAndUnloadAsync();
const fileURI = this.recording.getURI(); const fileURI = this.recording.getURI();
const fileData = await getInfoAsync(fileURI); const fileData = await getInfoAsync(fileURI as string);
const fileInfo = { const fileInfo = {
name: `${Date.now()}.m4a`, name: `${Date.now()}.m4a`,
mime: 'audio/aac', mime: 'audio/aac',

View File

@ -8,6 +8,8 @@ import { CustomIcon } from '../../lib/Icons';
import sharedStyles from '../../views/Styles'; import sharedStyles from '../../views/Styles';
import { themes } from '../../constants/colors'; import { themes } from '../../constants/colors';
import { IMessage } from '../../definitions/IMessage'; import { IMessage } from '../../definitions/IMessage';
import { useTheme } from '../../theme';
import { IApplicationState } from '../../definitions';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
@ -49,16 +51,15 @@ interface IMessageBoxReplyPreview {
baseUrl: string; baseUrl: string;
username: string; username: string;
getCustomEmoji: Function; getCustomEmoji: Function;
theme: string;
useRealName: boolean; useRealName: boolean;
} }
const ReplyPreview = React.memo( const ReplyPreview = React.memo(
({ message, Message_TimeFormat, replying, close, theme, useRealName }: IMessageBoxReplyPreview) => { ({ message, Message_TimeFormat, replying, close, useRealName }: IMessageBoxReplyPreview) => {
if (!replying) { if (!replying) {
return null; return null;
} }
const { theme } = useTheme();
const time = moment(message.ts).format(Message_TimeFormat); const time = moment(message.ts).format(Message_TimeFormat);
return ( return (
<View style={[styles.container, { backgroundColor: themes[theme].messageboxBackground }]}> <View style={[styles.container, { backgroundColor: themes[theme].messageboxBackground }]}>
@ -75,16 +76,14 @@ const ReplyPreview = React.memo(
</View> </View>
); );
}, },
(prevProps: any, nextProps: any) => (prevProps: IMessageBoxReplyPreview, nextProps: IMessageBoxReplyPreview) =>
prevProps.replying === nextProps.replying && prevProps.replying === nextProps.replying && prevProps.message.id === nextProps.message.id
prevProps.theme === nextProps.theme &&
prevProps.message.id === nextProps.message.id
); );
const mapStateToProps = (state: any) => ({ const mapStateToProps = (state: IApplicationState) => ({
Message_TimeFormat: state.settings.Message_TimeFormat, Message_TimeFormat: state.settings.Message_TimeFormat as string,
baseUrl: state.server.server, baseUrl: state.server.server,
useRealName: state.settings.UI_Use_Real_Name useRealName: state.settings.UI_Use_Real_Name as boolean
}); });
export default connect(mapStateToProps)(ReplyPreview); export default connect(mapStateToProps)(ReplyPreview);

View File

@ -5,24 +5,20 @@ import { ActionsButton, SendButton } from './buttons';
import styles from './styles'; import styles from './styles';
interface IMessageBoxRightButtons { interface IMessageBoxRightButtons {
theme: string;
showSend: boolean; showSend: boolean;
submit(): void; submit(): void;
showMessageBoxActions(): void; showMessageBoxActions(): void;
isActionsEnabled: boolean; isActionsEnabled: boolean;
} }
const RightButtons = React.memo( const RightButtons = React.memo(({ showSend, submit, showMessageBoxActions, isActionsEnabled }: IMessageBoxRightButtons) => {
({ theme, showSend, submit, showMessageBoxActions, isActionsEnabled }: IMessageBoxRightButtons) => {
if (showSend) { if (showSend) {
return <SendButton onPress={submit} theme={theme} />; return <SendButton onPress={submit} />;
} }
if (isActionsEnabled) { if (isActionsEnabled) {
return <ActionsButton onPress={showMessageBoxActions} theme={theme} />; return <ActionsButton onPress={showMessageBoxActions} />;
} }
return <View style={styles.buttonsWhitespace} />; return <View style={styles.buttonsWhitespace} />;
} });
);
export default RightButtons; export default RightButtons;

View File

@ -3,16 +3,15 @@ import React from 'react';
import { SendButton } from './buttons'; import { SendButton } from './buttons';
interface IMessageBoxRightButtons { interface IMessageBoxRightButtons {
theme: string;
showSend: boolean; showSend: boolean;
submit(): void; submit(): void;
} }
const RightButtons = React.memo(({ theme, showSend, submit }: IMessageBoxRightButtons) => { const RightButtons = ({ showSend, submit }: IMessageBoxRightButtons) => {
if (showSend) { if (showSend) {
return <SendButton theme={theme} onPress={submit} />; return <SendButton onPress={submit} />;
} }
return null; return null;
}); };
export default RightButtons; export default RightButtons;

View File

@ -3,12 +3,11 @@ import React from 'react';
import BaseButton from './BaseButton'; import BaseButton from './BaseButton';
interface IActionsButton { interface IActionsButton {
theme: string;
onPress(): void; onPress(): void;
} }
const ActionsButton = React.memo(({ theme, onPress }: IActionsButton) => ( const ActionsButton = ({ onPress }: IActionsButton) => (
<BaseButton onPress={onPress} testID='messagebox-actions' accessibilityLabel='Message_actions' icon='add' theme={theme} /> <BaseButton onPress={onPress} testID='messagebox-actions' accessibilityLabel='Message_actions' icon='add' />
)); );
export default ActionsButton; export default ActionsButton;

View File

@ -1,13 +1,13 @@
import React from 'react';
import { BorderlessButton } from 'react-native-gesture-handler'; import { BorderlessButton } from 'react-native-gesture-handler';
import React from 'react';
import { themes } from '../../../constants/colors';
import { CustomIcon } from '../../../lib/Icons';
import styles from '../styles'; import styles from '../styles';
import I18n from '../../../i18n'; import i18n from '../../../i18n';
import { CustomIcon } from '../../../lib/Icons';
import { useTheme } from '../../../theme';
import { themes } from '../../../constants/colors';
interface IBaseButton { interface IBaseButton {
theme: string;
onPress(): void; onPress(): void;
testID: string; testID: string;
accessibilityLabel: string; accessibilityLabel: string;
@ -15,16 +15,18 @@ interface IBaseButton {
color: string; color: string;
} }
const BaseButton = React.memo(({ onPress, testID, accessibilityLabel, icon, theme, color }: Partial<IBaseButton>) => ( const BaseButton = ({ accessibilityLabel, icon, color, ...props }: Partial<IBaseButton>) => {
const { theme } = useTheme();
return (
<BorderlessButton <BorderlessButton
onPress={onPress} {...props}
style={styles.actionButton} style={styles.actionButton}
testID={testID}
// @ts-ignore // @ts-ignore
accessibilityLabel={I18n.t(accessibilityLabel)} accessibilityLabel={i18n.t(accessibilityLabel)}
accessibilityTraits='button'> accessibilityTraits='button'>
<CustomIcon name={icon} size={24} color={color ?? themes[theme!].auxiliaryTintColor} /> <CustomIcon name={icon} size={24} color={color || themes[theme].auxiliaryTintColor} />
</BorderlessButton> </BorderlessButton>
)); );
};
export default BaseButton; export default BaseButton;

View File

@ -3,18 +3,11 @@ import React from 'react';
import BaseButton from './BaseButton'; import BaseButton from './BaseButton';
interface ICancelEditingButton { interface ICancelEditingButton {
theme: string;
onPress(): void; onPress(): void;
} }
const CancelEditingButton = React.memo(({ theme, onPress }: ICancelEditingButton) => ( const CancelEditingButton = ({ onPress }: ICancelEditingButton) => (
<BaseButton <BaseButton onPress={onPress} testID='messagebox-cancel-editing' accessibilityLabel='Cancel_editing' icon='close' />
onPress={onPress} );
testID='messagebox-cancel-editing'
accessibilityLabel='Cancel_editing'
icon='close'
theme={theme}
/>
));
export default CancelEditingButton; export default CancelEditingButton;

View File

@ -1,22 +1,24 @@
import React from 'react'; import React from 'react';
import BaseButton from './BaseButton';
import { themes } from '../../../constants/colors'; import { themes } from '../../../constants/colors';
import { useTheme } from '../../../theme';
import BaseButton from './BaseButton';
interface ISendButton { interface ISendButton {
theme: string;
onPress(): void; onPress(): void;
} }
const SendButton = React.memo(({ theme, onPress }: ISendButton) => ( const SendButton = ({ onPress }: ISendButton) => {
const { theme } = useTheme();
return (
<BaseButton <BaseButton
onPress={onPress} onPress={onPress}
testID='messagebox-send-message' testID='messagebox-send-message'
accessibilityLabel='Send_message' accessibilityLabel='Send_message'
icon='send-filled' icon='send-filled'
theme={theme}
color={themes[theme].tintColor} color={themes[theme].tintColor}
/> />
)); );
};
export default SendButton; export default SendButton;

View File

@ -3,33 +3,18 @@ import React from 'react';
import BaseButton from './BaseButton'; import BaseButton from './BaseButton';
interface IToggleEmojiButton { interface IToggleEmojiButton {
theme: string;
show: boolean; show: boolean;
open(): void; open(): void;
close(): void; close(): void;
} }
const ToggleEmojiButton = React.memo(({ theme, show, open, close }: IToggleEmojiButton) => { const ToggleEmojiButton = ({ show, open, close }: IToggleEmojiButton) => {
if (show) { if (show) {
return ( return (
<BaseButton <BaseButton onPress={close} testID='messagebox-close-emoji' accessibilityLabel='Close_emoji_selector' icon='keyboard' />
onPress={close}
testID='messagebox-close-emoji'
accessibilityLabel='Close_emoji_selector'
icon='keyboard'
theme={theme}
/>
); );
} }
return ( return <BaseButton onPress={open} testID='messagebox-open-emoji' accessibilityLabel='Open_emoji_selector' icon='emoji' />;
<BaseButton };
onPress={open}
testID='messagebox-open-emoji'
accessibilityLabel='Open_emoji_selector'
icon='emoji'
theme={theme}
/>
);
});
export default ToggleEmojiButton; export default ToggleEmojiButton;

View File

@ -2,7 +2,7 @@ import React, { Component } from 'react';
import { Alert, Keyboard, NativeModules, Text, View } from 'react-native'; import { Alert, Keyboard, NativeModules, Text, View } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { KeyboardAccessoryView } from 'react-native-ui-lib/keyboard'; import { KeyboardAccessoryView } from 'react-native-ui-lib/keyboard';
import ImagePicker, { Image, ImageOrVideo } from 'react-native-image-crop-picker'; import ImagePicker, { Image, ImageOrVideo, Options } from 'react-native-image-crop-picker';
import { dequal } from 'dequal'; import { dequal } from 'dequal';
import DocumentPicker from 'react-native-document-picker'; import DocumentPicker from 'react-native-document-picker';
import { Q } from '@nozbe/watermelondb'; import { Q } from '@nozbe/watermelondb';
@ -50,7 +50,8 @@ import { sanitizeLikeString } from '../../lib/database/utils';
import { CustomIcon } from '../../lib/Icons'; import { CustomIcon } from '../../lib/Icons';
import { IMessage } from '../../definitions/IMessage'; import { IMessage } from '../../definitions/IMessage';
import { forceJpgExtension } from './forceJpgExtension'; import { forceJpgExtension } from './forceJpgExtension';
import { IPreviewItem, IUser } from '../../definitions'; import { IBaseScreen, IPreviewItem, IUser, TSubscriptionModel, TThreadModel } from '../../definitions';
import { MasterDetailInsideStackParamList } from '../../stacks/MasterDetailStack/types';
if (isAndroid) { if (isAndroid) {
require('./EmojiKeyboard'); require('./EmojiKeyboard');
@ -63,18 +64,18 @@ const imagePickerConfig = {
forceJpg: true forceJpg: true
}; };
const libraryPickerConfig = { const libraryPickerConfig: Options = {
multiple: true, multiple: true,
compressVideoPreset: 'Passthrough', compressVideoPreset: 'Passthrough',
mediaType: 'any', mediaType: 'any',
forceJpg: true forceJpg: true
}; };
const videoPickerConfig = { const videoPickerConfig: Options = {
mediaType: 'video' mediaType: 'video'
}; };
export interface IMessageBoxProps { export interface IMessageBoxProps extends IBaseScreen<MasterDetailInsideStackParamList, any> {
rid: string; rid: string;
baseUrl: string; baseUrl: string;
message: IMessage; message: IMessage;
@ -97,7 +98,6 @@ export interface IMessageBoxProps {
theme: string; theme: string;
replyCancel(): void; replyCancel(): void;
showSend: boolean; showSend: boolean;
navigation: any;
children: JSX.Element; children: JSX.Element;
isMasterDetail: boolean; isMasterDetail: boolean;
showActionSheet: Function; showActionSheet: Function;
@ -118,7 +118,7 @@ interface IMessageBoxState {
commandPreview: IPreviewItem[]; commandPreview: IPreviewItem[];
showCommandPreview: boolean; showCommandPreview: boolean;
command: { command: {
appId?: any; appId?: string;
}; };
tshow: boolean; tshow: boolean;
mentionLoading: boolean; mentionLoading: boolean;
@ -132,17 +132,15 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
private focused: boolean; private focused: boolean;
private options: any; private imagePickerConfig: Options;
private imagePickerConfig: any; private libraryPickerConfig: Options;
private libraryPickerConfig: any; private videoPickerConfig: Options;
private videoPickerConfig: any; private room!: TSubscriptionModel;
private room: any; private thread!: TThreadModel;
private thread: any;
private unsubscribeFocus: any; private unsubscribeFocus: any;
@ -713,7 +711,8 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
chooseFromLibrary = async () => { chooseFromLibrary = async () => {
logEvent(events.ROOM_BOX_ACTION_LIBRARY); logEvent(events.ROOM_BOX_ACTION_LIBRARY);
try { try {
let attachments = (await ImagePicker.openPicker(this.libraryPickerConfig)) as ImageOrVideo[]; // The type can be video or photo, however the lib understands that it is just one of them.
let attachments = (await ImagePicker.openPicker(this.libraryPickerConfig)) as unknown as ImageOrVideo[];
attachments = attachments.map(att => forceJpgExtension(att)); attachments = attachments.map(att => forceJpgExtension(att));
this.openShareView(attachments); this.openShareView(attachments);
} catch (e) { } catch (e) {
@ -757,12 +756,12 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
openShareView = (attachments: any) => { openShareView = (attachments: any) => {
const { message, replyCancel, replyWithMention } = this.props; const { message, replyCancel, replyWithMention } = this.props;
// Start a thread with an attachment // Start a thread with an attachment
let { thread } = this; let value: TThreadModel | IMessage = this.thread;
if (replyWithMention) { if (replyWithMention) {
thread = message; value = message;
replyCancel(); replyCancel();
} }
Navigation.navigate('ShareView', { room: this.room, thread, attachments }); Navigation.navigate('ShareView', { room: this.room, value, attachments });
}; };
createDiscussion = () => { createDiscussion = () => {
@ -1060,7 +1059,7 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
const commandsPreviewAndMentions = !recording ? ( const commandsPreviewAndMentions = !recording ? (
<> <>
<CommandsPreview commandPreview={commandPreview} showCommandPreview={showCommandPreview} /> <CommandsPreview commandPreview={commandPreview} showCommandPreview={showCommandPreview} />
<Mentions mentions={mentions} trackingType={trackingType} theme={theme} loading={mentionLoading} /> <Mentions mentions={mentions} trackingType={trackingType} loading={mentionLoading} />
</> </>
) : null; ) : null;
@ -1071,7 +1070,6 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
username={user.username} username={user.username}
replying={replying} replying={replying}
getCustomEmoji={getCustomEmoji} getCustomEmoji={getCustomEmoji}
theme={theme}
/> />
) : null; ) : null;