Commit 5752b865 authored by Diego Mello's avatar Diego Mello Committed by GitHub

Several fixes for 1.2.1 (#448)

* Fix user.roles

* Better onLongPress handle on messages

* Indicator position

* Fix role undefined in system messages

* Add baseUrl in case of file attachments

* Join room fixed

* RoomView params

* Broadcast fixes

* Add server layout changes

* Use native images

* Subscribe to not joined channels

* Fix alerts without i18n

* Tests updated
parent c17c2954
export const RectButton = () => 'View';
export const State = () => 'View';
export const LongPressGestureHandler = () => 'View';
......@@ -184,6 +184,8 @@ repositories {
}
dependencies {
implementation project(':react-native-device-info')
implementation project(':react-native-gesture-handler')
implementation project(':react-native-image-crop-picker')
implementation project(':react-native-i18n')
implementation project(':react-native-fabric')
......
......@@ -25,6 +25,8 @@ import com.wix.reactnativenotifications.core.AppLifecycleFacade;
import com.wix.reactnativenotifications.core.JsIOHelper;
import com.wix.reactnativenotifications.core.notification.INotificationsApplication;
import com.wix.reactnativenotifications.core.notification.IPushNotification;
import com.swmansion.gesturehandler.react.RNGestureHandlerPackage;
import com.learnium.RNDeviceInfo.RNDeviceInfo;
import java.util.Arrays;
import java.util.List;
......@@ -57,6 +59,8 @@ public class MainApplication extends NavigationApplication implements INotificat
public List<ReactPackage> createAdditionalReactPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new RNDeviceInfo(),
new RNGestureHandlerPackage(),
new PickerPackage(),
new SvgPackage(),
new VectorIconsPackage(),
......
rootProject.name = 'RocketChatRN'
include ':react-native-device-info'
project(':react-native-device-info').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-device-info/android')
include ':react-native-gesture-handler'
project(':react-native-gesture-handler').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-gesture-handler/android')
include ':@remobile/react-native-toast'
project(':@remobile/react-native-toast').projectDir = new File(rootProject.projectDir, '../node_modules/@remobile/react-native-toast/android')
include ':rn-fetch-blob'
......
......@@ -266,7 +266,7 @@ export default class MessageBox extends React.PureComponent {
await RocketChat.sendFileMessage(this.props.rid, fileInfo);
} catch (e) {
if (e && e.error === 'error-file-too-large') {
return Alert.alert('File is too large!');
return Alert.alert(I18n.t(e.error));
}
log('finishAudioMessage', e);
}
......
import React from 'react';
import PropTypes from 'prop-types';
import { View, StyleSheet, TouchableOpacity, Text, Easing, Image } from 'react-native';
import { View, StyleSheet, Text, Easing, Image } from 'react-native';
import Video from 'react-native-video';
import Slider from 'react-native-slider';
import moment from 'moment';
import { BorderlessButton } from 'react-native-gesture-handler';
import Markdown from './Markdown';
......@@ -116,7 +117,7 @@ export default class Audio extends React.PureComponent {
paused={paused}
repeat={false}
/>
<TouchableOpacity
<BorderlessButton
style={styles.playPauseButton}
onPress={() => this.togglePlayPause()}
>
......@@ -125,7 +126,7 @@ export default class Audio extends React.PureComponent {
<Image source={{ uri: 'play' }} style={styles.playPauseImage} /> :
<Image source={{ uri: 'pause' }} style={styles.playPauseImage} />
}
</TouchableOpacity>
</BorderlessButton>
<Slider
style={styles.slider}
value={this.state.currentTime}
......
import PropTypes from 'prop-types';
import React from 'react';
import FastImage from 'react-native-fast-image';
import { TouchableOpacity } from 'react-native';
import { RectButton } from 'react-native-gesture-handler';
import PhotoModal from './PhotoModal';
import Markdown from './Markdown';
......@@ -20,6 +20,12 @@ export default class extends React.PureComponent {
state = { modalVisible: false };
onPressButton() {
this.setState({
modalVisible: true
});
}
getDescription() {
const {
file, customEmojis, baseUrl, user
......@@ -29,13 +35,12 @@ export default class extends React.PureComponent {
}
}
_onPressButton() {
this.setState({
modalVisible: true
});
isPressed = (state) => {
this.setState({ isPressed: state });
}
render() {
const { isPressed } = this.state;
const { baseUrl, file, user } = this.props;
const img = `${ baseUrl }${ file.image_url }?rc_uid=${ user.id }&rc_token=${ user.token }`;
......@@ -45,18 +50,20 @@ export default class extends React.PureComponent {
return (
[
<TouchableOpacity
<RectButton
key='image'
onPress={() => this._onPressButton()}
onPress={() => this.onPressButton()}
onActiveStateChange={this.isPressed}
style={styles.imageContainer}
underlayColor='#fff'
>
<FastImage
style={styles.image}
style={[styles.image, isPressed && { opacity: 0.5 }]}
source={{ uri: encodeURI(img) }}
resizeMode={FastImage.resizeMode.cover}
/>
{this.getDescription()}
</TouchableOpacity>,
</RectButton>,
<PhotoModal
key='modal'
title={file.title}
......
......@@ -52,17 +52,13 @@ export default class Markdown extends React.Component {
};
}
return (
<Text
key={key}
onPress={() => alert(`Username ${ content }`)}
style={mentionStyle}
>
<Text style={mentionStyle} key={key}>
&nbsp;{content}&nbsp;
</Text>
);
},
hashtag: node => (
<Text key={node.key} onPress={() => alert(`Room #${ node.content }`)} style={styles.mention}>
<Text key={node.key} style={styles.mention}>
&nbsp;#{node.content}&nbsp;
</Text>
),
......
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { View, Text, TouchableOpacity, ViewPropTypes, Image as ImageRN } from 'react-native';
import { View, Text, ViewPropTypes, Image as ImageRN } from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';
import moment from 'moment';
import { KeyboardUtils } from 'react-native-keyboard-input';
import { State, RectButton, LongPressGestureHandler } from 'react-native-gesture-handler';
import Image from './Image';
import User from './User';
......@@ -16,7 +17,6 @@ import Reply from './Reply';
import ReactionsModal from './ReactionsModal';
import Emoji from './Emoji';
import styles from './styles';
import Touch from '../../utils/touch';
import I18n from '../../i18n';
import messagesStatus from '../../constants/messagesStatus';
......@@ -247,25 +247,31 @@ export default class Message extends PureComponent {
renderReaction = (reaction) => {
const reacted = reaction.usernames.findIndex(item => item.value === this.props.user.username) !== -1;
const reactedContainerStyle = reacted && styles.reactedContainer;
const underlayColor = reacted ? '#fff' : '#e1e5e8';
return (
<TouchableOpacity
onPress={() => this.props.onReactionPress(reaction.emoji)}
onLongPress={this.props.onReactionLongPress}
<LongPressGestureHandler
key={reaction.emoji}
testID={`message-reaction-${ reaction.emoji }`}
onHandlerStateChange={({ nativeEvent }) => nativeEvent.state === State.ACTIVE && this.props.onReactionLongPress()}
>
<View style={[styles.reactionContainer, reactedContainerStyle]}>
<Emoji
content={reaction.emoji}
customEmojis={this.props.customEmojis}
standardEmojiStyle={styles.reactionEmoji}
customEmojiStyle={styles.reactionCustomEmoji}
baseUrl={this.props.baseUrl}
/>
<Text style={styles.reactionCount}>{ reaction.usernames.length }</Text>
</View>
</TouchableOpacity>
<RectButton
onPress={() => this.props.onReactionPress(reaction.emoji)}
testID={`message-reaction-${ reaction.emoji }`}
style={[styles.reactionButton, reacted && { backgroundColor: '#e8f2ff' }]}
activeOpacity={0.8}
underlayColor={underlayColor}
>
<View style={[styles.reactionContainer, reacted && styles.reactedContainer]}>
<Emoji
content={reaction.emoji}
customEmojis={this.props.customEmojis}
standardEmojiStyle={styles.reactionEmoji}
customEmojiStyle={styles.reactionCustomEmoji}
baseUrl={this.props.baseUrl}
/>
<Text style={styles.reactionCount}>{ reaction.usernames.length }</Text>
</View>
</RectButton>
</LongPressGestureHandler>
);
}
......@@ -277,14 +283,18 @@ export default class Message extends PureComponent {
return (
<View style={styles.reactionsContainer}>
{reactions.map(this.renderReaction)}
<TouchableOpacity
<RectButton
onPress={this.props.toggleReactionPicker}
key='message-add-reaction'
testID='message-add-reaction'
style={styles.reactionContainer}
style={styles.reactionButton}
activeOpacity={0.8}
underlayColor='#e1e5e8'
>
<ImageRN source={{ uri: 'add_reaction' }} style={styles.addReaction} />
</TouchableOpacity>
<View style={styles.reactionContainer}>
<ImageRN source={{ uri: 'add_reaction' }} style={styles.addReaction} />
</View>
</RectButton>
</View>
);
}
......@@ -292,15 +302,15 @@ export default class Message extends PureComponent {
renderBroadcastReply() {
if (this.props.broadcast && !this.isOwn()) {
return (
<Touch
<RectButton
onPress={this.props.replyBroadcast}
style={styles.broadcastButton}
activeOpacity={0.5}
underlayColor='#fff'
>
<View style={styles.broadcastButtonContainer}>
<ImageRN source={{ uri: 'reply' }} style={styles.broadcastButtonIcon} />
<Text style={styles.broadcastButtonText}>Reply</Text>
</View>
</Touch>
<ImageRN source={{ uri: 'reply' }} style={styles.broadcastButtonIcon} />
<Text style={styles.broadcastButtonText}>{I18n.t('Reply')}</Text>
</RectButton>
);
}
return null;
......@@ -313,39 +323,46 @@ export default class Message extends PureComponent {
const accessibilityLabel = I18n.t('Message_accessibility', { user: author.username, time: moment(ts).format(timeFormat), message: msg });
return (
<Touch
onPress={this.onPress}
onLongPress={onLongPress}
disabled={this.isInfoMessage() || this.hasError() || archived}
accessibilityLabel={accessibilityLabel}
style={[styles.container, header && { marginBottom: 10 }]}
<LongPressGestureHandler
onHandlerStateChange={({ nativeEvent }) => nativeEvent.state === State.ACTIVE && onLongPress()}
>
<View style={[styles.message, editing && styles.editing, style]}>
<View style={styles.flex}>
{this.renderError()}
{this.renderAvatar()}
<View style={[styles.messageContent, header && styles.hasHeader, this.isTemp() && styles.temp]}>
{this.renderUsername()}
{this.renderContent()}
{this.renderAttachment()}
{this.renderUrl()}
{this.renderReactions()}
{this.renderBroadcastReply()}
<RectButton
enabled={!(this.isInfoMessage() || this.hasError() || archived)}
style={[styles.container, header && { marginBottom: 10 }]}
onPress={this.onPress}
activeOpacity={0.8}
underlayColor='#e1e5e8'
>
<View
style={[styles.message, editing && styles.editing, style]}
accessibilityLabel={accessibilityLabel}
>
<View style={styles.flex}>
{this.renderError()}
{this.renderAvatar()}
<View style={[styles.messageContent, header && styles.hasHeader, this.isTemp() && styles.temp]}>
{this.renderUsername()}
{this.renderContent()}
{this.renderAttachment()}
{this.renderUrl()}
{this.renderReactions()}
{this.renderBroadcastReply()}
</View>
</View>
{reactionsModal ?
<ReactionsModal
isVisible={reactionsModal}
reactions={reactions}
user={user}
customEmojis={customEmojis}
baseUrl={baseUrl}
close={closeReactions}
/>
: null
}
</View>
{reactionsModal ?
<ReactionsModal
isVisible={reactionsModal}
reactions={reactions}
user={user}
customEmojis={customEmojis}
baseUrl={baseUrl}
close={closeReactions}
/>
: null
}
</View>
</Touch>
</RectButton>
</LongPressGestureHandler>
);
}
}
import React from 'react';
import { View, StyleSheet } from 'react-native';
import PropTypes from 'prop-types';
const styles = StyleSheet.create({
quoteSign: {
borderWidth: 2,
borderRadius: 4,
height: '100%',
marginRight: 5
}
});
const QuoteMark = ({ color }) => <View style={[styles.quoteSign, { borderColor: color || '#a0a0a0' }]} />;
QuoteMark.propTypes = {
color: PropTypes.string
};
export default QuoteMark;
......@@ -2,10 +2,10 @@ import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import PropTypes from 'prop-types';
import moment from 'moment';
import { RectButton } from 'react-native-gesture-handler';
import Markdown from './Markdown';
import openLink from '../../utils/openLink';
import Touch from '../../utils/touch';
const styles = StyleSheet.create({
button: {
......@@ -13,13 +13,13 @@ const styles = StyleSheet.create({
flexDirection: 'row',
alignItems: 'center',
marginTop: 15,
alignSelf: 'flex-end'
alignSelf: 'flex-end',
backgroundColor: '#f3f4f5'
},
attachmentContainer: {
flex: 1,
borderRadius: 4,
flexDirection: 'column',
backgroundColor: '#f3f4f5',
padding: 15
},
authorContainer: {
......@@ -55,12 +55,15 @@ const styles = StyleSheet.create({
}
});
const onPress = (attachment) => {
const url = attachment.title_link || attachment.author_link;
const onPress = (attachment, baseUrl, user) => {
let url = attachment.title_link || attachment.author_link;
if (!url) {
return;
}
openLink(attachment.title_link || attachment.author_link);
if (attachment.type === 'file') {
url = `${ baseUrl }${ url }?rc_uid=${ user.id }&rc_token=${ user.token }`;
}
openLink(url);
};
const Reply = ({
......@@ -91,9 +94,19 @@ const Reply = ({
);
};
const renderText = () => (
attachment.text ? <Markdown msg={attachment.text} customEmojis={customEmojis} baseUrl={baseUrl} username={user.username} /> : null
);
const renderText = () => {
const text = attachment.text || attachment.title;
if (text) {
return (
<Markdown
msg={text}
customEmojis={customEmojis}
baseUrl={baseUrl}
username={user.username}
/>
);
}
};
const renderFields = () => {
if (!attachment.fields) {
......@@ -113,16 +126,18 @@ const Reply = ({
};
return (
<Touch
onPress={() => onPress(attachment)}
<RectButton
onPress={() => onPress(attachment, baseUrl, user)}
style={[styles.button, index > 0 && styles.marginTop]}
activeOpacity={0.5}
underlayColor='#fff'
>
<View style={styles.attachmentContainer}>
{renderTitle()}
{renderText()}
{renderFields()}
</View>
</Touch>
</RectButton>
);
};
......
......@@ -2,9 +2,9 @@ import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import PropTypes from 'prop-types';
import FastImage from 'react-native-fast-image';
import { RectButton } from 'react-native-gesture-handler';
import openLink from '../../utils/openLink';
import Touch from '../../utils/touch';
const styles = StyleSheet.create({
button: {
......@@ -58,16 +58,19 @@ const Url = ({ url, index }) => {
return null;
}
return (
<Touch onPress={() => onPress(url.url)} style={[styles.button, index > 0 && styles.marginTop]}>
<View style={styles.container}>
{url.image ? <FastImage source={{ uri: url.image }} style={styles.image} resizeMode={FastImage.resizeMode.cover} /> : null}
<View style={styles.textContainer}>
<Text style={styles.url} numberOfLines={1}>{url.url}</Text>
<Text style={styles.title} numberOfLines={2}>{url.title}</Text>
<Text style={styles.description} numberOfLines={2}>{url.description}</Text>
</View>
<RectButton
onPress={() => onPress(url.url)}
style={[styles.button, index > 0 && styles.marginTop, styles.container]}
activeOpacity={0.5}
underlayColor='#fff'
>
{url.image ? <FastImage source={{ uri: url.image }} style={styles.image} resizeMode={FastImage.resizeMode.cover} /> : null}
<View style={styles.textContainer}>
<Text style={styles.url} numberOfLines={1}>{url.url}</Text>
<Text style={styles.title} numberOfLines={2}>{url.title}</Text>
<Text style={styles.description} numberOfLines={2}>{url.description}</Text>
</View>
</Touch>
</RectButton>
);
};
......
......@@ -36,8 +36,7 @@ export default class User extends React.PureComponent {
username: PropTypes.string,
alias: PropTypes.string,
ts: PropTypes.instanceOf(Date),
temp: PropTypes.bool,
onPress: PropTypes.func
temp: PropTypes.bool
}
render() {
......@@ -55,7 +54,7 @@ export default class User extends React.PureComponent {
return (
<View style={styles.usernameView}>
<Text onPress={this.props.onPress} style={styles.username}>
<Text style={styles.username}>
{alias || username}
</Text>
{aliasUsername}
......
import React from 'react';
import PropTypes from 'prop-types';
import { StyleSheet, TouchableOpacity, Image, Platform, View } from 'react-native';
import { StyleSheet, Image, Platform, View } from 'react-native';
import Modal from 'react-native-modal';
import VideoPlayer from 'react-native-video-controls';
import { RectButton } from 'react-native-gesture-handler';
import Markdown from './Markdown';
import openLink from '../../utils/openLink';
......@@ -70,15 +72,17 @@ export default class Video extends React.PureComponent {
return (
[
<View key='button'>
<TouchableOpacity
<RectButton
style={styles.button}
onPress={() => this.open()}
activeOpacity={0.5}
underlayColor='#fff'
>
<Image
source={{ uri: 'play_video' }}
style={styles.image}
/>
</TouchableOpacity>
</RectButton>
<Markdown msg={description} customEmojis={customEmojis} baseUrl={baseUrl} username={user.username} />
</View>,
<Modal
......
......@@ -146,7 +146,7 @@ export default class MessageContainer extends React.Component {
item, message, editing, user, style, archived, baseUrl, customEmojis, useRealName, broadcast
} = this.props;
const {
msg, ts, attachments, urls, reactions, t, status, avatar, u, alias, editedBy
msg, ts, attachments, urls, reactions, t, status, avatar, u, alias, editedBy, role
} = item;
const isEditing = message._id === item._id && editing;
return (
......@@ -173,6 +173,7 @@ export default class MessageContainer extends React.Component {
customEmojis={customEmojis}
reactionsModal={this.state.reactionsModal}
useRealName={useRealName}
role={role}
closeReactions={this.closeReactions}
onErrorPress={this.onErrorPress}
onLongPress={this.onLongPress}
......