-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Refactored Message component to use React.memo and re-render only what's necessary - Added a test mode to toggle markdown parse by long press drawer (it'll be removed in the next release)
- Loading branch information
1 parent
31cf0e5
commit 60418b7
Showing
57 changed files
with
15,504 additions
and
15,374 deletions.
There are no files selected for viewing
27,723 changes: 13,767 additions & 13,956 deletions
27,723
__tests__/__snapshots__/Storyshots.test.js.snap
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
import React from 'react'; | ||
import { | ||
View, Text, TouchableWithoutFeedback, ActivityIndicator, StyleSheet, SafeAreaView | ||
} from 'react-native'; | ||
import FastImage from 'react-native-fast-image'; | ||
import PropTypes from 'prop-types'; | ||
import Modal from 'react-native-modal'; | ||
import ImageViewer from 'react-native-image-zoom-viewer'; | ||
import VideoPlayer from 'react-native-video-controls'; | ||
|
||
import sharedStyles from '../views/Styles'; | ||
import { COLOR_WHITE } from '../constants/colors'; | ||
import { formatAttachmentUrl } from '../lib/utils'; | ||
|
||
const styles = StyleSheet.create({ | ||
safeArea: { | ||
flex: 1 | ||
}, | ||
modal: { | ||
margin: 0 | ||
}, | ||
titleContainer: { | ||
width: '100%', | ||
alignItems: 'center', | ||
marginVertical: 10 | ||
}, | ||
title: { | ||
color: COLOR_WHITE, | ||
textAlign: 'center', | ||
fontSize: 16, | ||
...sharedStyles.textSemibold | ||
}, | ||
description: { | ||
color: COLOR_WHITE, | ||
textAlign: 'center', | ||
fontSize: 14, | ||
...sharedStyles.textMedium | ||
}, | ||
indicator: { | ||
flex: 1 | ||
} | ||
}); | ||
|
||
const Indicator = React.memo(() => ( | ||
<ActivityIndicator style={styles.indicator} /> | ||
)); | ||
|
||
const ModalContent = React.memo(({ | ||
attachment, onClose, user, baseUrl | ||
}) => { | ||
if (attachment && attachment.image_url) { | ||
const url = formatAttachmentUrl(attachment.image_url, user.id, user.token, baseUrl); | ||
return ( | ||
<SafeAreaView style={styles.safeArea}> | ||
<TouchableWithoutFeedback onPress={onClose}> | ||
<View style={styles.titleContainer}> | ||
<Text style={styles.title}>{attachment.title}</Text> | ||
{attachment.description ? <Text style={styles.description}>{attachment.description}</Text> : null} | ||
</View> | ||
</TouchableWithoutFeedback> | ||
<ImageViewer | ||
imageUrls={[{ url }]} | ||
onClick={onClose} | ||
backgroundColor='transparent' | ||
enableSwipeDown | ||
onSwipeDown={onClose} | ||
renderIndicator={() => null} | ||
renderImage={props => <FastImage {...props} />} | ||
loadingRender={() => <Indicator />} | ||
/> | ||
</SafeAreaView> | ||
); | ||
} | ||
if (attachment && attachment.video_url) { | ||
const uri = formatAttachmentUrl(attachment.video_url, user.id, user.token, baseUrl); | ||
return ( | ||
<SafeAreaView style={styles.safeArea}> | ||
<VideoPlayer | ||
source={{ uri }} | ||
onBack={onClose} | ||
disableVolume | ||
/> | ||
</SafeAreaView> | ||
); | ||
} | ||
return null; | ||
}); | ||
|
||
const FileModal = React.memo(({ | ||
isVisible, onClose, attachment, user, baseUrl | ||
}) => ( | ||
<Modal | ||
style={styles.modal} | ||
isVisible={isVisible} | ||
onBackdropPress={onClose} | ||
onBackButtonPress={onClose} | ||
onSwipeComplete={onClose} | ||
swipeDirection={['up', 'left', 'right', 'down']} | ||
> | ||
<ModalContent attachment={attachment} onClose={onClose} user={user} baseUrl={baseUrl} /> | ||
</Modal> | ||
), (prevProps, nextProps) => prevProps.isVisible === nextProps.isVisible); | ||
|
||
FileModal.propTypes = { | ||
isVisible: PropTypes.bool, | ||
attachment: PropTypes.object, | ||
user: PropTypes.object, | ||
baseUrl: PropTypes.string, | ||
onClose: PropTypes.func | ||
}; | ||
FileModal.displayName = 'FileModal'; | ||
|
||
ModalContent.propTypes = { | ||
attachment: PropTypes.object, | ||
user: PropTypes.object, | ||
baseUrl: PropTypes.string, | ||
onClose: PropTypes.func | ||
}; | ||
ModalContent.displayName = 'FileModalContent'; | ||
|
||
export default FileModal; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
import React from 'react'; | ||
import { | ||
View, Text, FlatList, StyleSheet, SafeAreaView | ||
} from 'react-native'; | ||
import PropTypes from 'prop-types'; | ||
import Modal from 'react-native-modal'; | ||
import Touchable from 'react-native-platform-touchable'; | ||
|
||
import Emoji from './message/Emoji'; | ||
import I18n from '../i18n'; | ||
import { CustomIcon } from '../lib/Icons'; | ||
import sharedStyles from '../views/Styles'; | ||
import { COLOR_WHITE } from '../constants/colors'; | ||
|
||
const styles = StyleSheet.create({ | ||
titleContainer: { | ||
alignItems: 'center', | ||
paddingVertical: 10 | ||
}, | ||
title: { | ||
color: COLOR_WHITE, | ||
textAlign: 'center', | ||
fontSize: 16, | ||
...sharedStyles.textSemibold | ||
}, | ||
reactCount: { | ||
color: COLOR_WHITE, | ||
fontSize: 13, | ||
...sharedStyles.textRegular | ||
}, | ||
peopleReacted: { | ||
color: COLOR_WHITE, | ||
fontSize: 14, | ||
...sharedStyles.textMedium | ||
}, | ||
peopleItemContainer: { | ||
flex: 1, | ||
flexDirection: 'column', | ||
justifyContent: 'center' | ||
}, | ||
emojiContainer: { | ||
width: 50, | ||
height: 50, | ||
alignItems: 'center', | ||
justifyContent: 'center' | ||
}, | ||
itemContainer: { | ||
height: 50, | ||
flexDirection: 'row' | ||
}, | ||
listContainer: { | ||
flex: 1 | ||
}, | ||
closeButton: { | ||
position: 'absolute', | ||
left: 0, | ||
top: 10, | ||
color: COLOR_WHITE | ||
} | ||
}); | ||
const standardEmojiStyle = { fontSize: 20 }; | ||
const customEmojiStyle = { width: 20, height: 20 }; | ||
|
||
const Item = React.memo(({ item, user, baseUrl }) => { | ||
const count = item.usernames.length; | ||
let usernames = item.usernames.slice(0, 3) | ||
.map(username => (username === user.username ? I18n.t('you') : username)).join(', '); | ||
if (count > 3) { | ||
usernames = `${ usernames } ${ I18n.t('and_more') } ${ count - 3 }`; | ||
} else { | ||
usernames = usernames.replace(/,(?=[^,]*$)/, ` ${ I18n.t('and') }`); | ||
} | ||
return ( | ||
<View style={styles.itemContainer}> | ||
<View style={styles.emojiContainer}> | ||
<Emoji | ||
content={item.emoji} | ||
standardEmojiStyle={standardEmojiStyle} | ||
customEmojiStyle={customEmojiStyle} | ||
baseUrl={baseUrl} | ||
/> | ||
</View> | ||
<View style={styles.peopleItemContainer}> | ||
<Text style={styles.reactCount}> | ||
{count === 1 ? I18n.t('1_person_reacted') : I18n.t('N_people_reacted', { n: count })} | ||
</Text> | ||
<Text style={styles.peopleReacted}>{ usernames }</Text> | ||
</View> | ||
</View> | ||
); | ||
}); | ||
|
||
const ModalContent = React.memo(({ message, onClose, ...props }) => { | ||
if (message && message.reactions) { | ||
return ( | ||
<SafeAreaView style={{ flex: 1 }}> | ||
<Touchable onPress={onClose}> | ||
<View style={styles.titleContainer}> | ||
<CustomIcon | ||
style={styles.closeButton} | ||
name='cross' | ||
size={20} | ||
/> | ||
<Text style={styles.title}>{I18n.t('Reactions')}</Text> | ||
</View> | ||
</Touchable> | ||
<FlatList | ||
style={styles.listContainer} | ||
data={message.reactions} | ||
renderItem={({ item }) => <Item item={item} {...props} />} | ||
keyExtractor={item => item.emoji} | ||
/> | ||
</SafeAreaView> | ||
); | ||
} | ||
return null; | ||
}); | ||
|
||
const ReactionsModal = React.memo(({ isVisible, onClose, ...props }) => ( | ||
<Modal | ||
isVisible={isVisible} | ||
onBackdropPress={onClose} | ||
onBackButtonPress={onClose} | ||
backdropOpacity={0.8} | ||
onSwipeComplete={onClose} | ||
swipeDirection={['up', 'left', 'right', 'down']} | ||
> | ||
<ModalContent onClose={onClose} {...props} /> | ||
</Modal> | ||
), (prevProps, nextProps) => prevProps.isVisible === nextProps.isVisible); | ||
|
||
ReactionsModal.propTypes = { | ||
isVisible: PropTypes.bool, | ||
onClose: PropTypes.func | ||
}; | ||
ReactionsModal.displayName = 'ReactionsModal'; | ||
|
||
ModalContent.propTypes = { | ||
message: PropTypes.object, | ||
onClose: PropTypes.func | ||
}; | ||
ModalContent.displayName = 'ReactionsModalContent'; | ||
|
||
Item.propTypes = { | ||
item: PropTypes.object, | ||
user: PropTypes.object, | ||
baseUrl: PropTypes.string | ||
}; | ||
Item.displayName = 'ReactionsModalItem'; | ||
|
||
export default ReactionsModal; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import React from 'react'; | ||
import isEqual from 'lodash/isEqual'; | ||
import PropTypes from 'prop-types'; | ||
|
||
import Image from './Image'; | ||
import Audio from './Audio'; | ||
import Video from './Video'; | ||
import Reply from './Reply'; | ||
|
||
const Attachments = React.memo(({ | ||
attachments, timeFormat, user, baseUrl, useMarkdown, onOpenFileModal, getCustomEmoji | ||
}) => { | ||
if (!attachments || attachments.length === 0) { | ||
return null; | ||
} | ||
|
||
return attachments.map((file, index) => { | ||
if (file.image_url) { | ||
return <Image key={file.image_url} file={file} user={user} baseUrl={baseUrl} onOpenFileModal={onOpenFileModal} getCustomEmoji={getCustomEmoji} useMarkdown={useMarkdown} />; | ||
} | ||
if (file.audio_url) { | ||
return <Audio key={file.audio_url} file={file} user={user} baseUrl={baseUrl} getCustomEmoji={getCustomEmoji} useMarkdown={useMarkdown} />; | ||
} | ||
if (file.video_url) { | ||
return <Video key={file.video_url} file={file} user={user} baseUrl={baseUrl} onOpenFileModal={onOpenFileModal} getCustomEmoji={getCustomEmoji} useMarkdown={useMarkdown} />; | ||
} | ||
|
||
// eslint-disable-next-line react/no-array-index-key | ||
return <Reply key={index} index={index} attachment={file} timeFormat={timeFormat} user={user} baseUrl={baseUrl} getCustomEmoji={getCustomEmoji} useMarkdown={useMarkdown} />; | ||
}); | ||
}, (prevProps, nextProps) => isEqual(prevProps.attachments, nextProps.attachments)); | ||
|
||
Attachments.propTypes = { | ||
attachments: PropTypes.array, | ||
timeFormat: PropTypes.string, | ||
user: PropTypes.object, | ||
baseUrl: PropTypes.string, | ||
useMarkdown: PropTypes.bool, | ||
onOpenFileModal: PropTypes.func, | ||
getCustomEmoji: PropTypes.func | ||
}; | ||
Attachments.displayName = 'MessageAttachments'; | ||
|
||
export default Attachments; |
Oops, something went wrong.