vn-verdnaturachat/app/containers/MessageActions.js

468 lines
12 KiB
JavaScript

import React from 'react';
import PropTypes from 'prop-types';
import { Alert, Clipboard, Share } from 'react-native';
import { connect } from 'react-redux';
import ActionSheet from 'react-native-action-sheet';
import moment from 'moment';
import * as Haptics from 'expo-haptics';
import RocketChat from '../lib/rocketchat';
import database from '../lib/database';
import I18n from '../i18n';
import log from '../utils/log';
import Navigation from '../lib/Navigation';
import { getMessageTranslation } from './message/utils';
import { LISTENER } from './Toast';
import EventEmitter from '../utils/events';
import { showConfirmationAlert } from '../utils/info';
class MessageActions extends React.Component {
static propTypes = {
actionsHide: PropTypes.func.isRequired,
room: PropTypes.object.isRequired,
message: PropTypes.object,
user: PropTypes.object,
editInit: PropTypes.func.isRequired,
reactionInit: PropTypes.func.isRequired,
replyInit: PropTypes.func.isRequired,
isReadOnly: PropTypes.bool,
Message_AllowDeleting: PropTypes.bool,
Message_AllowDeleting_BlockDeleteInMinutes: PropTypes.number,
Message_AllowEditing: PropTypes.bool,
Message_AllowEditing_BlockEditInMinutes: PropTypes.number,
Message_AllowPinning: PropTypes.bool,
Message_AllowStarring: PropTypes.bool,
Message_Read_Receipt_Store_Users: PropTypes.bool,
isMasterDetail: PropTypes.bool
};
constructor(props) {
super(props);
this.handleActionPress = this.handleActionPress.bind(this);
}
async componentDidMount() {
await this.setPermissions();
const {
Message_AllowStarring, Message_AllowPinning, Message_Read_Receipt_Store_Users, user, room, message, isReadOnly
} = this.props;
// Cancel
this.options = [I18n.t('Cancel')];
this.CANCEL_INDEX = 0;
// Reply
if (!isReadOnly) {
this.options.push(I18n.t('Reply'));
this.REPLY_INDEX = this.options.length - 1;
}
// Edit
if (this.allowEdit(this.props)) {
this.options.push(I18n.t('Edit'));
this.EDIT_INDEX = this.options.length - 1;
}
// Create Discussion
this.options.push(I18n.t('Create_Discussion'));
this.CREATE_DISCUSSION_INDEX = this.options.length - 1;
// Mark as unread
if (message.u && message.u._id !== user.id) {
this.options.push(I18n.t('Mark_unread'));
this.UNREAD_INDEX = this.options.length - 1;
}
// Permalink
this.options.push(I18n.t('Permalink'));
this.PERMALINK_INDEX = this.options.length - 1;
// Copy
this.options.push(I18n.t('Copy'));
this.COPY_INDEX = this.options.length - 1;
// Share
this.options.push(I18n.t('Share'));
this.SHARE_INDEX = this.options.length - 1;
// Quote
if (!isReadOnly) {
this.options.push(I18n.t('Quote'));
this.QUOTE_INDEX = this.options.length - 1;
}
// Star
if (Message_AllowStarring) {
this.options.push(I18n.t(message.starred ? 'Unstar' : 'Star'));
this.STAR_INDEX = this.options.length - 1;
}
// Pin
if (Message_AllowPinning) {
this.options.push(I18n.t(message.pinned ? 'Unpin' : 'Pin'));
this.PIN_INDEX = this.options.length - 1;
}
// Reaction
if (!isReadOnly || this.canReactWhenReadOnly()) {
this.options.push(I18n.t('Add_Reaction'));
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;
}
// Toggle Auto-translate
if (room.autoTranslate && message.u && message.u._id !== user.id) {
this.options.push(I18n.t(message.autoTranslate ? 'View_Original' : 'Translate'));
this.TOGGLE_TRANSLATION_INDEX = this.options.length - 1;
}
// Report
this.options.push(I18n.t('Report'));
this.REPORT_INDEX = this.options.length - 1;
// Delete
if (this.allowDelete(this.props)) {
this.options.push(I18n.t('Delete'));
this.DELETE_INDEX = this.options.length - 1;
}
setTimeout(() => {
this.showActionSheet();
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
});
}
async setPermissions() {
try {
const { room } = this.props;
const permissions = ['edit-message', 'delete-message', 'force-delete-message'];
const result = await RocketChat.hasPermission(permissions, room.rid);
this.hasEditPermission = result[permissions[0]];
this.hasDeletePermission = result[permissions[1]];
this.hasForceDeletePermission = result[permissions[2]];
} catch (e) {
log(e);
}
Promise.resolve();
}
showActionSheet = () => {
ActionSheet.showActionSheetWithOptions({
options: this.options,
cancelButtonIndex: this.CANCEL_INDEX,
destructiveButtonIndex: this.DELETE_INDEX,
title: I18n.t('Message_actions')
}, (actionIndex) => {
this.handleActionPress(actionIndex);
});
}
getPermalink = async(message) => {
try {
return await RocketChat.getPermalinkMessage(message);
} catch (error) {
return null;
}
}
isOwn = props => props.message.u && props.message.u._id === props.user.id;
canReactWhenReadOnly = () => {
const { room } = this.props;
return room.reactWhenReadOnly;
}
allowEdit = (props) => {
if (props.isReadOnly) {
return false;
}
const editOwn = this.isOwn(props);
const { Message_AllowEditing: isEditAllowed, Message_AllowEditing_BlockEditInMinutes } = this.props;
if (!(this.hasEditPermission || (isEditAllowed && editOwn))) {
return false;
}
const blockEditInMinutes = Message_AllowEditing_BlockEditInMinutes;
if (blockEditInMinutes) {
let msgTs;
if (props.message.ts != null) {
msgTs = moment(props.message.ts);
}
let currentTsDiff;
if (msgTs != null) {
currentTsDiff = moment().diff(msgTs, 'minutes');
}
return currentTsDiff < blockEditInMinutes;
}
return true;
}
allowDelete = (props) => {
if (props.isReadOnly) {
return false;
}
// Prevent from deleting thread start message when positioned inside the thread
if (props.tmid && props.tmid === props.message.id) {
return false;
}
const deleteOwn = this.isOwn(props);
const { Message_AllowDeleting: isDeleteAllowed, Message_AllowDeleting_BlockDeleteInMinutes } = this.props;
if (!(this.hasDeletePermission || (isDeleteAllowed && deleteOwn) || this.hasForceDeletePermission)) {
return false;
}
if (this.hasForceDeletePermission) {
return true;
}
const blockDeleteInMinutes = Message_AllowDeleting_BlockDeleteInMinutes;
if (blockDeleteInMinutes != null && blockDeleteInMinutes !== 0) {
let msgTs;
if (props.message.ts != null) {
msgTs = moment(props.message.ts);
}
let currentTsDiff;
if (msgTs != null) {
currentTsDiff = moment().diff(msgTs, 'minutes');
}
return currentTsDiff < blockDeleteInMinutes;
}
return true;
}
handleDelete = () => {
showConfirmationAlert({
message: I18n.t('You_will_not_be_able_to_recover_this_message'),
callToAction: I18n.t('Delete'),
onPress: async() => {
const { message } = this.props;
try {
await RocketChat.deleteMessage(message.id, message.subscription.id);
} catch (e) {
log(e);
}
}
});
}
handleEdit = () => {
const { message, editInit } = this.props;
editInit(message);
}
handleUnread = async() => {
const { message, room, isMasterDetail } = this.props;
const { id: messageId, ts } = message;
const { rid } = room;
try {
const db = database.active;
const result = await RocketChat.markAsUnread({ messageId });
if (result.success) {
const subCollection = db.collections.get('subscriptions');
const subRecord = await subCollection.find(rid);
await db.action(async() => {
try {
await subRecord.update(sub => sub.lastOpen = ts);
} catch {
// do nothing
}
});
if (isMasterDetail) {
Navigation.replace('RoomView');
} else {
Navigation.navigate('RoomsListView');
}
}
} catch (e) {
log(e);
}
}
handleCopy = async() => {
const { message } = this.props;
await Clipboard.setString(message.msg);
EventEmitter.emit(LISTENER, { message: I18n.t('Copied_to_clipboard') });
}
handleShare = async() => {
const { message } = this.props;
const permalink = await this.getPermalink(message);
if (!permalink) {
return;
}
Share.share({
message: permalink
});
};
handleStar = async() => {
const { message } = this.props;
try {
await RocketChat.toggleStarMessage(message.id, message.starred);
EventEmitter.emit(LISTENER, { message: message.starred ? I18n.t('Message_unstarred') : I18n.t('Message_starred') });
} catch (e) {
log(e);
}
}
handlePermalink = async() => {
const { message } = this.props;
const permalink = await this.getPermalink(message);
Clipboard.setString(permalink);
EventEmitter.emit(LISTENER, { message: I18n.t('Permalink_copied_to_clipboard') });
}
handlePin = async() => {
const { message } = this.props;
try {
await RocketChat.togglePinMessage(message.id, message.pinned);
} catch (e) {
log(e);
}
}
handleReply = () => {
const { message, replyInit } = this.props;
replyInit(message, true);
}
handleQuote = () => {
const { message, replyInit } = this.props;
replyInit(message, false);
}
handleReaction = () => {
const { message, reactionInit } = this.props;
reactionInit(message);
}
handleReadReceipt = () => {
const { message } = this.props;
Navigation.navigate('ReadReceiptsView', { messageId: message.id });
}
handleReport = async() => {
const { message } = this.props;
try {
await RocketChat.reportMessage(message.id);
Alert.alert(I18n.t('Message_Reported'));
} catch (e) {
log(e);
}
}
handleToggleTranslation = async() => {
const { message, room } = this.props;
try {
const db = database.active;
await db.action(async() => {
await message.update((m) => {
m.autoTranslate = !m.autoTranslate;
m._updatedAt = new Date();
});
});
const translatedMessage = getMessageTranslation(message, room.autoTranslateLanguage);
if (!translatedMessage) {
const m = {
_id: message.id,
rid: message.subscription.id,
u: message.u,
msg: message.msg
};
await RocketChat.translateMessage(m, room.autoTranslateLanguage);
}
} catch (e) {
log(e);
}
}
handleCreateDiscussion = () => {
const { message, room: channel, isMasterDetail } = this.props;
const params = { message, channel, showCloseModal: true };
if (isMasterDetail) {
Navigation.navigate('ModalStackNavigator', { screen: 'CreateDiscussionView', params });
} else {
Navigation.navigate('NewMessageStackNavigator', { screen: 'CreateDiscussionView', params });
}
}
handleActionPress = (actionIndex) => {
if (actionIndex) {
switch (actionIndex) {
case this.REPLY_INDEX:
this.handleReply();
break;
case this.EDIT_INDEX:
this.handleEdit();
break;
case this.UNREAD_INDEX:
this.handleUnread();
break;
case this.PERMALINK_INDEX:
this.handlePermalink();
break;
case this.COPY_INDEX:
this.handleCopy();
break;
case this.SHARE_INDEX:
this.handleShare();
break;
case this.QUOTE_INDEX:
this.handleQuote();
break;
case this.STAR_INDEX:
this.handleStar();
break;
case this.PIN_INDEX:
this.handlePin();
break;
case this.REACTION_INDEX:
this.handleReaction();
break;
case this.REPORT_INDEX:
this.handleReport();
break;
case this.DELETE_INDEX:
this.handleDelete();
break;
case this.READ_RECEIPT_INDEX:
this.handleReadReceipt();
break;
case this.CREATE_DISCUSSION_INDEX:
this.handleCreateDiscussion();
break;
case this.TOGGLE_TRANSLATION_INDEX:
this.handleToggleTranslation();
break;
default:
break;
}
}
const { actionsHide } = this.props;
actionsHide();
}
render() {
return (
null
);
}
}
const mapStateToProps = state => ({
Message_AllowDeleting: state.settings.Message_AllowDeleting,
Message_AllowDeleting_BlockDeleteInMinutes: state.settings.Message_AllowDeleting_BlockDeleteInMinutes,
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_Read_Receipt_Store_Users: state.settings.Message_Read_Receipt_Store_Users,
isMasterDetail: state.app.isMasterDetail
});
export default connect(mapStateToProps)(MessageActions);