[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
This commit is contained in:
parent
cfa126914c
commit
b3986b98b5
|
@ -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
|
||||
};
|
||||
|
|
|
@ -70,5 +70,8 @@ export default {
|
|||
},
|
||||
API_Gitlab_URL: {
|
||||
type: 'valueAsString'
|
||||
},
|
||||
AutoTranslate_Enabled: {
|
||||
type: 'valueAsBoolean'
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 (
|
||||
<Message
|
||||
id={_id}
|
||||
msg={msg}
|
||||
msg={message}
|
||||
author={u}
|
||||
ts={ts}
|
||||
type={t}
|
||||
|
|
|
@ -114,3 +114,15 @@ export const getCustomEmoji = (content) => {
|
|||
});
|
||||
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;
|
||||
};
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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
|
||||
}, {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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(() => <View style={styles.sectionSeparator} />);
|
||||
|
||||
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 = () => <Separator />
|
||||
|
||||
renderIcon = () => <CustomIcon name='check' size={20} style={sharedStyles.colorPrimary} />
|
||||
|
||||
renderSwitch = () => {
|
||||
const { enableAutoTranslate } = this.state;
|
||||
return (
|
||||
<Switch
|
||||
value={enableAutoTranslate}
|
||||
trackColor={SWITCH_TRACK_COLOR}
|
||||
onValueChange={this.toggleAutoTranslate}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderItem = ({ item }) => {
|
||||
const { selectedLanguage } = this.state;
|
||||
const { language, name } = item;
|
||||
const isSelected = selectedLanguage === language;
|
||||
|
||||
return (
|
||||
<ListItem
|
||||
title={name || language}
|
||||
onPress={() => this.saveAutoTranslateLanguage(language)}
|
||||
testID={`auto-translate-view-${ language }`}
|
||||
right={isSelected ? this.renderIcon : null}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { languages } = this.state;
|
||||
return (
|
||||
<SafeAreaView style={sharedStyles.listSafeArea} testID='auto-translate-view' forceInset={{ bottom: 'never' }}>
|
||||
<StatusBar />
|
||||
<ScrollView
|
||||
{...scrollPersistTaps}
|
||||
contentContainerStyle={styles.contentContainerStyle}
|
||||
testID='auto-translate-view-list'
|
||||
>
|
||||
<ListItem
|
||||
title={I18n.t('Enable_Auto_Translate')}
|
||||
testID='auto-translate-view-switch'
|
||||
right={() => this.renderSwitch()}
|
||||
/>
|
||||
<SectionSeparator />
|
||||
<FlatList
|
||||
data={languages}
|
||||
extraData={this.state}
|
||||
keyExtractor={item => item.language}
|
||||
renderItem={this.renderItem}
|
||||
ItemSeparatorComponent={this.renderSeparator}
|
||||
/>
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
console.disableYellowBox = true;
|
|
@ -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}
|
||||
/>
|
||||
</View>
|
||||
|
|
|
@ -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 {
|
|||
<Text style={styles.dropdownItemText}>{I18n.t('Search_global_users')}</Text>
|
||||
<Text style={styles.dropdownItemDescription}>{I18n.t('Search_global_users_description')}</Text>
|
||||
</View>
|
||||
<Switch value={globalUsers} onValueChange={toggleWorkspace} />
|
||||
<Switch value={globalUsers} onValueChange={toggleWorkspace} trackColor={SWITCH_TRACK_COLOR} />
|
||||
</View>
|
||||
</React.Fragment>
|
||||
)
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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: [
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
<View style={styles.switchLabelContainer}>
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
);
|
||||
|
||||
|
|
|
@ -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(() => <View style={styles.sectionSeparatorBorder} />);
|
||||
const SWITCH_TRACK_COLOR = {
|
||||
false: isAndroid ? COLOR_DANGER : null,
|
||||
true: COLOR_SUCCESS
|
||||
};
|
||||
|
||||
@connect(state => ({
|
||||
server: state.server,
|
||||
|
|
Loading…
Reference in New Issue