[NEW] Read receipt (#975)
* switching to ubountu * added read Recipt functionality to the app fix: #542 * placed the check icon on the end of timestamp * removed linting errors * updating snapshots * done requested changes * removed width scrollView * done required changes * fixed linting errors * added migrations * resolved conflicts and done requested changes * undone uneesasary changes * adding migrations * done requested changes * Add stories and fix some issues
This commit is contained in:
parent
467a2d4002
commit
d68eb01b82
|
@ -9216,6 +9216,612 @@ exports[`Storyshots Message list 1`] = `
|
|||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<Text
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"fontSize": 20,
|
||||
"fontWeight": "300",
|
||||
"marginLeft": 10,
|
||||
"marginTop": 30,
|
||||
},
|
||||
Object {
|
||||
"marginBottom": 0,
|
||||
"marginTop": 30,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
Message with read receipt
|
||||
</Text>
|
||||
<View
|
||||
accessible={true}
|
||||
isTVSelectable={true}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"opacity": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View>
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"flexDirection": "column",
|
||||
"paddingHorizontal": 14,
|
||||
"paddingVertical": 4,
|
||||
"width": "100%",
|
||||
},
|
||||
undefined,
|
||||
undefined,
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"borderRadius": 4,
|
||||
"height": 36,
|
||||
"width": 36,
|
||||
},
|
||||
Object {
|
||||
"marginTop": 4,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"overflow": "hidden",
|
||||
},
|
||||
Object {
|
||||
"borderRadius": 4,
|
||||
"height": 36,
|
||||
"width": 36,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<FastImageView
|
||||
resizeMode="cover"
|
||||
source={
|
||||
Object {
|
||||
"priority": "high",
|
||||
"uri": "https://open.rocket.chat/avatar/diego.mello?format=png&width=50&height=50&rc_token=79q6lH40W4ZRGLOshDiDiVlQaCc4f_lU9HNdHLAzuHz&rc_uid=y8bd77ptZswPj3EW8",
|
||||
}
|
||||
}
|
||||
style={
|
||||
Object {
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"flex": 1,
|
||||
"marginLeft": 46,
|
||||
},
|
||||
Object {
|
||||
"marginLeft": 10,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"flex": 1,
|
||||
"flexDirection": "row",
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"flex": 1,
|
||||
"flexDirection": "row",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
numberOfLines={1}
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "transparent",
|
||||
"color": "#2F343D",
|
||||
"fontFamily": "System",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "500",
|
||||
"lineHeight": 22,
|
||||
}
|
||||
}
|
||||
>
|
||||
diego.mello
|
||||
</Text>
|
||||
</View>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "transparent",
|
||||
"color": "#9ca2a8",
|
||||
"fontFamily": "System",
|
||||
"fontSize": 12,
|
||||
"fontWeight": "300",
|
||||
"lineHeight": 22,
|
||||
"paddingLeft": 10,
|
||||
}
|
||||
}
|
||||
>
|
||||
10:00 AM
|
||||
</Text>
|
||||
</View>
|
||||
<View
|
||||
style={Object {}}
|
||||
>
|
||||
<Text
|
||||
numberOfLines={0}
|
||||
style={
|
||||
Object {
|
||||
"alignItems": "flex-start",
|
||||
"flexDirection": "row",
|
||||
"flexWrap": "wrap",
|
||||
"justifyContent": "flex-start",
|
||||
"marginBottom": 0,
|
||||
"marginTop": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "transparent",
|
||||
"color": "#2F343D",
|
||||
"fontFamily": "System",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "400",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text>
|
||||
I’m fine!
|
||||
</Text>
|
||||
</Text>
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
accessible={true}
|
||||
isTVSelectable={true}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"opacity": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View>
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"flexDirection": "column",
|
||||
"paddingHorizontal": 14,
|
||||
"paddingVertical": 4,
|
||||
"width": "100%",
|
||||
},
|
||||
undefined,
|
||||
undefined,
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"flex": 1,
|
||||
"marginLeft": 46,
|
||||
},
|
||||
false,
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={Object {}}
|
||||
>
|
||||
<Text
|
||||
numberOfLines={0}
|
||||
style={
|
||||
Object {
|
||||
"alignItems": "flex-start",
|
||||
"flexDirection": "row",
|
||||
"flexWrap": "wrap",
|
||||
"justifyContent": "flex-start",
|
||||
"marginBottom": 0,
|
||||
"marginTop": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "transparent",
|
||||
"color": "#2F343D",
|
||||
"fontFamily": "System",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "400",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text>
|
||||
I’m fine!
|
||||
</Text>
|
||||
</Text>
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
accessible={true}
|
||||
isTVSelectable={true}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"opacity": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View>
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"flexDirection": "column",
|
||||
"paddingHorizontal": 14,
|
||||
"paddingVertical": 4,
|
||||
"width": "100%",
|
||||
},
|
||||
undefined,
|
||||
undefined,
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"borderRadius": 4,
|
||||
"height": 36,
|
||||
"width": 36,
|
||||
},
|
||||
Object {
|
||||
"marginTop": 4,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"overflow": "hidden",
|
||||
},
|
||||
Object {
|
||||
"borderRadius": 4,
|
||||
"height": 36,
|
||||
"width": 36,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<FastImageView
|
||||
resizeMode="cover"
|
||||
source={
|
||||
Object {
|
||||
"priority": "high",
|
||||
"uri": "https://open.rocket.chat/avatar/diego.mello?format=png&width=50&height=50&rc_token=79q6lH40W4ZRGLOshDiDiVlQaCc4f_lU9HNdHLAzuHz&rc_uid=y8bd77ptZswPj3EW8",
|
||||
}
|
||||
}
|
||||
style={
|
||||
Object {
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"flex": 1,
|
||||
"marginLeft": 46,
|
||||
},
|
||||
Object {
|
||||
"marginLeft": 10,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"flex": 1,
|
||||
"flexDirection": "row",
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"flex": 1,
|
||||
"flexDirection": "row",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
numberOfLines={1}
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "transparent",
|
||||
"color": "#2F343D",
|
||||
"fontFamily": "System",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "500",
|
||||
"lineHeight": 22,
|
||||
}
|
||||
}
|
||||
>
|
||||
diego.mello
|
||||
</Text>
|
||||
</View>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "transparent",
|
||||
"color": "#9ca2a8",
|
||||
"fontFamily": "System",
|
||||
"fontSize": 12,
|
||||
"fontWeight": "300",
|
||||
"lineHeight": 22,
|
||||
"paddingLeft": 10,
|
||||
}
|
||||
}
|
||||
>
|
||||
10:00 AM
|
||||
</Text>
|
||||
</View>
|
||||
<View
|
||||
style={Object {}}
|
||||
>
|
||||
<Text
|
||||
numberOfLines={0}
|
||||
style={
|
||||
Object {
|
||||
"alignItems": "flex-start",
|
||||
"flexDirection": "row",
|
||||
"flexWrap": "wrap",
|
||||
"justifyContent": "flex-start",
|
||||
"marginBottom": 0,
|
||||
"marginTop": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "transparent",
|
||||
"color": "#2F343D",
|
||||
"fontFamily": "System",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "400",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text>
|
||||
I’m fine!
|
||||
</Text>
|
||||
</Text>
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<Text
|
||||
allowFontScaling={false}
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"color": "#1d74f5",
|
||||
"fontSize": 15,
|
||||
},
|
||||
Object {
|
||||
"lineHeight": 20,
|
||||
},
|
||||
Object {
|
||||
"fontFamily": "custom",
|
||||
"fontStyle": "normal",
|
||||
"fontWeight": "normal",
|
||||
},
|
||||
Object {},
|
||||
]
|
||||
}
|
||||
>
|
||||
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
accessible={true}
|
||||
isTVSelectable={true}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"opacity": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View>
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"flexDirection": "column",
|
||||
"paddingHorizontal": 14,
|
||||
"paddingVertical": 4,
|
||||
"width": "100%",
|
||||
},
|
||||
undefined,
|
||||
undefined,
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"flex": 1,
|
||||
"marginLeft": 46,
|
||||
},
|
||||
false,
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={Object {}}
|
||||
>
|
||||
<Text
|
||||
numberOfLines={0}
|
||||
style={
|
||||
Object {
|
||||
"alignItems": "flex-start",
|
||||
"flexDirection": "row",
|
||||
"flexWrap": "wrap",
|
||||
"justifyContent": "flex-start",
|
||||
"marginBottom": 0,
|
||||
"marginTop": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "transparent",
|
||||
"color": "#2F343D",
|
||||
"fontFamily": "System",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "400",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text>
|
||||
I’m fine!
|
||||
</Text>
|
||||
</Text>
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<Text
|
||||
allowFontScaling={false}
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"color": "#1d74f5",
|
||||
"fontSize": 15,
|
||||
},
|
||||
Object {
|
||||
"lineHeight": 20,
|
||||
},
|
||||
Object {
|
||||
"fontFamily": "custom",
|
||||
"fontStyle": "normal",
|
||||
"fontWeight": "normal",
|
||||
},
|
||||
Object {},
|
||||
]
|
||||
}
|
||||
>
|
||||
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<Text
|
||||
style={
|
||||
Array [
|
||||
|
|
|
@ -59,6 +59,12 @@ export default {
|
|||
Assets_favicon_512: {
|
||||
type: null
|
||||
},
|
||||
Message_Read_Receipt_Enabled: {
|
||||
type: 'valueAsBoolean'
|
||||
},
|
||||
Message_Read_Receipt_Store_Users: {
|
||||
type: 'valueAsBoolean'
|
||||
},
|
||||
Threads_enabled: {
|
||||
type: null
|
||||
},
|
||||
|
|
|
@ -17,6 +17,7 @@ import { vibrate } from '../utils/vibration';
|
|||
import RocketChat from '../lib/rocketchat';
|
||||
import I18n from '../i18n';
|
||||
import log from '../utils/log';
|
||||
import Navigation from '../lib/Navigation';
|
||||
|
||||
@connect(
|
||||
state => ({
|
||||
|
@ -26,7 +27,8 @@ import log from '../utils/log';
|
|||
Message_AllowEditing: state.settings.Message_AllowEditing,
|
||||
Message_AllowEditing_BlockEditInMinutes: state.settings.Message_AllowEditing_BlockEditInMinutes,
|
||||
Message_AllowPinning: state.settings.Message_AllowPinning,
|
||||
Message_AllowStarring: state.settings.Message_AllowStarring
|
||||
Message_AllowStarring: state.settings.Message_AllowStarring,
|
||||
Message_Read_Receipt_Store_Users: state.settings.Message_Read_Receipt_Store_Users
|
||||
}),
|
||||
dispatch => ({
|
||||
actionsHide: () => dispatch(actionsHideAction()),
|
||||
|
@ -56,7 +58,8 @@ export default class MessageActions extends React.Component {
|
|||
Message_AllowEditing: PropTypes.bool,
|
||||
Message_AllowEditing_BlockEditInMinutes: PropTypes.number,
|
||||
Message_AllowPinning: PropTypes.bool,
|
||||
Message_AllowStarring: PropTypes.bool
|
||||
Message_AllowStarring: PropTypes.bool,
|
||||
Message_Read_Receipt_Store_Users: PropTypes.bool
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
|
@ -64,7 +67,7 @@ export default class MessageActions extends React.Component {
|
|||
this.handleActionPress = this.handleActionPress.bind(this);
|
||||
this.setPermissions();
|
||||
|
||||
const { Message_AllowStarring, Message_AllowPinning } = this.props;
|
||||
const { Message_AllowStarring, Message_AllowPinning, Message_Read_Receipt_Store_Users } = this.props;
|
||||
|
||||
// Cancel
|
||||
this.options = [I18n.t('Cancel')];
|
||||
|
@ -118,6 +121,12 @@ export default class MessageActions extends React.Component {
|
|||
this.REACTION_INDEX = this.options.length - 1;
|
||||
}
|
||||
|
||||
// Read Receipts
|
||||
if (Message_Read_Receipt_Store_Users) {
|
||||
this.options.push(I18n.t('Read_Receipt'));
|
||||
this.READ_RECEIPT_INDEX = this.options.length - 1;
|
||||
}
|
||||
|
||||
// Report
|
||||
this.options.push(I18n.t('Report'));
|
||||
this.REPORT_INDEX = this.options.length - 1;
|
||||
|
@ -302,6 +311,11 @@ export default class MessageActions extends React.Component {
|
|||
toggleReactionPicker(actionMessage);
|
||||
}
|
||||
|
||||
handleReadReceipt = () => {
|
||||
const { actionMessage } = this.props;
|
||||
Navigation.navigate('ReadReceiptsView', { messageId: actionMessage._id });
|
||||
}
|
||||
|
||||
handleReport = async() => {
|
||||
const { actionMessage } = this.props;
|
||||
try {
|
||||
|
@ -348,6 +362,9 @@ export default class MessageActions extends React.Component {
|
|||
case this.DELETE_INDEX:
|
||||
this.handleDelete();
|
||||
break;
|
||||
case this.READ_RECEIPT_INDEX:
|
||||
this.handleReadReceipt();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import Reactions from './Reactions';
|
|||
import Broadcast from './Broadcast';
|
||||
import Discussion from './Discussion';
|
||||
import Content from './Content';
|
||||
import ReadReceipt from './ReadReceipt';
|
||||
|
||||
const MessageInner = React.memo((props) => {
|
||||
if (props.type === 'discussion-created') {
|
||||
|
@ -72,6 +73,10 @@ const Message = React.memo((props) => {
|
|||
>
|
||||
<MessageInner {...props} />
|
||||
</View>
|
||||
<ReadReceipt
|
||||
isReadReceiptEnabled={props.isReadReceiptEnabled}
|
||||
unread={props.unread}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
|
@ -119,7 +124,9 @@ Message.propTypes = {
|
|||
hasError: PropTypes.bool,
|
||||
style: PropTypes.any,
|
||||
onLongPress: PropTypes.func,
|
||||
onPress: PropTypes.func
|
||||
onPress: PropTypes.func,
|
||||
isReadReceiptEnabled: PropTypes.bool,
|
||||
unread: PropTypes.bool
|
||||
};
|
||||
|
||||
MessageInner.propTypes = {
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { COLOR_PRIMARY } from '../../constants/colors';
|
||||
import { CustomIcon } from '../../lib/Icons';
|
||||
import styles from './styles';
|
||||
|
||||
const ReadReceipt = React.memo(({ isReadReceiptEnabled, unread }) => {
|
||||
if (isReadReceiptEnabled && !unread && unread !== null) {
|
||||
return <CustomIcon name='check' color={COLOR_PRIMARY} size={15} style={styles.readReceipt} />;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
ReadReceipt.displayName = 'MessageReadReceipt';
|
||||
|
||||
ReadReceipt.propTypes = {
|
||||
isReadReceiptEnabled: PropTypes.bool,
|
||||
unread: PropTypes.bool
|
||||
};
|
||||
|
||||
export default ReadReceipt;
|
|
@ -24,6 +24,7 @@ export default class MessageContainer extends React.Component {
|
|||
_updatedAt: PropTypes.instanceOf(Date),
|
||||
baseUrl: PropTypes.string,
|
||||
Message_GroupingPeriod: PropTypes.number,
|
||||
isReadReceiptEnabled: PropTypes.bool,
|
||||
useRealName: PropTypes.bool,
|
||||
useMarkdown: PropTypes.bool,
|
||||
status: PropTypes.number,
|
||||
|
@ -57,6 +58,9 @@ export default class MessageContainer extends React.Component {
|
|||
if (item.tmsg !== nextProps.item.tmsg) {
|
||||
return true;
|
||||
}
|
||||
if (item.unread !== nextProps.item.unread) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return _updatedAt.toISOString() !== nextProps._updatedAt.toISOString();
|
||||
}
|
||||
|
@ -187,10 +191,10 @@ export default class MessageContainer extends React.Component {
|
|||
|
||||
render() {
|
||||
const {
|
||||
item, user, style, archived, baseUrl, useRealName, broadcast, fetchThreadName, customThreadTimeFormat, onOpenFileModal, timeFormat, useMarkdown
|
||||
item, user, style, archived, baseUrl, useRealName, broadcast, fetchThreadName, customThreadTimeFormat, onOpenFileModal, timeFormat, useMarkdown, isReadReceiptEnabled
|
||||
} = this.props;
|
||||
const {
|
||||
_id, msg, ts, attachments, urls, reactions, t, avatar, u, alias, editedBy, role, drid, dcount, dlm, tmid, tcount, tlm, tmsg, mentions, channels
|
||||
_id, msg, ts, attachments, urls, reactions, t, avatar, u, alias, editedBy, role, drid, dcount, dlm, tmid, tcount, tlm, tmsg, mentions, channels, unread
|
||||
} = item;
|
||||
|
||||
return (
|
||||
|
@ -213,6 +217,8 @@ export default class MessageContainer extends React.Component {
|
|||
broadcast={broadcast}
|
||||
baseUrl={baseUrl}
|
||||
useRealName={useRealName}
|
||||
isReadReceiptEnabled={isReadReceiptEnabled}
|
||||
unread={unread}
|
||||
role={role}
|
||||
drid={drid}
|
||||
dcount={dcount}
|
||||
|
|
|
@ -234,5 +234,8 @@ export default StyleSheet.create({
|
|||
flex: 1,
|
||||
color: COLOR_PRIMARY,
|
||||
...sharedStyles.textRegular
|
||||
},
|
||||
readReceipt: {
|
||||
lineHeight: 20
|
||||
}
|
||||
});
|
||||
|
|
|
@ -233,6 +233,7 @@ export default {
|
|||
No_Message: 'No Message',
|
||||
No_messages_yet: 'No messages yet',
|
||||
No_Reactions: 'No Reactions',
|
||||
No_Read_Receipts: 'No Read Receipts',
|
||||
Not_logged: 'Not logged',
|
||||
Nothing_to_save: 'Nothing to save!',
|
||||
Notify_active_in_this_room: 'Notify active users in this room',
|
||||
|
@ -265,6 +266,7 @@ export default {
|
|||
Reactions: 'Reactions',
|
||||
Read_Only_Channel: 'Read Only Channel',
|
||||
Read_Only: 'Read Only',
|
||||
Read_Receipt: 'Read Receipt',
|
||||
Register: 'Register',
|
||||
Repeat_Password: 'Repeat Password',
|
||||
Replied_on: 'Replied on:',
|
||||
|
|
|
@ -266,6 +266,7 @@ export default {
|
|||
Read_Only_Channel: 'Canal Somente Leitura',
|
||||
Read_Only: 'Somente Leitura',
|
||||
Register: 'Registrar',
|
||||
Read_Receipt: 'Lida por',
|
||||
Repeat_Password: 'Repetir Senha',
|
||||
Replied_on: 'Respondido em:',
|
||||
replies: 'respostas',
|
||||
|
|
|
@ -29,6 +29,7 @@ import RoomInfoView from './views/RoomInfoView';
|
|||
import RoomInfoEditView from './views/RoomInfoEditView';
|
||||
import RoomMembersView from './views/RoomMembersView';
|
||||
import SearchMessagesView from './views/SearchMessagesView';
|
||||
import ReadReceiptsView from './views/ReadReceiptView';
|
||||
import ThreadMessagesView from './views/ThreadMessagesView';
|
||||
import MessagesView from './views/MessagesView';
|
||||
import SelectedUsersView from './views/SelectedUsersView';
|
||||
|
@ -114,6 +115,7 @@ const ChatsStack = createStackNavigator({
|
|||
SelectedUsersView,
|
||||
ThreadMessagesView,
|
||||
MessagesView,
|
||||
ReadReceiptsView,
|
||||
DirectoryView
|
||||
}, {
|
||||
defaultNavigationOptions: defaultHeader
|
||||
|
|
|
@ -26,6 +26,7 @@ export default (msg) => {
|
|||
|
||||
msg = normalizeAttachments(msg);
|
||||
msg.reactions = msg.reactions || [];
|
||||
msg.unread = msg.unread || false;
|
||||
// TODO: api problems
|
||||
// if (Array.isArray(msg.reactions)) {
|
||||
// msg.reactions = msg.reactions.map((value, key) => ({ emoji: key, usernames: value.usernames.map(username => ({ value: username })) }));
|
||||
|
|
|
@ -197,7 +197,8 @@ const messagesSchema = {
|
|||
tlm: { type: 'date', optional: true },
|
||||
replies: 'string[]',
|
||||
mentions: { type: 'list', objectType: 'users' },
|
||||
channels: { type: 'list', objectType: 'rooms' }
|
||||
channels: { type: 'list', objectType: 'rooms' },
|
||||
unread: { type: 'bool', optional: true }
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -415,7 +416,7 @@ class DB {
|
|||
return this.databases.activeDB = new Realm({
|
||||
path: `${ path }.realm`,
|
||||
schema,
|
||||
schemaVersion: 11,
|
||||
schemaVersion: 12,
|
||||
migration: (oldRealm, newRealm) => {
|
||||
if (oldRealm.schemaVersion >= 3 && newRealm.schemaVersion <= 11) {
|
||||
const newSubs = newRealm.objects('subscriptions');
|
||||
|
|
|
@ -771,6 +771,12 @@ const RocketChat = {
|
|||
sort: { ts: -1 }
|
||||
});
|
||||
},
|
||||
|
||||
getReadReceipts(messageId) {
|
||||
return this.sdk.get('chat.getMessageReadReceipts', {
|
||||
messageId
|
||||
});
|
||||
},
|
||||
searchMessages(roomId, searchText) {
|
||||
// RC 0.60.0
|
||||
return this.sdk.get('chat.search', {
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FlatList, View, Text } from 'react-native';
|
||||
import { SafeAreaView } from 'react-navigation';
|
||||
import equal from 'deep-equal';
|
||||
import moment from 'moment';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import Avatar from '../../containers/Avatar';
|
||||
import styles from './styles';
|
||||
import RCActivityIndicator from '../../containers/ActivityIndicator';
|
||||
import I18n from '../../i18n';
|
||||
import RocketChat from '../../lib/rocketchat';
|
||||
import StatusBar from '../../containers/StatusBar';
|
||||
|
||||
@connect(state => ({
|
||||
Message_TimeFormat: state.settings.Message_TimeFormat,
|
||||
baseUrl: state.settings.Site_Url || state.server ? state.server.server : '',
|
||||
userId: state.login.user && state.login.user.id,
|
||||
token: state.login.user && state.login.user.token
|
||||
}))
|
||||
export default class ReadReceiptsView extends React.Component {
|
||||
static navigationOptions = {
|
||||
title: I18n.t('Read_Receipt')
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
navigation: PropTypes.object,
|
||||
Message_TimeFormat: PropTypes.string,
|
||||
baseUrl: PropTypes.string,
|
||||
userId: PropTypes.string,
|
||||
token: PropTypes.string
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.messageId = props.navigation.getParam('messageId');
|
||||
this.state = {
|
||||
loading: false,
|
||||
receipts: []
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.load();
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
const { loading, receipts } = this.state;
|
||||
if (nextState.loading !== loading) {
|
||||
return true;
|
||||
}
|
||||
if (!equal(nextState.receipts, receipts)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
load = async() => {
|
||||
const { loading } = this.state;
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({ loading: true });
|
||||
|
||||
try {
|
||||
const result = await RocketChat.getReadReceipts(this.messageId);
|
||||
if (result.success) {
|
||||
this.setState({
|
||||
receipts: result.receipts,
|
||||
loading: false
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
this.setState({ loading: false });
|
||||
console.log('err_fetch_read_receipts', error);
|
||||
}
|
||||
}
|
||||
|
||||
renderEmpty = () => (
|
||||
<View style={styles.listEmptyContainer} testID='read-receipt-view'>
|
||||
<Text>{I18n.t('No_Read_Receipts')}</Text>
|
||||
</View>
|
||||
)
|
||||
|
||||
renderItem = ({ item }) => {
|
||||
const {
|
||||
Message_TimeFormat, userId, baseUrl, token
|
||||
} = this.props;
|
||||
const time = moment(item.ts).format(Message_TimeFormat);
|
||||
return (
|
||||
<View style={styles.itemContainer}>
|
||||
<Avatar
|
||||
text={item.user.username}
|
||||
size={40}
|
||||
baseUrl={baseUrl}
|
||||
userId={userId}
|
||||
token={token}
|
||||
/>
|
||||
<View style={styles.infoContainer}>
|
||||
<View style={styles.item}>
|
||||
<Text style={styles.name}>
|
||||
{item.user.name}
|
||||
</Text>
|
||||
<Text>
|
||||
{time}
|
||||
</Text>
|
||||
</View>
|
||||
<Text>
|
||||
{`@${ item.user.username }`}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
renderSeparator = () => <View style={styles.separator} />;
|
||||
|
||||
render() {
|
||||
const { receipts, loading } = this.state;
|
||||
|
||||
if (!loading && receipts.length === 0) {
|
||||
return this.renderEmpty();
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.container} testID='read-receipt-view' forceInset={{ bottom: 'always' }}>
|
||||
<StatusBar />
|
||||
<View>
|
||||
{loading
|
||||
? <RCActivityIndicator />
|
||||
: (
|
||||
<FlatList
|
||||
data={receipts}
|
||||
renderItem={this.renderItem}
|
||||
ItemSeparatorComponent={this.renderSeparator}
|
||||
style={styles.list}
|
||||
keyExtractor={item => item._id}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
import { StyleSheet } from 'react-native';
|
||||
import { COLOR_SEPARATOR, COLOR_WHITE, COLOR_BACKGROUND_CONTAINER } from '../../constants/colors';
|
||||
import sharedStyles from '../Styles';
|
||||
|
||||
export default StyleSheet.create({
|
||||
listEmptyContainer: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: COLOR_BACKGROUND_CONTAINER
|
||||
},
|
||||
item: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between'
|
||||
},
|
||||
separator: {
|
||||
height: StyleSheet.hairlineWidth,
|
||||
backgroundColor: COLOR_SEPARATOR
|
||||
},
|
||||
name: {
|
||||
...sharedStyles.textRegular,
|
||||
...sharedStyles.textColorTitle,
|
||||
fontSize: 17
|
||||
},
|
||||
username: {
|
||||
flex: 1,
|
||||
...sharedStyles.textRegular,
|
||||
...sharedStyles.textColorDescription,
|
||||
fontSize: 14
|
||||
},
|
||||
infoContainer: {
|
||||
flex: 1,
|
||||
marginLeft: 10
|
||||
},
|
||||
itemContainer: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
padding: 10,
|
||||
backgroundColor: COLOR_WHITE
|
||||
},
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: COLOR_BACKGROUND_CONTAINER
|
||||
},
|
||||
list: {
|
||||
...sharedStyles.separatorVertical,
|
||||
marginVertical: 10
|
||||
}
|
||||
});
|
|
@ -60,7 +60,8 @@ import { Toast } from '../../utils/info';
|
|||
Message_GroupingPeriod: state.settings.Message_GroupingPeriod,
|
||||
Message_TimeFormat: state.settings.Message_TimeFormat,
|
||||
useMarkdown: state.markdown.useMarkdown,
|
||||
baseUrl: state.settings.baseUrl || state.server ? state.server.server : ''
|
||||
baseUrl: state.settings.baseUrl || state.server ? state.server.server : '',
|
||||
Message_Read_Receipt_Enabled: state.settings.Message_Read_Receipt_Enabled
|
||||
}), dispatch => ({
|
||||
editCancel: () => dispatch(editCancelAction()),
|
||||
replyCancel: () => dispatch(replyCancelAction()),
|
||||
|
@ -116,6 +117,7 @@ export default class RoomView extends React.Component {
|
|||
isAuthenticated: PropTypes.bool,
|
||||
Message_GroupingPeriod: PropTypes.number,
|
||||
Message_TimeFormat: PropTypes.string,
|
||||
Message_Read_Receipt_Enabled: PropTypes.bool,
|
||||
editing: PropTypes.bool,
|
||||
replying: PropTypes.bool,
|
||||
baseUrl: PropTypes.string,
|
||||
|
@ -499,7 +501,7 @@ export default class RoomView extends React.Component {
|
|||
renderItem = (item, previousItem) => {
|
||||
const { room, lastOpen } = this.state;
|
||||
const {
|
||||
user, Message_GroupingPeriod, Message_TimeFormat, useRealName, baseUrl, useMarkdown
|
||||
user, Message_GroupingPeriod, Message_TimeFormat, useRealName, baseUrl, useMarkdown, Message_Read_Receipt_Enabled
|
||||
} = this.props;
|
||||
let dateSeparator = null;
|
||||
let showUnreadSeparator = false;
|
||||
|
@ -541,6 +543,7 @@ export default class RoomView extends React.Component {
|
|||
timeFormat={Message_TimeFormat}
|
||||
useRealName={useRealName}
|
||||
useMarkdown={useMarkdown}
|
||||
isReadReceiptEnabled={Message_Read_Receipt_Enabled}
|
||||
/>
|
||||
);
|
||||
|
||||
|
|
|
@ -311,6 +311,30 @@ export default (
|
|||
}]}
|
||||
/>
|
||||
|
||||
<Separator title='Message with read receipt' />
|
||||
<Message
|
||||
msg="I'm fine!"
|
||||
isReadReceiptEnabled
|
||||
unread
|
||||
/>
|
||||
<Message
|
||||
msg="I'm fine!"
|
||||
isReadReceiptEnabled
|
||||
unread
|
||||
isHeader={false}
|
||||
/>
|
||||
<Message
|
||||
msg="I'm fine!"
|
||||
isReadReceiptEnabled
|
||||
read
|
||||
/>
|
||||
<Message
|
||||
msg="I'm fine!"
|
||||
isReadReceiptEnabled
|
||||
read
|
||||
isHeader={false}
|
||||
/>
|
||||
|
||||
<Separator title='Message with thread' />
|
||||
<Message
|
||||
msg='How are you?'
|
||||
|
|
Loading…
Reference in New Issue