diff --git a/app/actions/actionsTypes.js b/app/actions/actionsTypes.js index 3a0d3bbd4..29545dc22 100644 --- a/app/actions/actionsTypes.js +++ b/app/actions/actionsTypes.js @@ -12,8 +12,7 @@ function createRequestTypes(base, types = defaultTypes) { export const LOGIN = createRequestTypes('LOGIN', [ ...defaultTypes, 'SET_SERVICES', - 'SET_PREFERENCE', - 'SET_SORT_PREFERENCE' + 'SET_PREFERENCE' ]); export const USER = createRequestTypes('USER', ['SET']); export const ROOMS = createRequestTypes('ROOMS', [ @@ -67,3 +66,4 @@ export const LOGOUT = 'LOGOUT'; // logout is always success export const SNIPPETED_MESSAGES = createRequestTypes('SNIPPETED_MESSAGES', ['OPEN', 'READY', 'CLOSE', 'MESSAGES_RECEIVED']); export const DEEP_LINKING = createRequestTypes('DEEP_LINKING', ['OPEN']); export const SORT_PREFERENCES = createRequestTypes('SORT_PREFERENCES', ['SET_ALL', 'SET']); +export const TOGGLE_MARKDOWN = 'TOGGLE_MARKDOWN'; diff --git a/app/actions/markdown.js b/app/actions/markdown.js new file mode 100644 index 000000000..d33075a38 --- /dev/null +++ b/app/actions/markdown.js @@ -0,0 +1,8 @@ +import * as types from './actionsTypes'; + +export function toggleMarkdown(value) { + return { + type: types.TOGGLE_MARKDOWN, + payload: value + }; +} diff --git a/app/containers/message/Markdown.js b/app/containers/message/Markdown.js index 9cdd6804e..ae8cc77af 100644 --- a/app/containers/message/Markdown.js +++ b/app/containers/message/Markdown.js @@ -36,7 +36,7 @@ const Markdown = React.memo(({ } if (!useMarkdown) { - return {m}; + return {m}; } return ( diff --git a/app/i18n/locales/de.js b/app/i18n/locales/de.js index 0e90bcb53..e0b0c6f6e 100644 --- a/app/i18n/locales/de.js +++ b/app/i18n/locales/de.js @@ -308,7 +308,6 @@ export default { This_room_is_blocked: 'Dieser Raum ist gesperrt', This_room_is_read_only: 'Dieser Raum kann nur gelesen werden', Timezone: 'Zeitzone', - Toggle_Drawer: 'Toggle_Drawer', topic: 'Thema', Topic: 'Thema', Try_again: 'Versuchen Sie es nochmal', diff --git a/app/i18n/locales/en.js b/app/i18n/locales/en.js index f6bd6d801..62b4f117a 100644 --- a/app/i18n/locales/en.js +++ b/app/i18n/locales/en.js @@ -153,6 +153,7 @@ export default { Email_or_password_field_is_empty: 'Email or password field is empty', Email: 'Email', email: 'e-mail', + Enable_markdown: 'Enable markdown', Enable_notifications: 'Enable notifications', Everyone_can_access_this_channel: 'Everyone can access this channel', erasing_room: 'erasing room', @@ -328,7 +329,6 @@ export default { Thread: 'Thread', Threads: 'Threads', Timezone: 'Timezone', - Toggle_Drawer: 'Toggle_Drawer', topic: 'topic', Topic: 'Topic', Try_again: 'Try again', diff --git a/app/i18n/locales/fr.js b/app/i18n/locales/fr.js index bb9ff4e04..cf5129e67 100644 --- a/app/i18n/locales/fr.js +++ b/app/i18n/locales/fr.js @@ -309,7 +309,6 @@ export default { This_room_is_blocked: 'Cette canal est bloquée', This_room_is_read_only: 'Cette canal est en lecture seule', Timezone: 'Fuseau horaire', - Toggle_Drawer: 'Toggle_Drawer', topic: 'sujet', Topic: 'Sujet', Try_again: 'Réessayer', diff --git a/app/i18n/locales/pt-BR.js b/app/i18n/locales/pt-BR.js index 53ed7270d..859b4f299 100644 --- a/app/i18n/locales/pt-BR.js +++ b/app/i18n/locales/pt-BR.js @@ -160,6 +160,7 @@ export default { Email_or_password_field_is_empty: 'Email ou senha estão vazios', Email: 'Email', email: 'e-mail', + Enable_markdown: 'Habilitar markdown', Enable_notifications: 'Habilitar notificações', Everyone_can_access_this_channel: 'Todos podem acessar este canal', Error_uploading: 'Erro subindo', diff --git a/app/i18n/locales/pt-PT.js b/app/i18n/locales/pt-PT.js index f16bc95ac..c81675694 100644 --- a/app/i18n/locales/pt-PT.js +++ b/app/i18n/locales/pt-PT.js @@ -311,7 +311,6 @@ export default { This_room_is_blocked: 'Esta sala está bloqueada', This_room_is_read_only: 'Esta sala é apenas de leitura', Timezone: 'Fuso Horário', - Toggle_Drawer: 'Toggle_Drawer', topic: 'tópico', Topic: 'Tópico', Try_again: 'Tente novamente', diff --git a/app/i18n/locales/ru.js b/app/i18n/locales/ru.js index 32ca154cd..780706f9a 100644 --- a/app/i18n/locales/ru.js +++ b/app/i18n/locales/ru.js @@ -270,7 +270,6 @@ export default { This_room_is_blocked: 'Этот канал заблокирован', This_room_is_read_only: 'Этот канал доступен только для чтения', Timezone: 'Часовой пояс', - Toggle_Drawer: 'Toggle_Drawer', topic: 'топик', Topic: 'Топик', Try_again: 'Попробуйте еще раз', diff --git a/app/i18n/locales/zh-CN.js b/app/i18n/locales/zh-CN.js index 4748d8202..56c18f1bc 100644 --- a/app/i18n/locales/zh-CN.js +++ b/app/i18n/locales/zh-CN.js @@ -305,7 +305,6 @@ export default { This_room_is_blocked: '这个房间被锁了', This_room_is_read_only: '这个房间是只读的', Timezone: '时区', - Toggle_Drawer: 'Toggle_Drawer', topic: '主题', Topic: '主题', Try_again: '再试一次', diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js index b0a118dc0..e83c3b5ef 100644 --- a/app/lib/rocketchat.js +++ b/app/lib/rocketchat.js @@ -40,6 +40,7 @@ import { roomsRequest } from '../actions/rooms'; const TOKEN_KEY = 'reactnativemeteor_usertoken'; const SORT_PREFS_KEY = 'RC_SORT_PREFS_KEY'; +export const MARKDOWN_KEY = 'RC_MARKDOWN_KEY'; const returnAnArray = obj => obj || []; const MIN_ROCKETCHAT_VERSION = '0.70.0'; @@ -685,6 +686,13 @@ const RocketChat = { // RC 0.51.0 return this.sdk.methodCall('setAvatarFromService', data, contentType, service); }, + async getUseMarkdown() { + const useMarkdown = await AsyncStorage.getItem(MARKDOWN_KEY); + if (useMarkdown === null) { + return true; + } + return JSON.parse(useMarkdown); + }, async getSortPreferences() { const prefs = await AsyncStorage.getItem(SORT_PREFS_KEY); return JSON.parse(prefs); diff --git a/app/reducers/index.js b/app/reducers/index.js index 9d2b023cb..d33c26abc 100644 --- a/app/reducers/index.js +++ b/app/reducers/index.js @@ -9,6 +9,7 @@ import selectedUsers from './selectedUsers'; import createChannel from './createChannel'; import app from './app'; import sortPreferences from './sortPreferences'; +import markdown from './markdown'; export default combineReducers({ settings, @@ -20,5 +21,6 @@ export default combineReducers({ createChannel, app, rooms, - sortPreferences + sortPreferences, + markdown }); diff --git a/app/reducers/markdown.js b/app/reducers/markdown.js new file mode 100644 index 000000000..aa3fb49f4 --- /dev/null +++ b/app/reducers/markdown.js @@ -0,0 +1,17 @@ +import { TOGGLE_MARKDOWN } from '../actions/actionsTypes'; + +const initialState = { + useMarkdown: true +}; + + +export default (state = initialState, action) => { + switch (action.type) { + case TOGGLE_MARKDOWN: + return { + useMarkdown: action.payload + }; + default: + return state; + } +}; diff --git a/app/sagas/init.js b/app/sagas/init.js index 1b458e6e5..cf15cbc2a 100644 --- a/app/sagas/init.js +++ b/app/sagas/init.js @@ -5,6 +5,7 @@ import SplashScreen from 'react-native-splash-screen'; import * as actions from '../actions'; import { selectServerRequest } from '../actions/server'; import { setAllPreferences } from '../actions/sortPreferences'; +import { toggleMarkdown } from '../actions/markdown'; import { APP } from '../actions/actionsTypes'; import RocketChat from '../lib/rocketchat'; import log from '../utils/log'; @@ -21,6 +22,9 @@ const restore = function* restore() { const sortPreferences = yield RocketChat.getSortPreferences(); yield put(setAllPreferences(sortPreferences)); + const useMarkdown = yield RocketChat.getUseMarkdown(); + yield put(toggleMarkdown(useMarkdown)); + if (!token || !server) { yield all([ AsyncStorage.removeItem(RocketChat.TOKEN_KEY), diff --git a/app/views/RoomView/index.js b/app/views/RoomView/index.js index 888615e91..df829b89e 100644 --- a/app/views/RoomView/index.js +++ b/app/views/RoomView/index.js @@ -60,6 +60,7 @@ import { Toast } from '../../utils/info'; isAuthenticated: state.login.isAuthenticated, 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 : '' }), dispatch => ({ editCancel: () => dispatch(editCancelAction()), @@ -120,6 +121,7 @@ export default class RoomView extends LoggedView { editing: PropTypes.bool, replying: PropTypes.bool, baseUrl: PropTypes.string, + useMarkdown: PropTypes.bool, toggleReactionPicker: PropTypes.func, actionsShow: PropTypes.func, editCancel: PropTypes.func, @@ -135,7 +137,6 @@ export default class RoomView extends LoggedView { this.rid = props.navigation.getParam('rid'); this.t = props.navigation.getParam('t'); this.tmid = props.navigation.getParam('tmid'); - this.useMarkdown = props.navigation.getParam('useMarkdown', true); this.rooms = database.objects('subscriptions').filtered('rid = $0', this.rid); this.state = { joined: this.rooms.length > 0, @@ -504,7 +505,7 @@ export default class RoomView extends LoggedView { renderItem = (item, previousItem) => { const { room, lastOpen } = this.state; const { - user, Message_GroupingPeriod, Message_TimeFormat, useRealName, baseUrl + user, Message_GroupingPeriod, Message_TimeFormat, useRealName, baseUrl, useMarkdown } = this.props; let dateSeparator = null; let showUnreadSeparator = false; @@ -545,7 +546,7 @@ export default class RoomView extends LoggedView { Message_GroupingPeriod={Message_GroupingPeriod} timeFormat={Message_TimeFormat} useRealName={useRealName} - useMarkdown={this.useMarkdown} + useMarkdown={useMarkdown} /> ); diff --git a/app/views/RoomsListView/index.js b/app/views/RoomsListView/index.js index 49e48bddf..072cda7cd 100644 --- a/app/views/RoomsListView/index.js +++ b/app/views/RoomsListView/index.js @@ -66,7 +66,6 @@ export default class RoomsListView extends LoggedView { const cancelSearchingAndroid = navigation.getParam('cancelSearchingAndroid'); const onPressItem = navigation.getParam('onPressItem', () => {}); const initSearchingAndroid = navigation.getParam('initSearchingAndroid', () => {}); - const toggleUseMarkdown = navigation.getParam('toggleUseMarkdown', () => {}); return { headerLeft: ( @@ -76,7 +75,7 @@ export default class RoomsListView extends LoggedView { ) - : + : ), headerTitle: , headerRight: ( @@ -125,7 +124,6 @@ export default class RoomsListView extends LoggedView { searching: false, search: [], loading: true, - useMarkdown: true, chats: [], unread: [], favorites: [], @@ -146,8 +144,7 @@ export default class RoomsListView extends LoggedView { navigation.setParams({ onPressItem: this._onPressItem, initSearchingAndroid: this.initSearchingAndroid, - cancelSearchingAndroid: this.cancelSearchingAndroid, - toggleUseMarkdown: this.toggleUseMarkdown + cancelSearchingAndroid: this.cancelSearchingAndroid }); console.timeEnd(`${ this.constructor.name } mount`); } @@ -316,15 +313,6 @@ export default class RoomsListView extends LoggedView { } } - // Just for tests purposes - toggleUseMarkdown = () => { - this.setState(({ useMarkdown }) => ({ useMarkdown: !useMarkdown }), - () => { - const { useMarkdown } = this.state; - alert(`Markdown ${ useMarkdown ? 'enabled' : 'disabled' }`); - }); - } - // this is necessary during development (enables Cmd + r) hasActiveDB = () => database && database.databases && database.databases.activeDB; @@ -355,10 +343,9 @@ export default class RoomsListView extends LoggedView { goRoom = (item) => { this.cancelSearchingAndroid(); - const { useMarkdown } = this.state; const { navigation } = this.props; navigation.navigate('RoomView', { - rid: item.rid, name: this.getRoomTitle(item), t: item.t, prid: item.prid, useMarkdown + rid: item.rid, name: this.getRoomTitle(item), t: item.t, prid: item.prid }); } diff --git a/app/views/SettingsView/index.js b/app/views/SettingsView/index.js index 50a073668..1c0c84fe9 100644 --- a/app/views/SettingsView/index.js +++ b/app/views/SettingsView/index.js @@ -1,12 +1,15 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { View, ScrollView } from 'react-native'; +import { + View, ScrollView, Switch, Text, StyleSheet, AsyncStorage +} from 'react-native'; import RNPickerSelect from 'react-native-picker-select'; import { connect } from 'react-redux'; import { SafeAreaView } from 'react-navigation'; +import { Answers } from 'react-native-fabric'; import LoggedView from '../View'; -import RocketChat from '../../lib/rocketchat'; +import RocketChat, { MARKDOWN_KEY } from '../../lib/rocketchat'; import KeyboardView from '../../presentation/KeyboardView'; import sharedStyles from '../Styles'; import RCTextInput from '../../containers/TextInput'; @@ -17,13 +20,41 @@ import Loading from '../../containers/Loading'; import { showErrorAlert, Toast } from '../../utils/info'; import log from '../../utils/log'; import { setUser as setUserAction } from '../../actions/login'; +import { toggleMarkdown as toggleMarkdownAction } from '../../actions/markdown'; import { DrawerButton } from '../../containers/HeaderButton'; import StatusBar from '../../containers/StatusBar'; +import { isAndroid } from '../../utils/deviceInfo'; +import { + COLOR_WHITE, COLOR_SEPARATOR, COLOR_DANGER, COLOR_SUCCESS +} from '../../constants/colors'; + +const styles = StyleSheet.create({ + swithContainer: { + backgroundColor: COLOR_WHITE, + alignItems: 'center', + justifyContent: 'space-between', + flexDirection: 'row' + }, + label: { + fontSize: 17, + flex: 1, + ...sharedStyles.textMedium, + ...sharedStyles.textColorNormal + }, + separator: { + flex: 1, + height: 1, + backgroundColor: COLOR_SEPARATOR, + marginVertical: 10 + } +}); @connect(state => ({ - userLanguage: state.login.user && state.login.user.language + userLanguage: state.login.user && state.login.user.language, + useMarkdown: state.markdown.useMarkdown }), dispatch => ({ - setUser: params => dispatch(setUserAction(params)) + setUser: params => dispatch(setUserAction(params)), + toggleMarkdown: params => dispatch(toggleMarkdownAction(params)) })) /** @extends React.Component */ export default class SettingsView extends LoggedView { @@ -35,7 +66,9 @@ export default class SettingsView extends LoggedView { static propTypes = { componentId: PropTypes.string, userLanguage: PropTypes.string, - setUser: PropTypes.func + useMarkdown: PropTypes.bool, + setUser: PropTypes.func, + toggleMarkdown: PropTypes.func } constructor(props) { @@ -71,13 +104,16 @@ export default class SettingsView extends LoggedView { shouldComponentUpdate(nextProps, nextState) { const { language, saving } = this.state; - const { userLanguage } = this.props; + const { userLanguage, useMarkdown } = this.props; if (nextState.language !== language) { return true; } if (nextState.saving !== saving) { return true; } + if (nextProps.useMarkdown !== useMarkdown) { + return true; + } if (nextProps.userLanguage !== userLanguage) { return true; } @@ -133,10 +169,18 @@ export default class SettingsView extends LoggedView { } } + toggleMarkdown = (value) => { + AsyncStorage.setItem(MARKDOWN_KEY, JSON.stringify(value)); + const { toggleMarkdown } = this.props; + toggleMarkdown(value); + Answers.logCustom('toggle_markdown', { value }); + } + render() { const { language, languages, placeholder, saving } = this.state; + const { useMarkdown } = this.props; return ( + + + {I18n.t('Enable_markdown')} + + this.toast = toast} />