[IMPROVEMENT] Tap on avatar/username/channel to show info (#1097)
* added feature to tab on mentions and avtar * fixed lint * removed room param from roomActionView * removed room param from roomActionView * Update tests
This commit is contained in:
parent
80570b0591
commit
857d23ee88
File diff suppressed because it is too large
Load Diff
|
@ -28,6 +28,7 @@ const Content = React.memo((props) => {
|
|||
numberOfLines={props.tmid ? 1 : 0}
|
||||
getCustomEmoji={props.getCustomEmoji}
|
||||
useMarkdown={props.useMarkdown}
|
||||
navToRoomInfo={props.navToRoomInfo}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -50,6 +51,7 @@ Content.propTypes = {
|
|||
user: PropTypes.object,
|
||||
mentions: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
|
||||
channels: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
|
||||
navToRoomInfo: PropTypes.func,
|
||||
getCustomEmoji: PropTypes.func
|
||||
};
|
||||
Content.displayName = 'MessageContent';
|
||||
|
|
|
@ -48,7 +48,7 @@ const emojiCount = (str) => {
|
|||
};
|
||||
|
||||
const Markdown = React.memo(({
|
||||
msg, style, rules, baseUrl, username, isEdited, numberOfLines, mentions, channels, getCustomEmoji, useMarkdown = true
|
||||
msg, style, rules, baseUrl, username, isEdited, numberOfLines, mentions, channels, getCustomEmoji, useMarkdown = true, navToRoomInfo
|
||||
}) => {
|
||||
if (!msg) {
|
||||
return null;
|
||||
|
@ -92,8 +92,17 @@ const Markdown = React.memo(({
|
|||
};
|
||||
}
|
||||
if (mentions && mentions.length && mentions.findIndex(mention => mention.username === content) !== -1) {
|
||||
const index = mentions.findIndex(mention => mention.username === content);
|
||||
const navParam = {
|
||||
t: 'd',
|
||||
rid: mentions[index]._id
|
||||
};
|
||||
return (
|
||||
<Text style={mentionStyle} key={key}>
|
||||
<Text
|
||||
style={mentionStyle}
|
||||
key={key}
|
||||
onPress={() => navToRoomInfo(navParam)}
|
||||
>
|
||||
{content}
|
||||
</Text>
|
||||
);
|
||||
|
@ -103,8 +112,17 @@ const Markdown = React.memo(({
|
|||
hashtag: (node) => {
|
||||
const { content, key } = node;
|
||||
if (channels && channels.length && channels.findIndex(channel => channel.name === content) !== -1) {
|
||||
const index = channels.findIndex(channel => channel.name === content);
|
||||
const navParam = {
|
||||
t: 'c',
|
||||
rid: channels[index]._id
|
||||
};
|
||||
return (
|
||||
<Text key={key} style={styles.mention}>
|
||||
<Text
|
||||
key={key}
|
||||
style={styles.mention}
|
||||
onPress={() => navToRoomInfo(navParam)}
|
||||
>
|
||||
#{content}
|
||||
</Text>
|
||||
);
|
||||
|
@ -161,7 +179,8 @@ Markdown.propTypes = {
|
|||
useMarkdown: PropTypes.bool,
|
||||
mentions: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
|
||||
channels: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
|
||||
getCustomEmoji: PropTypes.func
|
||||
getCustomEmoji: PropTypes.func,
|
||||
navToRoomInfo: PropTypes.func
|
||||
};
|
||||
Markdown.displayName = 'MessageMarkdown';
|
||||
|
||||
|
|
|
@ -1,24 +1,34 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { TouchableOpacity } from 'react-native';
|
||||
|
||||
import Avatar from '../Avatar';
|
||||
import styles from './styles';
|
||||
|
||||
const MessageAvatar = React.memo(({
|
||||
isHeader, avatar, author, baseUrl, user, small
|
||||
isHeader, avatar, author, baseUrl, user, small, navToRoomInfo
|
||||
}) => {
|
||||
if (isHeader) {
|
||||
const navParam = {
|
||||
t: 'd',
|
||||
rid: author._id
|
||||
};
|
||||
return (
|
||||
<Avatar
|
||||
style={small ? styles.avatarSmall : styles.avatar}
|
||||
text={avatar ? '' : author.username}
|
||||
size={small ? 20 : 36}
|
||||
borderRadius={small ? 2 : 4}
|
||||
avatar={avatar}
|
||||
baseUrl={baseUrl}
|
||||
userId={user.id}
|
||||
token={user.token}
|
||||
/>
|
||||
<TouchableOpacity
|
||||
onPress={() => navToRoomInfo(navParam)}
|
||||
disabled={author._id === user.id}
|
||||
>
|
||||
<Avatar
|
||||
style={small ? styles.avatarSmall : styles.avatar}
|
||||
text={avatar ? '' : author.username}
|
||||
size={small ? 20 : 36}
|
||||
borderRadius={small ? 2 : 4}
|
||||
avatar={avatar}
|
||||
baseUrl={baseUrl}
|
||||
userId={user.id}
|
||||
token={user.token}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
@ -30,7 +40,8 @@ MessageAvatar.propTypes = {
|
|||
author: PropTypes.obj,
|
||||
baseUrl: PropTypes.string,
|
||||
user: PropTypes.obj,
|
||||
small: PropTypes.bool
|
||||
small: PropTypes.bool,
|
||||
navToRoomInfo: PropTypes.func
|
||||
};
|
||||
MessageAvatar.displayName = 'MessageAvatar';
|
||||
|
||||
|
|
|
@ -39,7 +39,8 @@ export default class MessageContainer extends React.Component {
|
|||
toggleReactionPicker: PropTypes.func,
|
||||
fetchThreadName: PropTypes.func,
|
||||
onOpenFileModal: PropTypes.func,
|
||||
onReactionLongPress: PropTypes.func
|
||||
onReactionLongPress: PropTypes.func,
|
||||
navToRoomInfo: PropTypes.func
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
|
@ -199,7 +200,7 @@ export default class MessageContainer extends React.Component {
|
|||
|
||||
render() {
|
||||
const {
|
||||
item, user, style, archived, baseUrl, useRealName, broadcast, fetchThreadName, customThreadTimeFormat, onOpenFileModal, timeFormat, useMarkdown, isReadReceiptEnabled, autoTranslateRoom, autoTranslateLanguage
|
||||
item, user, style, archived, baseUrl, useRealName, broadcast, fetchThreadName, customThreadTimeFormat, onOpenFileModal, timeFormat, useMarkdown, isReadReceiptEnabled, autoTranslateRoom, autoTranslateLanguage, navToRoomInfo
|
||||
} = this.props;
|
||||
const {
|
||||
_id, msg, ts, attachments, urls, reactions, t, avatar, u, alias, editedBy, role, drid, dcount, dlm, tmid, tcount, tlm, tmsg, mentions, channels, unread, autoTranslate: autoTranslateMessage
|
||||
|
@ -263,6 +264,7 @@ export default class MessageContainer extends React.Component {
|
|||
onDiscussionPress={this.onDiscussionPress}
|
||||
onOpenFileModal={onOpenFileModal}
|
||||
getCustomEmoji={getCustomEmoji}
|
||||
navToRoomInfo={navToRoomInfo}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -640,6 +640,10 @@ const RocketChat = {
|
|||
// RC 0.48.0
|
||||
return this.sdk.get('users.info', { userId });
|
||||
},
|
||||
getRoomInfo(roomId) {
|
||||
// RC 0.72.0
|
||||
return this.sdk.get('rooms.info', { roomId });
|
||||
},
|
||||
getRoomMemberId(rid, currentUserId) {
|
||||
if (rid === `${ currentUserId }${ currentUserId }`) {
|
||||
return currentUserId;
|
||||
|
|
|
@ -184,7 +184,7 @@ class RoomActionsView extends React.Component {
|
|||
name: I18n.t('Room_Info'),
|
||||
route: 'RoomInfoView',
|
||||
// forward room only if room isn't joined
|
||||
params: { rid, t, room: joined ? null : room },
|
||||
params: { rid, t },
|
||||
testID: 'room-actions-info'
|
||||
}],
|
||||
renderItem: this.renderRoomInfo
|
||||
|
|
|
@ -9,7 +9,7 @@ import Status from '../../containers/Status';
|
|||
import Avatar from '../../containers/Avatar';
|
||||
import styles from './styles';
|
||||
import sharedStyles from '../Styles';
|
||||
import database, { safeAddListener } from '../../lib/realm';
|
||||
import database from '../../lib/realm';
|
||||
import RocketChat from '../../lib/rocketchat';
|
||||
import RoomTypeIcon from '../../containers/RoomTypeIcon';
|
||||
import I18n from '../../i18n';
|
||||
|
@ -20,8 +20,8 @@ import log from '../../utils/log';
|
|||
const PERMISSION_EDIT_ROOM = 'edit-room';
|
||||
|
||||
const camelize = str => str.replace(/^(.)/, (match, chr) => chr.toUpperCase());
|
||||
const getRoomTitle = room => (room.t === 'd'
|
||||
? <Text testID='room-info-view-name' style={styles.roomTitle}>{room.fname}</Text>
|
||||
const getRoomTitle = (room, type, name) => (type === 'd'
|
||||
? <Text testID='room-info-view-name' style={styles.roomTitle}>{name}</Text>
|
||||
: (
|
||||
<View style={styles.roomTitleRow}>
|
||||
<RoomTypeIcon type={room.prid ? 'discussion' : room.t} key='room-info-type' />
|
||||
|
@ -59,28 +59,18 @@ class RoomInfoView extends React.Component {
|
|||
constructor(props) {
|
||||
super(props);
|
||||
this.rid = props.navigation.getParam('rid');
|
||||
const room = props.navigation.getParam('room');
|
||||
this.t = props.navigation.getParam('t');
|
||||
this.rooms = database.objects('subscriptions').filtered('rid = $0', this.rid);
|
||||
this.roles = database.objects('roles');
|
||||
this.sub = {
|
||||
unsubscribe: () => {}
|
||||
};
|
||||
this.state = {
|
||||
room: this.rooms[0] || room || {},
|
||||
room: {},
|
||||
roomUser: {}
|
||||
};
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
safeAddListener(this.rooms, this.updateRoom);
|
||||
const { room } = this.state;
|
||||
const permissions = RocketChat.hasPermission([PERMISSION_EDIT_ROOM], room.rid);
|
||||
if (permissions[PERMISSION_EDIT_ROOM] && !room.prid) {
|
||||
const { navigation } = this.props;
|
||||
navigation.setParams({ showEdit: true });
|
||||
}
|
||||
|
||||
if (this.t === 'd') {
|
||||
const { user } = this.props;
|
||||
const roomUserId = RocketChat.getRoomMemberId(this.rid, user.id);
|
||||
|
@ -92,11 +82,30 @@ class RoomInfoView extends React.Component {
|
|||
} catch (error) {
|
||||
log('err_get_user_info', error);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const rooms = database.objects('subscriptions').filtered('rid = $0', this.rid);
|
||||
let room = {};
|
||||
if (rooms.length > 0) {
|
||||
this.setState({ room: rooms[0] });
|
||||
[room] = rooms;
|
||||
} else {
|
||||
try {
|
||||
const result = await RocketChat.getRoomInfo(this.rid);
|
||||
if (result.success) {
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
room = result.room;
|
||||
this.setState({ room });
|
||||
}
|
||||
} catch (error) {
|
||||
log('err_get_room_info', error);
|
||||
}
|
||||
}
|
||||
const permissions = RocketChat.hasPermission([PERMISSION_EDIT_ROOM], room.rid);
|
||||
if (permissions[PERMISSION_EDIT_ROOM] && !room.prid) {
|
||||
const { navigation } = this.props;
|
||||
navigation.setParams({ showEdit: true });
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.rooms.removeAllListeners();
|
||||
}
|
||||
|
||||
getRoleDescription = (id) => {
|
||||
|
@ -107,10 +116,7 @@ class RoomInfoView extends React.Component {
|
|||
return null;
|
||||
}
|
||||
|
||||
isDirect = () => {
|
||||
const { room: { t } } = this.state;
|
||||
return t === 'd';
|
||||
}
|
||||
isDirect = () => this.t === 'd'
|
||||
|
||||
updateRoom = () => {
|
||||
if (this.rooms.length > 0) {
|
||||
|
@ -181,15 +187,15 @@ class RoomInfoView extends React.Component {
|
|||
|
||||
return (
|
||||
<Avatar
|
||||
text={room.name}
|
||||
text={room.name || roomUser.username}
|
||||
size={100}
|
||||
style={styles.avatar}
|
||||
type={room.t}
|
||||
type={this.t}
|
||||
baseUrl={baseUrl}
|
||||
userId={user.id}
|
||||
token={user.token}
|
||||
>
|
||||
{room.t === 'd' && roomUser._id ? <Status style={[sharedStyles.status, styles.status]} size={24} id={roomUser._id} /> : null}
|
||||
{this.t === 'd' && roomUser._id ? <Status style={[sharedStyles.status, styles.status]} size={24} id={roomUser._id} /> : null}
|
||||
</Avatar>
|
||||
);
|
||||
}
|
||||
|
@ -231,6 +237,29 @@ class RoomInfoView extends React.Component {
|
|||
return null;
|
||||
}
|
||||
|
||||
renderChannel = () => {
|
||||
const { room } = this.state;
|
||||
return (
|
||||
<React.Fragment>
|
||||
{this.renderItem('description', room)}
|
||||
{this.renderItem('topic', room)}
|
||||
{this.renderItem('announcement', room)}
|
||||
{room.broadcast ? this.renderBroadcast() : null}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
renderDirect = () => {
|
||||
const { roomUser } = this.state;
|
||||
return (
|
||||
<React.Fragment>
|
||||
{this.renderRoles()}
|
||||
{this.renderTimezone()}
|
||||
{this.renderCustomFields(roomUser._id)}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { room, roomUser } = this.state;
|
||||
if (!room) {
|
||||
|
@ -242,15 +271,9 @@ class RoomInfoView extends React.Component {
|
|||
<SafeAreaView style={styles.container} testID='room-info-view' forceInset={{ vertical: 'never' }}>
|
||||
<View style={styles.avatarContainer}>
|
||||
{this.renderAvatar(room, roomUser)}
|
||||
<View style={styles.roomTitleContainer}>{ getRoomTitle(room) }</View>
|
||||
<View style={styles.roomTitleContainer}>{ getRoomTitle(room, this.t, roomUser && roomUser.name) }</View>
|
||||
</View>
|
||||
{!this.isDirect() ? this.renderItem('description', room) : null}
|
||||
{!this.isDirect() ? this.renderItem('topic', room) : null}
|
||||
{!this.isDirect() ? this.renderItem('announcement', room) : null}
|
||||
{this.isDirect() ? this.renderRoles() : null}
|
||||
{this.isDirect() ? this.renderTimezone() : null}
|
||||
{this.isDirect() ? this.renderCustomFields(roomUser._id) : null}
|
||||
{room.broadcast ? this.renderBroadcast() : null}
|
||||
{this.isDirect() ? this.renderDirect() : this.renderChannel()}
|
||||
</SafeAreaView>
|
||||
</ScrollView>
|
||||
);
|
||||
|
|
|
@ -452,6 +452,14 @@ class RoomView extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
navToRoomInfo = (navParam) => {
|
||||
const { navigation, user } = this.props;
|
||||
if (navParam.rid === user.id) {
|
||||
return;
|
||||
}
|
||||
navigation.navigate('RoomInfoView', navParam);
|
||||
}
|
||||
|
||||
renderItem = (item, previousItem) => {
|
||||
const { room, lastOpen, canAutoTranslate } = this.state;
|
||||
const {
|
||||
|
@ -500,6 +508,7 @@ class RoomView extends React.Component {
|
|||
isReadReceiptEnabled={Message_Read_Receipt_Enabled}
|
||||
autoTranslateRoom={canAutoTranslate && room.autoTranslate}
|
||||
autoTranslateLanguage={room.autoTranslateLanguage}
|
||||
navToRoomInfo={this.navToRoomInfo}
|
||||
/>
|
||||
);
|
||||
|
||||
|
|
|
@ -8754,7 +8754,7 @@ pad-component@0.0.1:
|
|||
resolved "https://registry.yarnpkg.com/pad-component/-/pad-component-0.0.1.tgz#ad1f22ce1bf0fdc0d6ddd908af17f351a404b8ac"
|
||||
integrity sha1-rR8izhvw/cDW3dkIrxfzUaQEuKw=
|
||||
|
||||
"paho-mqtt@github:eclipse/paho.mqtt.javascript#master":
|
||||
paho-mqtt@eclipse/paho.mqtt.javascript#master:
|
||||
version "1.1.0"
|
||||
resolved "https://codeload.github.com/eclipse/paho.mqtt.javascript/tar.gz/f5859463aba9a9b7c19f99ab7c4849a723f8d832"
|
||||
|
||||
|
|
Loading…
Reference in New Issue