From b3986b98b59dfea00cf8c384e85e54204e19a809 Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Fri, 28 Jun 2019 14:02:30 -0300 Subject: [PATCH] [NEW] Auto-translate (#1012) * Update realm * View original and translate working * Read AutoTranslate_Enabled setting * RocketChat.canAutoTranslate() * AutoTranslateView * Save language * Auto-translate switch * Translate message --- app/constants/colors.js | 7 +- app/constants/settings.js | 3 + app/containers/MessageActions.js | 30 +++- app/containers/message/index.js | 22 ++- app/containers/message/utils.js | 12 ++ app/i18n/locales/en.js | 4 + app/index.js | 2 + app/lib/methods/helpers/normalizeMessage.js | 3 + app/lib/realm.js | 42 ++++- app/lib/rocketchat.js | 25 +++ app/views/AutoTranslateView/index.js | 156 ++++++++++++++++++ app/views/CreateChannelView.js | 6 +- app/views/DirectoryView/Options.js | 3 +- app/views/LanguageView/index.js | 1 - app/views/RoomActionsView/index.js | 19 ++- app/views/RoomInfoEditView/SwitchContainer.js | 2 + app/views/RoomView/index.js | 17 +- app/views/SettingsView/index.js | 8 +- 18 files changed, 330 insertions(+), 32 deletions(-) create mode 100644 app/views/AutoTranslateView/index.js diff --git a/app/constants/colors.js b/app/constants/colors.js index a1fa7cee7c..b65cbc22a6 100644 --- a/app/constants/colors.js +++ b/app/constants/colors.js @@ -1,4 +1,4 @@ -import { isIOS } from '../utils/deviceInfo'; +import { isIOS, isAndroid } from '../utils/deviceInfo'; export const COLOR_DANGER = '#f5455c'; export const COLOR_SUCCESS = '#2de0a5'; @@ -25,3 +25,8 @@ export const HEADER_BACKGROUND = isIOS ? '#f8f8f8' : '#2F343D'; export const HEADER_TITLE = isIOS ? COLOR_TITLE : COLOR_WHITE; export const HEADER_BACK = isIOS ? COLOR_PRIMARY : COLOR_WHITE; export const HEADER_TINT = isIOS ? COLOR_PRIMARY : COLOR_WHITE; + +export const SWITCH_TRACK_COLOR = { + false: isAndroid ? COLOR_DANGER : null, + true: COLOR_SUCCESS +}; diff --git a/app/constants/settings.js b/app/constants/settings.js index 1ae229a182..4e4d993956 100644 --- a/app/constants/settings.js +++ b/app/constants/settings.js @@ -70,5 +70,8 @@ export default { }, API_Gitlab_URL: { type: 'valueAsString' + }, + AutoTranslate_Enabled: { + type: 'valueAsBoolean' } }; diff --git a/app/containers/MessageActions.js b/app/containers/MessageActions.js index bc798c7720..73a0cbd4f9 100644 --- a/app/containers/MessageActions.js +++ b/app/containers/MessageActions.js @@ -15,9 +15,11 @@ import { } from '../actions/messages'; import { vibrate } from '../utils/vibration'; import RocketChat from '../lib/rocketchat'; +import database from '../lib/realm'; import I18n from '../i18n'; import log from '../utils/log'; import Navigation from '../lib/Navigation'; +import { getMessageTranslation } from './message/utils'; @connect( state => ({ @@ -46,7 +48,7 @@ export default class MessageActions extends React.Component { room: PropTypes.object.isRequired, actionMessage: PropTypes.object, toast: PropTypes.element, - // user: PropTypes.object.isRequired, + user: PropTypes.object, deleteRequest: PropTypes.func.isRequired, editInit: PropTypes.func.isRequired, toggleStarRequest: PropTypes.func.isRequired, @@ -127,6 +129,12 @@ export default class MessageActions extends React.Component { this.READ_RECEIPT_INDEX = this.options.length - 1; } + // Toggle Auto-translate + if (props.room.autoTranslate && props.actionMessage.u && props.actionMessage.u._id !== props.user.id) { + this.options.push(I18n.t(props.actionMessage.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; @@ -326,6 +334,23 @@ export default class MessageActions extends React.Component { } } + handleToggleTranslation = async() => { + const { actionMessage, room } = this.props; + try { + const message = database.objectForPrimaryKey('messages', actionMessage._id); + database.write(() => { + message.autoTranslate = !message.autoTranslate; + message._updatedAt = new Date(); + }); + const translatedMessage = getMessageTranslation(message, room.autoTranslateLanguage); + if (!translatedMessage) { + await RocketChat.translateMessage(actionMessage, room.autoTranslateLanguage); + } + } catch (err) { + log('err_toggle_translation', err); + } + } + handleActionPress = (actionIndex) => { if (actionIndex) { switch (actionIndex) { @@ -365,6 +390,9 @@ export default class MessageActions extends React.Component { case this.READ_RECEIPT_INDEX: this.handleReadReceipt(); break; + case this.TOGGLE_TRANSLATION_INDEX: + this.handleToggleTranslation(); + break; default: break; } diff --git a/app/containers/message/index.js b/app/containers/message/index.js index 478055ad09..de62fe758b 100644 --- a/app/containers/message/index.js +++ b/app/containers/message/index.js @@ -4,7 +4,7 @@ import { KeyboardUtils } from 'react-native-keyboard-input'; import Message from './Message'; import debounce from '../../utils/debounce'; -import { SYSTEM_MESSAGES, getCustomEmoji } from './utils'; +import { SYSTEM_MESSAGES, getCustomEmoji, getMessageTranslation } from './utils'; import messagesStatus from '../../constants/messagesStatus'; export default class MessageContainer extends React.Component { @@ -27,6 +27,8 @@ export default class MessageContainer extends React.Component { isReadReceiptEnabled: PropTypes.bool, useRealName: PropTypes.bool, useMarkdown: PropTypes.bool, + autoTranslateRoom: PropTypes.bool, + autoTranslateLanguage: PropTypes.string, status: PropTypes.number, onLongPress: PropTypes.func, onReactionPress: PropTypes.func, @@ -49,12 +51,15 @@ export default class MessageContainer extends React.Component { shouldComponentUpdate(nextProps) { const { - status, item, _updatedAt + status, item, _updatedAt, autoTranslateRoom } = this.props; if (status !== nextProps.status) { return true; } + if (autoTranslateRoom !== nextProps.autoTranslateRoom) { + return true; + } if (item.tmsg !== nextProps.item.tmsg) { return true; } @@ -191,16 +196,23 @@ export default class MessageContainer extends React.Component { render() { const { - item, user, style, archived, baseUrl, useRealName, broadcast, fetchThreadName, customThreadTimeFormat, onOpenFileModal, timeFormat, useMarkdown, isReadReceiptEnabled + item, user, style, archived, baseUrl, useRealName, broadcast, fetchThreadName, customThreadTimeFormat, onOpenFileModal, timeFormat, useMarkdown, isReadReceiptEnabled, autoTranslateRoom, autoTranslateLanguage } = 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 + _id, msg, ts, attachments, urls, reactions, t, avatar, u, alias, editedBy, role, drid, dcount, dlm, tmid, tcount, tlm, tmsg, mentions, channels, unread, autoTranslate: autoTranslateMessage } = item; + let message = msg; + // "autoTranslateRoom" and "autoTranslateLanguage" are properties from the subscription + // "autoTranslateMessage" is a toggle between "View Original" and "Translate" state + if (autoTranslateRoom && autoTranslateMessage) { + message = getMessageTranslation(item, autoTranslateLanguage) || message; + } + return ( { }); return findByAlias; }; + +export const getMessageTranslation = (message, autoTranslateLanguage) => { + if (!autoTranslateLanguage) { + return null; + } + const { translations } = message; + if (translations) { + const translation = translations.find(trans => trans.language === autoTranslateLanguage); + return translation && translation.value; + } + return null; +}; diff --git a/app/i18n/locales/en.js b/app/i18n/locales/en.js index 08657afabe..e00b2fd3b2 100644 --- a/app/i18n/locales/en.js +++ b/app/i18n/locales/en.js @@ -99,6 +99,7 @@ export default { Are_you_sure_question_mark: 'Are you sure?', Are_you_sure_you_want_to_leave_the_room: 'Are you sure you want to leave the room {{room}}?', Authenticating: 'Authenticating', + Auto_Translate: 'Auto-Translate', Avatar_changed_successfully: 'Avatar changed successfully!', Avatar_Url: 'Avatar URL', Away: 'Away', @@ -155,6 +156,7 @@ export default { Email_or_password_field_is_empty: 'Email or password field is empty', Email: 'Email', email: 'e-mail', + Enable_Auto_Translate: 'Enable Auto-Translate', Enable_markdown: 'Enable markdown', Enable_notifications: 'Enable notifications', Everyone_can_access_this_channel: 'Everyone can access this channel', @@ -343,6 +345,7 @@ export default { Timezone: 'Timezone', topic: 'topic', Topic: 'Topic', + Translate: 'Translate', Try_again: 'Try again', Two_Factor_Authentication: 'Two-factor Authentication', Type_the_channel_name_here: 'Type the channel name here', @@ -374,6 +377,7 @@ export default { Username_or_email: 'Username or email', Validating: 'Validating', Video_call: 'Video call', + View_Original: 'View Original', Voice_call: 'Voice call', Welcome: 'Welcome', Welcome_to_RocketChat: 'Welcome to Rocket.Chat', diff --git a/app/index.js b/app/index.js index 04afb221a2..0eb58053f4 100644 --- a/app/index.js +++ b/app/index.js @@ -33,6 +33,7 @@ import SearchMessagesView from './views/SearchMessagesView'; import ReadReceiptsView from './views/ReadReceiptView'; import ThreadMessagesView from './views/ThreadMessagesView'; import MessagesView from './views/MessagesView'; +import AutoTranslateView from './views/AutoTranslateView'; import SelectedUsersView from './views/SelectedUsersView'; import CreateChannelView from './views/CreateChannelView'; import LegalView from './views/LegalView'; @@ -116,6 +117,7 @@ const ChatsStack = createStackNavigator({ SelectedUsersView, ThreadMessagesView, MessagesView, + AutoTranslateView, ReadReceiptsView, DirectoryView }, { diff --git a/app/lib/methods/helpers/normalizeMessage.js b/app/lib/methods/helpers/normalizeMessage.js index 39fa9dae05..f728f271e5 100644 --- a/app/lib/methods/helpers/normalizeMessage.js +++ b/app/lib/methods/helpers/normalizeMessage.js @@ -36,6 +36,9 @@ export default (msg) => { if (!Array.isArray(msg.reactions)) { msg.reactions = Object.keys(msg.reactions).map(key => ({ _id: `${ msg._id }${ key }`, emoji: key, usernames: msg.reactions[key].usernames })); } + if (msg.translations && Object.keys(msg.translations).length) { + msg.translations = Object.keys(msg.translations).map(key => ({ _id: `${ msg._id }${ key }`, language: key, value: msg.translations[key] })); + } msg.urls = msg.urls ? parseUrls(msg.urls) : []; msg._updatedAt = new Date(); // loadHistory returns msg.starred as object diff --git a/app/lib/realm.js b/app/lib/realm.js index 93ebf00dc6..7b4277882f 100644 --- a/app/lib/realm.js +++ b/app/lib/realm.js @@ -96,7 +96,9 @@ const subscriptionSchema = { broadcast: { type: 'bool', optional: true }, prid: { type: 'string', optional: true }, draftMessage: { type: 'string', optional: true }, - lastThreadSync: 'date?' + lastThreadSync: 'date?', + autoTranslate: 'bool?', + autoTranslateLanguage: 'string?' } }; @@ -171,6 +173,16 @@ const messagesReactionsSchema = { } }; +const messagesTranslationsSchema = { + name: 'messagesTranslations', + primaryKey: '_id', + properties: { + _id: 'string', + language: 'string', + value: 'string' + } +}; + const messagesEditedBySchema = { name: 'messagesEditedBy', primaryKey: '_id', @@ -212,7 +224,9 @@ const messagesSchema = { replies: 'string[]', mentions: { type: 'list', objectType: 'users' }, channels: { type: 'list', objectType: 'rooms' }, - unread: { type: 'bool', optional: true } + unread: { type: 'bool', optional: true }, + autoTranslate: { type: 'bool', default: false }, + translations: { type: 'list', objectType: 'messagesTranslations' } } }; @@ -246,6 +260,11 @@ const threadsSchema = { tcount: { type: 'int', optional: true }, tlm: { type: 'date', optional: true }, replies: 'string[]', + mentions: { type: 'list', objectType: 'users' }, + channels: { type: 'list', objectType: 'rooms' }, + unread: { type: 'bool', optional: true }, + autoTranslate: { type: 'bool', default: false }, + translations: { type: 'list', objectType: 'messagesTranslations' }, draftMessage: 'string?' } }; @@ -272,7 +291,13 @@ const threadMessagesSchema = { starred: { type: 'bool', optional: true }, editedBy: 'messagesEditedBy', reactions: { type: 'list', objectType: 'messagesReactions' }, - role: { type: 'string', optional: true } + role: { type: 'string', optional: true }, + replies: 'string[]', + mentions: { type: 'list', objectType: 'users' }, + channels: { type: 'list', objectType: 'rooms' }, + unread: { type: 'bool', optional: true }, + autoTranslate: { type: 'bool', default: false }, + translations: { type: 'list', objectType: 'messagesTranslations' } } }; @@ -374,7 +399,8 @@ const schema = [ messagesReactionsSchema, rolesSchema, uploadsSchema, - slashCommandSchema + slashCommandSchema, + messagesTranslationsSchema ]; const inMemorySchema = [usersTypingSchema, activeUsersSchema]; @@ -387,9 +413,9 @@ class DB { userSchema, serversSchema ], - schemaVersion: 8, + schemaVersion: 9, migration: (oldRealm, newRealm) => { - if (oldRealm.schemaVersion >= 1 && newRealm.schemaVersion <= 8) { + if (oldRealm.schemaVersion >= 1 && newRealm.schemaVersion <= 9) { const newServers = newRealm.objects('servers'); // eslint-disable-next-line no-plusplus @@ -444,9 +470,9 @@ class DB { return this.databases.activeDB = new Realm({ path: `${ path }.realm`, schema, - schemaVersion: 12, + schemaVersion: 13, migration: (oldRealm, newRealm) => { - if (oldRealm.schemaVersion >= 3 && newRealm.schemaVersion <= 11) { + if (oldRealm.schemaVersion >= 3 && newRealm.schemaVersion <= 13) { const newSubs = newRealm.objects('subscriptions'); newRealm.delete(newSubs); const newMessages = newRealm.objects('messages'); diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js index 673c3358af..7efcc717aa 100644 --- a/app/lib/rocketchat.js +++ b/app/lib/rocketchat.js @@ -881,6 +881,31 @@ const RocketChat = { return this.sdk.get('directory', { query, count, offset, sort }); + }, + canAutoTranslate() { + try { + const AutoTranslate_Enabled = reduxStore.getState().settings && reduxStore.getState().settings.AutoTranslate_Enabled; + if (!AutoTranslate_Enabled) { + return false; + } + const autoTranslatePermission = database.objectForPrimaryKey('permissions', 'auto-translate'); + const userRoles = (reduxStore.getState().login.user && reduxStore.getState().login.user.roles) || []; + return autoTranslatePermission.roles.some(role => userRoles.includes(role)); + } catch (error) { + log('err_can_auto_translate', error); + return false; + } + }, + saveAutoTranslate({ + rid, field, value, options + }) { + return this.sdk.methodCall('autoTranslate.saveSettings', rid, field, value, options); + }, + getSupportedLanguagesAutoTranslate() { + return this.sdk.methodCall('autoTranslate.getSupportedLanguages', 'en'); + }, + translateMessage(message, targetLanguage) { + return this.sdk.methodCall('autoTranslate.translateMessage', message, targetLanguage); } }; diff --git a/app/views/AutoTranslateView/index.js b/app/views/AutoTranslateView/index.js new file mode 100644 index 0000000000..d2691632d3 --- /dev/null +++ b/app/views/AutoTranslateView/index.js @@ -0,0 +1,156 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { + FlatList, Switch, View, StyleSheet +} from 'react-native'; +import { SafeAreaView, ScrollView } from 'react-navigation'; + +import RocketChat from '../../lib/rocketchat'; +import I18n from '../../i18n'; +// import log from '../../utils/log'; +import StatusBar from '../../containers/StatusBar'; +import { CustomIcon } from '../../lib/Icons'; +import sharedStyles from '../Styles'; +import ListItem from '../../containers/ListItem'; +import Separator from '../../containers/Separator'; +import { + SWITCH_TRACK_COLOR, COLOR_BACKGROUND_CONTAINER, COLOR_WHITE, COLOR_SEPARATOR +} from '../../constants/colors'; +import scrollPersistTaps from '../../utils/scrollPersistTaps'; +import database from '../../lib/realm'; + +const styles = StyleSheet.create({ + contentContainerStyle: { + borderColor: COLOR_SEPARATOR, + borderTopWidth: StyleSheet.hairlineWidth, + borderBottomWidth: StyleSheet.hairlineWidth, + backgroundColor: COLOR_WHITE, + marginTop: 10, + paddingBottom: 30 + }, + sectionSeparator: { + ...sharedStyles.separatorVertical, + backgroundColor: COLOR_BACKGROUND_CONTAINER, + height: 10 + } +}); + +const SectionSeparator = React.memo(() => ); + +export default class AutoTranslateView extends React.Component { + static navigationOptions = () => ({ + title: I18n.t('Auto_Translate') + }) + + static propTypes = { + navigation: PropTypes.object + } + + constructor(props) { + super(props); + this.rid = props.navigation.getParam('rid'); + this.rooms = database.objects('subscriptions').filtered('rid = $0', this.rid); + this.state = { + languages: [], + selectedLanguage: this.rooms[0].autoTranslateLanguage, + enableAutoTranslate: this.rooms[0].autoTranslate + }; + } + + async componentDidMount() { + try { + const languages = await RocketChat.getSupportedLanguagesAutoTranslate(); + this.setState({ languages }); + } catch (error) { + console.log(error); + } + } + + toggleAutoTranslate = async() => { + const { enableAutoTranslate } = this.state; + try { + await RocketChat.saveAutoTranslate({ + rid: this.rid, + field: 'autoTranslate', + value: enableAutoTranslate ? '0' : '1', + options: { defaultLanguage: 'en' } + }); + this.setState({ enableAutoTranslate: !enableAutoTranslate }); + } catch (error) { + console.log(error); + } + } + + saveAutoTranslateLanguage = async(language) => { + try { + await RocketChat.saveAutoTranslate({ + rid: this.rid, + field: 'autoTranslateLanguage', + value: language + }); + this.setState({ selectedLanguage: language }); + } catch (error) { + console.log(error); + } + } + + renderSeparator = () => + + renderIcon = () => + + renderSwitch = () => { + const { enableAutoTranslate } = this.state; + return ( + + ); + } + + renderItem = ({ item }) => { + const { selectedLanguage } = this.state; + const { language, name } = item; + const isSelected = selectedLanguage === language; + + return ( + this.saveAutoTranslateLanguage(language)} + testID={`auto-translate-view-${ language }`} + right={isSelected ? this.renderIcon : null} + /> + ); + } + + render() { + const { languages } = this.state; + return ( + + + + this.renderSwitch()} + /> + + item.language} + renderItem={this.renderItem} + ItemSeparatorComponent={this.renderSeparator} + /> + + + ); + } +} + +console.disableYellowBox = true; diff --git a/app/views/CreateChannelView.js b/app/views/CreateChannelView.js index 2cf346fccb..b29cedbda5 100644 --- a/app/views/CreateChannelView.js +++ b/app/views/CreateChannelView.js @@ -16,10 +16,9 @@ import scrollPersistTaps from '../utils/scrollPersistTaps'; import I18n from '../i18n'; import UserItem from '../presentation/UserItem'; import { showErrorAlert } from '../utils/info'; -import { isAndroid } from '../utils/deviceInfo'; import { CustomHeaderButtons, Item } from '../containers/HeaderButton'; import StatusBar from '../containers/StatusBar'; -import { COLOR_TEXT_DESCRIPTION, COLOR_WHITE } from '../constants/colors'; +import { COLOR_TEXT_DESCRIPTION, COLOR_WHITE, SWITCH_TRACK_COLOR } from '../constants/colors'; const styles = StyleSheet.create({ container: { @@ -245,8 +244,7 @@ export default class CreateChannelView extends React.Component { value={value} onValueChange={onValueChange} testID={`create-channel-${ id }`} - onTintColor='#2de0a5' - tintColor={isAndroid ? '#f5455c' : null} + trackColor={SWITCH_TRACK_COLOR} disabled={disabled} /> diff --git a/app/views/DirectoryView/Options.js b/app/views/DirectoryView/Options.js index 841484152e..31725c118c 100644 --- a/app/views/DirectoryView/Options.js +++ b/app/views/DirectoryView/Options.js @@ -9,6 +9,7 @@ import styles from './styles'; import { CustomIcon } from '../../lib/Icons'; import Check from '../../containers/Check'; import I18n from '../../i18n'; +import { SWITCH_TRACK_COLOR } from '../../constants/colors'; const ANIMATION_DURATION = 200; const ANIMATION_PROPS = { @@ -109,7 +110,7 @@ export default class DirectoryOptions extends PureComponent { {I18n.t('Search_global_users')} {I18n.t('Search_global_users_description')} - + ) diff --git a/app/views/LanguageView/index.js b/app/views/LanguageView/index.js index 045d2e9c5a..5c7522fbb8 100644 --- a/app/views/LanguageView/index.js +++ b/app/views/LanguageView/index.js @@ -46,7 +46,6 @@ const LANGUAGES = [ }), dispatch => ({ setUser: params => dispatch(setUserAction(params)) })) -/** @extends React.Component */ export default class LanguageView extends React.Component { static navigationOptions = () => ({ title: I18n.t('Change_Language') diff --git a/app/views/RoomActionsView/index.js b/app/views/RoomActionsView/index.js index 2f4d60d7f5..4c875db31f 100644 --- a/app/views/RoomActionsView/index.js +++ b/app/views/RoomActionsView/index.js @@ -60,7 +60,8 @@ export default class RoomActionsView extends React.Component { membersCount: 0, member: {}, joined: this.rooms.length > 0, - canViewMembers: false + canViewMembers: false, + canAutoTranslate: false }; } @@ -89,6 +90,10 @@ export default class RoomActionsView extends React.Component { } else if (room.t === 'd') { this.updateRoomMember(); } + + const canAutoTranslate = RocketChat.canAutoTranslate(); + this.setState({ canAutoTranslate }); + safeAddListener(this.rooms, this.updateRoom); } @@ -169,7 +174,7 @@ export default class RoomActionsView extends React.Component { get sections() { const { - room, membersCount, canViewMembers, joined + room, membersCount, canViewMembers, joined, canAutoTranslate } = this.state; const { rid, t, blocker, notifications @@ -255,6 +260,16 @@ export default class RoomActionsView extends React.Component { renderItem: this.renderItem }]; + if (canAutoTranslate) { + sections[2].data.push({ + icon: 'language', + name: I18n.t('Auto_Translate'), + route: 'AutoTranslateView', + params: { rid }, + testID: 'room-actions-auto-translate' + }); + } + if (t === 'd') { sections.push({ data: [ diff --git a/app/views/RoomInfoEditView/SwitchContainer.js b/app/views/RoomInfoEditView/SwitchContainer.js index 40f10342c7..6f32660980 100644 --- a/app/views/RoomInfoEditView/SwitchContainer.js +++ b/app/views/RoomInfoEditView/SwitchContainer.js @@ -4,6 +4,7 @@ import PropTypes from 'prop-types'; import styles from './styles'; import sharedStyles from '../Styles'; +import { SWITCH_TRACK_COLOR } from '../../constants/colors'; export default class SwitchContainer extends React.PureComponent { static propTypes = { @@ -33,6 +34,7 @@ export default class SwitchContainer extends React.PureComponent { onValueChange={onValueChange} value={value} disabled={disabled} + trackColor={SWITCH_TRACK_COLOR} testID={testID} /> diff --git a/app/views/RoomView/index.js b/app/views/RoomView/index.js index 2dc7f4fdc6..688879c7a7 100644 --- a/app/views/RoomView/index.js +++ b/app/views/RoomView/index.js @@ -138,6 +138,7 @@ export default class RoomView extends React.Component { this.t = props.navigation.getParam('t'); this.tmid = props.navigation.getParam('tmid'); this.rooms = database.objects('subscriptions').filtered('rid = $0', this.rid); + const canAutoTranslate = RocketChat.canAutoTranslate(); this.state = { joined: this.rooms.length > 0, room: this.rooms[0] || { rid: this.rid, t: this.t }, @@ -145,7 +146,8 @@ export default class RoomView extends React.Component { photoModalVisible: false, reactionsModalVisible: false, selectedAttachment: {}, - selectedMessage: {} + selectedMessage: {}, + canAutoTranslate }; this.beginAnimating = false; this.beginAnimatingTimeout = setTimeout(() => this.beginAnimating = true, 300); @@ -180,7 +182,7 @@ export default class RoomView extends React.Component { shouldComponentUpdate(nextProps, nextState) { const { - room, joined, lastOpen, photoModalVisible, reactionsModalVisible + room, joined, lastOpen, photoModalVisible, reactionsModalVisible, canAutoTranslate } = this.state; const { showActions, showErrorActions, appState } = this.props; @@ -202,6 +204,8 @@ export default class RoomView extends React.Component { return true; } else if (joined !== nextState.joined) { return true; + } else if (canAutoTranslate !== nextState.canAutoTranslate) { + return true; } else if (showActions !== nextProps.showActions) { return true; } else if (showErrorActions !== nextProps.showErrorActions) { @@ -298,6 +302,11 @@ export default class RoomView extends React.Component { this.sub = await RocketChat.subscribeRoom(room); } } + + // We run `canAutoTranslate` again in order to refetch auto translate permission + // in case of a missing connection or poor connection on room open + const canAutoTranslate = RocketChat.canAutoTranslate(); + this.setState({ canAutoTranslate }); }); } catch (e) { log('err_room_init', e); @@ -500,7 +509,7 @@ export default class RoomView extends React.Component { } renderItem = (item, previousItem) => { - const { room, lastOpen } = this.state; + const { room, lastOpen, canAutoTranslate } = this.state; const { user, Message_GroupingPeriod, Message_TimeFormat, useRealName, baseUrl, useMarkdown, Message_Read_Receipt_Enabled } = this.props; @@ -545,6 +554,8 @@ export default class RoomView extends React.Component { useRealName={useRealName} useMarkdown={useMarkdown} isReadReceiptEnabled={Message_Read_Receipt_Enabled} + autoTranslateRoom={canAutoTranslate && room.autoTranslate} + autoTranslateLanguage={room.autoTranslateLanguage} /> ); diff --git a/app/views/SettingsView/index.js b/app/views/SettingsView/index.js index b66f7cc841..751d1cf692 100644 --- a/app/views/SettingsView/index.js +++ b/app/views/SettingsView/index.js @@ -6,7 +6,7 @@ import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { toggleMarkdown as toggleMarkdownAction } from '../../actions/markdown'; -import { COLOR_DANGER, COLOR_SUCCESS } from '../../constants/colors'; +import { SWITCH_TRACK_COLOR } from '../../constants/colors'; import { DrawerButton } from '../../containers/HeaderButton'; import StatusBar from '../../containers/StatusBar'; import ListItem from '../../containers/ListItem'; @@ -14,7 +14,7 @@ import { DisclosureImage } from '../../containers/DisclosureIndicator'; import Separator from '../../containers/Separator'; import I18n from '../../i18n'; import { MARKDOWN_KEY } from '../../lib/rocketchat'; -import { getReadableVersion, getDeviceModel, isAndroid } from '../../utils/deviceInfo'; +import { getReadableVersion, getDeviceModel } from '../../utils/deviceInfo'; import openLink from '../../utils/openLink'; import scrollPersistTaps from '../../utils/scrollPersistTaps'; import { showErrorAlert } from '../../utils/info'; @@ -23,10 +23,6 @@ import sharedStyles from '../Styles'; const LICENSE_LINK = 'https://github.com/RocketChat/Rocket.Chat.ReactNative/blob/develop/LICENSE'; const SectionSeparator = React.memo(() => ); -const SWITCH_TRACK_COLOR = { - false: isAndroid ? COLOR_DANGER : null, - true: COLOR_SUCCESS -}; @connect(state => ({ server: state.server,