[IMPROVEMENT] Add toggle markdown to settings (#907)

* Add toggle markdown to settings

* Remove unused translation
This commit is contained in:
Diego Mello 2019-05-21 09:12:15 -03:00 committed by GitHub
parent 9a713923f3
commit 896240457f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 112 additions and 35 deletions

View File

@ -12,8 +12,7 @@ function createRequestTypes(base, types = defaultTypes) {
export const LOGIN = createRequestTypes('LOGIN', [ export const LOGIN = createRequestTypes('LOGIN', [
...defaultTypes, ...defaultTypes,
'SET_SERVICES', 'SET_SERVICES',
'SET_PREFERENCE', 'SET_PREFERENCE'
'SET_SORT_PREFERENCE'
]); ]);
export const USER = createRequestTypes('USER', ['SET']); export const USER = createRequestTypes('USER', ['SET']);
export const ROOMS = createRequestTypes('ROOMS', [ 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 SNIPPETED_MESSAGES = createRequestTypes('SNIPPETED_MESSAGES', ['OPEN', 'READY', 'CLOSE', 'MESSAGES_RECEIVED']);
export const DEEP_LINKING = createRequestTypes('DEEP_LINKING', ['OPEN']); export const DEEP_LINKING = createRequestTypes('DEEP_LINKING', ['OPEN']);
export const SORT_PREFERENCES = createRequestTypes('SORT_PREFERENCES', ['SET_ALL', 'SET']); export const SORT_PREFERENCES = createRequestTypes('SORT_PREFERENCES', ['SET_ALL', 'SET']);
export const TOGGLE_MARKDOWN = 'TOGGLE_MARKDOWN';

8
app/actions/markdown.js Normal file
View File

@ -0,0 +1,8 @@
import * as types from './actionsTypes';
export function toggleMarkdown(value) {
return {
type: types.TOGGLE_MARKDOWN,
payload: value
};
}

View File

@ -36,7 +36,7 @@ const Markdown = React.memo(({
} }
if (!useMarkdown) { if (!useMarkdown) {
return <Text style={styles.text}>{m}</Text>; return <Text style={styles.text} numberOfLines={numberOfLines}>{m}</Text>;
} }
return ( return (

View File

@ -308,7 +308,6 @@ export default {
This_room_is_blocked: 'Dieser Raum ist gesperrt', This_room_is_blocked: 'Dieser Raum ist gesperrt',
This_room_is_read_only: 'Dieser Raum kann nur gelesen werden', This_room_is_read_only: 'Dieser Raum kann nur gelesen werden',
Timezone: 'Zeitzone', Timezone: 'Zeitzone',
Toggle_Drawer: 'Toggle_Drawer',
topic: 'Thema', topic: 'Thema',
Topic: 'Thema', Topic: 'Thema',
Try_again: 'Versuchen Sie es nochmal', Try_again: 'Versuchen Sie es nochmal',

View File

@ -153,6 +153,7 @@ export default {
Email_or_password_field_is_empty: 'Email or password field is empty', Email_or_password_field_is_empty: 'Email or password field is empty',
Email: 'Email', Email: 'Email',
email: 'e-mail', email: 'e-mail',
Enable_markdown: 'Enable markdown',
Enable_notifications: 'Enable notifications', Enable_notifications: 'Enable notifications',
Everyone_can_access_this_channel: 'Everyone can access this channel', Everyone_can_access_this_channel: 'Everyone can access this channel',
erasing_room: 'erasing room', erasing_room: 'erasing room',
@ -328,7 +329,6 @@ export default {
Thread: 'Thread', Thread: 'Thread',
Threads: 'Threads', Threads: 'Threads',
Timezone: 'Timezone', Timezone: 'Timezone',
Toggle_Drawer: 'Toggle_Drawer',
topic: 'topic', topic: 'topic',
Topic: 'Topic', Topic: 'Topic',
Try_again: 'Try again', Try_again: 'Try again',

View File

@ -309,7 +309,6 @@ export default {
This_room_is_blocked: 'Cette canal est bloquée', This_room_is_blocked: 'Cette canal est bloquée',
This_room_is_read_only: 'Cette canal est en lecture seule', This_room_is_read_only: 'Cette canal est en lecture seule',
Timezone: 'Fuseau horaire', Timezone: 'Fuseau horaire',
Toggle_Drawer: 'Toggle_Drawer',
topic: 'sujet', topic: 'sujet',
Topic: 'Sujet', Topic: 'Sujet',
Try_again: 'Réessayer', Try_again: 'Réessayer',

View File

@ -160,6 +160,7 @@ export default {
Email_or_password_field_is_empty: 'Email ou senha estão vazios', Email_or_password_field_is_empty: 'Email ou senha estão vazios',
Email: 'Email', Email: 'Email',
email: 'e-mail', email: 'e-mail',
Enable_markdown: 'Habilitar markdown',
Enable_notifications: 'Habilitar notificações', Enable_notifications: 'Habilitar notificações',
Everyone_can_access_this_channel: 'Todos podem acessar este canal', Everyone_can_access_this_channel: 'Todos podem acessar este canal',
Error_uploading: 'Erro subindo', Error_uploading: 'Erro subindo',

View File

@ -311,7 +311,6 @@ export default {
This_room_is_blocked: 'Esta sala está bloqueada', This_room_is_blocked: 'Esta sala está bloqueada',
This_room_is_read_only: 'Esta sala é apenas de leitura', This_room_is_read_only: 'Esta sala é apenas de leitura',
Timezone: 'Fuso Horário', Timezone: 'Fuso Horário',
Toggle_Drawer: 'Toggle_Drawer',
topic: 'tópico', topic: 'tópico',
Topic: 'Tópico', Topic: 'Tópico',
Try_again: 'Tente novamente', Try_again: 'Tente novamente',

View File

@ -270,7 +270,6 @@ export default {
This_room_is_blocked: 'Этот канал заблокирован', This_room_is_blocked: 'Этот канал заблокирован',
This_room_is_read_only: 'Этот канал доступен только для чтения', This_room_is_read_only: 'Этот канал доступен только для чтения',
Timezone: 'Часовой пояс', Timezone: 'Часовой пояс',
Toggle_Drawer: 'Toggle_Drawer',
topic: 'топик', topic: 'топик',
Topic: 'Топик', Topic: 'Топик',
Try_again: 'Попробуйте еще раз', Try_again: 'Попробуйте еще раз',

View File

@ -305,7 +305,6 @@ export default {
This_room_is_blocked: '这个房间被锁了', This_room_is_blocked: '这个房间被锁了',
This_room_is_read_only: '这个房间是只读的', This_room_is_read_only: '这个房间是只读的',
Timezone: '时区', Timezone: '时区',
Toggle_Drawer: 'Toggle_Drawer',
topic: '主题', topic: '主题',
Topic: '主题', Topic: '主题',
Try_again: '再试一次', Try_again: '再试一次',

View File

@ -40,6 +40,7 @@ import { roomsRequest } from '../actions/rooms';
const TOKEN_KEY = 'reactnativemeteor_usertoken'; const TOKEN_KEY = 'reactnativemeteor_usertoken';
const SORT_PREFS_KEY = 'RC_SORT_PREFS_KEY'; const SORT_PREFS_KEY = 'RC_SORT_PREFS_KEY';
export const MARKDOWN_KEY = 'RC_MARKDOWN_KEY';
const returnAnArray = obj => obj || []; const returnAnArray = obj => obj || [];
const MIN_ROCKETCHAT_VERSION = '0.70.0'; const MIN_ROCKETCHAT_VERSION = '0.70.0';
@ -685,6 +686,13 @@ const RocketChat = {
// RC 0.51.0 // RC 0.51.0
return this.sdk.methodCall('setAvatarFromService', data, contentType, service); 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() { async getSortPreferences() {
const prefs = await AsyncStorage.getItem(SORT_PREFS_KEY); const prefs = await AsyncStorage.getItem(SORT_PREFS_KEY);
return JSON.parse(prefs); return JSON.parse(prefs);

View File

@ -9,6 +9,7 @@ import selectedUsers from './selectedUsers';
import createChannel from './createChannel'; import createChannel from './createChannel';
import app from './app'; import app from './app';
import sortPreferences from './sortPreferences'; import sortPreferences from './sortPreferences';
import markdown from './markdown';
export default combineReducers({ export default combineReducers({
settings, settings,
@ -20,5 +21,6 @@ export default combineReducers({
createChannel, createChannel,
app, app,
rooms, rooms,
sortPreferences sortPreferences,
markdown
}); });

17
app/reducers/markdown.js Normal file
View File

@ -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;
}
};

View File

@ -5,6 +5,7 @@ import SplashScreen from 'react-native-splash-screen';
import * as actions from '../actions'; import * as actions from '../actions';
import { selectServerRequest } from '../actions/server'; import { selectServerRequest } from '../actions/server';
import { setAllPreferences } from '../actions/sortPreferences'; import { setAllPreferences } from '../actions/sortPreferences';
import { toggleMarkdown } from '../actions/markdown';
import { APP } from '../actions/actionsTypes'; import { APP } from '../actions/actionsTypes';
import RocketChat from '../lib/rocketchat'; import RocketChat from '../lib/rocketchat';
import log from '../utils/log'; import log from '../utils/log';
@ -21,6 +22,9 @@ const restore = function* restore() {
const sortPreferences = yield RocketChat.getSortPreferences(); const sortPreferences = yield RocketChat.getSortPreferences();
yield put(setAllPreferences(sortPreferences)); yield put(setAllPreferences(sortPreferences));
const useMarkdown = yield RocketChat.getUseMarkdown();
yield put(toggleMarkdown(useMarkdown));
if (!token || !server) { if (!token || !server) {
yield all([ yield all([
AsyncStorage.removeItem(RocketChat.TOKEN_KEY), AsyncStorage.removeItem(RocketChat.TOKEN_KEY),

View File

@ -60,6 +60,7 @@ import { Toast } from '../../utils/info';
isAuthenticated: state.login.isAuthenticated, isAuthenticated: state.login.isAuthenticated,
Message_GroupingPeriod: state.settings.Message_GroupingPeriod, Message_GroupingPeriod: state.settings.Message_GroupingPeriod,
Message_TimeFormat: state.settings.Message_TimeFormat, 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 : ''
}), dispatch => ({ }), dispatch => ({
editCancel: () => dispatch(editCancelAction()), editCancel: () => dispatch(editCancelAction()),
@ -120,6 +121,7 @@ export default class RoomView extends LoggedView {
editing: PropTypes.bool, editing: PropTypes.bool,
replying: PropTypes.bool, replying: PropTypes.bool,
baseUrl: PropTypes.string, baseUrl: PropTypes.string,
useMarkdown: PropTypes.bool,
toggleReactionPicker: PropTypes.func, toggleReactionPicker: PropTypes.func,
actionsShow: PropTypes.func, actionsShow: PropTypes.func,
editCancel: PropTypes.func, editCancel: PropTypes.func,
@ -135,7 +137,6 @@ export default class RoomView extends LoggedView {
this.rid = props.navigation.getParam('rid'); this.rid = props.navigation.getParam('rid');
this.t = props.navigation.getParam('t'); this.t = props.navigation.getParam('t');
this.tmid = props.navigation.getParam('tmid'); this.tmid = props.navigation.getParam('tmid');
this.useMarkdown = props.navigation.getParam('useMarkdown', true);
this.rooms = database.objects('subscriptions').filtered('rid = $0', this.rid); this.rooms = database.objects('subscriptions').filtered('rid = $0', this.rid);
this.state = { this.state = {
joined: this.rooms.length > 0, joined: this.rooms.length > 0,
@ -504,7 +505,7 @@ export default class RoomView extends LoggedView {
renderItem = (item, previousItem) => { renderItem = (item, previousItem) => {
const { room, lastOpen } = this.state; const { room, lastOpen } = this.state;
const { const {
user, Message_GroupingPeriod, Message_TimeFormat, useRealName, baseUrl user, Message_GroupingPeriod, Message_TimeFormat, useRealName, baseUrl, useMarkdown
} = this.props; } = this.props;
let dateSeparator = null; let dateSeparator = null;
let showUnreadSeparator = false; let showUnreadSeparator = false;
@ -545,7 +546,7 @@ export default class RoomView extends LoggedView {
Message_GroupingPeriod={Message_GroupingPeriod} Message_GroupingPeriod={Message_GroupingPeriod}
timeFormat={Message_TimeFormat} timeFormat={Message_TimeFormat}
useRealName={useRealName} useRealName={useRealName}
useMarkdown={this.useMarkdown} useMarkdown={useMarkdown}
/> />
); );

View File

@ -66,7 +66,6 @@ export default class RoomsListView extends LoggedView {
const cancelSearchingAndroid = navigation.getParam('cancelSearchingAndroid'); const cancelSearchingAndroid = navigation.getParam('cancelSearchingAndroid');
const onPressItem = navigation.getParam('onPressItem', () => {}); const onPressItem = navigation.getParam('onPressItem', () => {});
const initSearchingAndroid = navigation.getParam('initSearchingAndroid', () => {}); const initSearchingAndroid = navigation.getParam('initSearchingAndroid', () => {});
const toggleUseMarkdown = navigation.getParam('toggleUseMarkdown', () => {});
return { return {
headerLeft: ( headerLeft: (
@ -76,7 +75,7 @@ export default class RoomsListView extends LoggedView {
<Item title='cancel' iconName='cross' onPress={cancelSearchingAndroid} /> <Item title='cancel' iconName='cross' onPress={cancelSearchingAndroid} />
</CustomHeaderButtons> </CustomHeaderButtons>
) )
: <DrawerButton navigation={navigation} testID='rooms-list-view-sidebar' onLongPress={toggleUseMarkdown} /> : <DrawerButton navigation={navigation} testID='rooms-list-view-sidebar' />
), ),
headerTitle: <RoomsListHeaderView />, headerTitle: <RoomsListHeaderView />,
headerRight: ( headerRight: (
@ -125,7 +124,6 @@ export default class RoomsListView extends LoggedView {
searching: false, searching: false,
search: [], search: [],
loading: true, loading: true,
useMarkdown: true,
chats: [], chats: [],
unread: [], unread: [],
favorites: [], favorites: [],
@ -146,8 +144,7 @@ export default class RoomsListView extends LoggedView {
navigation.setParams({ navigation.setParams({
onPressItem: this._onPressItem, onPressItem: this._onPressItem,
initSearchingAndroid: this.initSearchingAndroid, initSearchingAndroid: this.initSearchingAndroid,
cancelSearchingAndroid: this.cancelSearchingAndroid, cancelSearchingAndroid: this.cancelSearchingAndroid
toggleUseMarkdown: this.toggleUseMarkdown
}); });
console.timeEnd(`${ this.constructor.name } mount`); 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) // this is necessary during development (enables Cmd + r)
hasActiveDB = () => database && database.databases && database.databases.activeDB; hasActiveDB = () => database && database.databases && database.databases.activeDB;
@ -355,10 +343,9 @@ export default class RoomsListView extends LoggedView {
goRoom = (item) => { goRoom = (item) => {
this.cancelSearchingAndroid(); this.cancelSearchingAndroid();
const { useMarkdown } = this.state;
const { navigation } = this.props; const { navigation } = this.props;
navigation.navigate('RoomView', { 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
}); });
} }

View File

@ -1,12 +1,15 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; 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 RNPickerSelect from 'react-native-picker-select';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { SafeAreaView } from 'react-navigation'; import { SafeAreaView } from 'react-navigation';
import { Answers } from 'react-native-fabric';
import LoggedView from '../View'; import LoggedView from '../View';
import RocketChat from '../../lib/rocketchat'; import RocketChat, { MARKDOWN_KEY } from '../../lib/rocketchat';
import KeyboardView from '../../presentation/KeyboardView'; import KeyboardView from '../../presentation/KeyboardView';
import sharedStyles from '../Styles'; import sharedStyles from '../Styles';
import RCTextInput from '../../containers/TextInput'; import RCTextInput from '../../containers/TextInput';
@ -17,13 +20,41 @@ import Loading from '../../containers/Loading';
import { showErrorAlert, Toast } from '../../utils/info'; import { showErrorAlert, Toast } from '../../utils/info';
import log from '../../utils/log'; import log from '../../utils/log';
import { setUser as setUserAction } from '../../actions/login'; import { setUser as setUserAction } from '../../actions/login';
import { toggleMarkdown as toggleMarkdownAction } from '../../actions/markdown';
import { DrawerButton } from '../../containers/HeaderButton'; import { DrawerButton } from '../../containers/HeaderButton';
import StatusBar from '../../containers/StatusBar'; 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 => ({ @connect(state => ({
userLanguage: state.login.user && state.login.user.language userLanguage: state.login.user && state.login.user.language,
useMarkdown: state.markdown.useMarkdown
}), dispatch => ({ }), dispatch => ({
setUser: params => dispatch(setUserAction(params)) setUser: params => dispatch(setUserAction(params)),
toggleMarkdown: params => dispatch(toggleMarkdownAction(params))
})) }))
/** @extends React.Component */ /** @extends React.Component */
export default class SettingsView extends LoggedView { export default class SettingsView extends LoggedView {
@ -35,7 +66,9 @@ export default class SettingsView extends LoggedView {
static propTypes = { static propTypes = {
componentId: PropTypes.string, componentId: PropTypes.string,
userLanguage: PropTypes.string, userLanguage: PropTypes.string,
setUser: PropTypes.func useMarkdown: PropTypes.bool,
setUser: PropTypes.func,
toggleMarkdown: PropTypes.func
} }
constructor(props) { constructor(props) {
@ -71,13 +104,16 @@ export default class SettingsView extends LoggedView {
shouldComponentUpdate(nextProps, nextState) { shouldComponentUpdate(nextProps, nextState) {
const { language, saving } = this.state; const { language, saving } = this.state;
const { userLanguage } = this.props; const { userLanguage, useMarkdown } = this.props;
if (nextState.language !== language) { if (nextState.language !== language) {
return true; return true;
} }
if (nextState.saving !== saving) { if (nextState.saving !== saving) {
return true; return true;
} }
if (nextProps.useMarkdown !== useMarkdown) {
return true;
}
if (nextProps.userLanguage !== userLanguage) { if (nextProps.userLanguage !== userLanguage) {
return true; 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() { render() {
const { const {
language, languages, placeholder, saving language, languages, placeholder, saving
} = this.state; } = this.state;
const { useMarkdown } = this.props;
return ( return (
<KeyboardView <KeyboardView
contentContainerStyle={sharedStyles.container} contentContainerStyle={sharedStyles.container}
@ -174,6 +218,16 @@ export default class SettingsView extends LoggedView {
testID='settings-view-button' testID='settings-view-button'
/> />
</View> </View>
<View style={styles.separator} />
<View style={styles.swithContainer}>
<Text style={styles.label}>{I18n.t('Enable_markdown')}</Text>
<Switch
value={useMarkdown}
onValueChange={this.toggleMarkdown}
onTintColor={COLOR_SUCCESS}
tintColor={isAndroid ? COLOR_DANGER : null}
/>
</View>
<Loading visible={saving} /> <Loading visible={saving} />
<Toast ref={toast => this.toast = toast} /> <Toast ref={toast => this.toast = toast} />
</SafeAreaView> </SafeAreaView>