[NEW] User notification preferences (#2403)
* Button to preferences view Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com> * Create screen to preferences and listItem to notifications Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com> * Refactoring NotificationPreferencesView Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com> * List notification preferences Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com> * Adding translations to labels Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com> * SetUserPreferences api call Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com> * Saving new user preference in API Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com> * Fix lint Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com> * Add in-app notification test Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com> * Fix in app mentions preference Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com> * Improve object in testInAppNotification Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com> * Removing improper options for NotificationpreferencesView Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com> * Adding API version Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com> * Use redux in UserNotificationPrefView Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com> * Remove in app test Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com> * Use components from another view Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com> * Removing verification for testing in-app notifications Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com> * Move to ProfileView Co-authored-by: Diego Mello <diegolmello@gmail.com>
This commit is contained in:
parent
a07b4cf81d
commit
54c4614e2e
|
@ -61,6 +61,12 @@ export const SaveButton = React.memo(({ onPress, testID, ...props }) => (
|
|||
</CustomHeaderButtons>
|
||||
));
|
||||
|
||||
export const PreferencesButton = React.memo(({ onPress, testID, ...props }) => (
|
||||
<CustomHeaderButtons>
|
||||
<Item title='preferences' iconName='settings' onPress={onPress} testID={testID} {...props} />
|
||||
</CustomHeaderButtons>
|
||||
));
|
||||
|
||||
export const LegalButton = React.memo(({ navigation, testID }) => (
|
||||
<MoreButton onPress={() => navigation.navigate('LegalView')} testID={testID} />
|
||||
));
|
||||
|
@ -89,6 +95,10 @@ SaveButton.propTypes = {
|
|||
onPress: PropTypes.func.isRequired,
|
||||
testID: PropTypes.string.isRequired
|
||||
};
|
||||
PreferencesButton.propTypes = {
|
||||
onPress: PropTypes.func.isRequired,
|
||||
testID: PropTypes.string.isRequired
|
||||
};
|
||||
LegalButton.propTypes = {
|
||||
navigation: PropTypes.object.isRequired,
|
||||
testID: PropTypes.string.isRequired
|
||||
|
|
|
@ -181,6 +181,8 @@ export default {
|
|||
description: 'description',
|
||||
Description: 'Description',
|
||||
DESKTOP_OPTIONS: 'DESKTOP OPTIONS',
|
||||
DESKTOP_NOTIFICATIONS: 'DESKTOP NOTIFICATIONS',
|
||||
Desktop_Alert_info: 'These notifications are delivered in desktop',
|
||||
Directory: 'Directory',
|
||||
Direct_Messages: 'Direct Messages',
|
||||
Disable_notifications: 'Disable notifications',
|
||||
|
@ -197,6 +199,8 @@ export default {
|
|||
Edit: 'Edit',
|
||||
Edit_Status: 'Edit Status',
|
||||
Edit_Invite: 'Edit Invite',
|
||||
Email_Notification_Mode_All: 'Every Mention/DM',
|
||||
Email_Notification_Mode_Disabled: 'Disabled',
|
||||
Email_or_password_field_is_empty: 'Email or password field is empty',
|
||||
Email: 'Email',
|
||||
EMAIL: 'EMAIL',
|
||||
|
@ -567,6 +571,7 @@ export default {
|
|||
You: 'You',
|
||||
Logged_out_by_server: 'You\'ve been logged out by the server. Please log in again.',
|
||||
You_need_to_access_at_least_one_RocketChat_server_to_share_something: 'You need to access at least one Rocket.Chat server to share something.',
|
||||
You_need_to_verifiy_your_email_address_to_get_notications: 'You need to verify your email address to get notifications',
|
||||
Your_certificate: 'Your Certificate',
|
||||
Your_message: 'Your message',
|
||||
Your_invite_link_will_expire_after__usesLeft__uses: 'Your invite link will expire after {{usesLeft}} uses.',
|
||||
|
|
|
@ -175,6 +175,8 @@ export default {
|
|||
deleting_room: 'excluindo sala',
|
||||
Direct_Messages: 'Mensagens Diretas',
|
||||
DESKTOP_OPTIONS: 'OPÇÕES DE ÁREA DE TRABALHO',
|
||||
DESKTOP_NOTIFICATIONS: 'NOTIFICAÇÕES DE ÁREA DE TRABALHO',
|
||||
Desktop_Alert_info: 'Essas notificações são entregues a você na área de trabalho',
|
||||
Directory: 'Diretório',
|
||||
description: 'descrição',
|
||||
Description: 'Descrição',
|
||||
|
@ -195,6 +197,8 @@ export default {
|
|||
Email: 'Email',
|
||||
email: 'e-mail',
|
||||
Empty_title: 'Título vazio',
|
||||
Email_Notification_Mode_All: 'Cada Menção / Mensagem Direta',
|
||||
Email_Notification_Mode_Disabled: 'Desativado',
|
||||
Enable_Auto_Translate: 'Ativar a tradução automática',
|
||||
Enable_notifications: 'Habilitar notificações',
|
||||
Everyone_can_access_this_channel: 'Todos podem acessar este canal',
|
||||
|
@ -508,6 +512,7 @@ export default {
|
|||
You_are_offline: 'Você está offline',
|
||||
You_can_search_using_RegExp_eg: 'Você pode usar expressões regulares, por exemplo `/^text$/i`',
|
||||
Your_message: 'Sua mensagem',
|
||||
You_need_to_verifiy_your_email_address_to_get_notications: 'Você precisa confirmar seu endereço de e-mail para obter notificações',
|
||||
You_colon: 'Você: ',
|
||||
you_were_mentioned: 'você foi mencionado',
|
||||
You_were_removed_from_channel: 'Você foi removido de {{channel}}',
|
||||
|
|
|
@ -726,6 +726,10 @@ const RocketChat = {
|
|||
setUserPresenceOnline() {
|
||||
return this.methodCall('UserPresence:online');
|
||||
},
|
||||
setUserPreferences(userId, data) {
|
||||
// RC 0.62.0
|
||||
return this.sdk.post('users.setPreferences', { userId, data });
|
||||
},
|
||||
setUserStatus(status, message) {
|
||||
// RC 1.2.0
|
||||
return this.post('users.setStatus', { status, message });
|
||||
|
@ -780,6 +784,10 @@ const RocketChat = {
|
|||
// RC 0.48.0
|
||||
return this.sdk.get('users.info', { userId });
|
||||
},
|
||||
getUserPreferences(userId) {
|
||||
// RC 0.62.0
|
||||
return this.sdk.get('users.getPreferences', { userId });
|
||||
},
|
||||
getRoomInfo(roomId) {
|
||||
// RC 0.72.0
|
||||
return this.sdk.get('rooms.info', { roomId });
|
||||
|
|
|
@ -41,6 +41,8 @@ import LanguageView from '../views/LanguageView';
|
|||
import ThemeView from '../views/ThemeView';
|
||||
import DefaultBrowserView from '../views/DefaultBrowserView';
|
||||
import ScreenLockConfigView from '../views/ScreenLockConfigView';
|
||||
import PreferencesView from '../views/PreferencesView';
|
||||
import UserNotificationPrefView from '../views/UserNotificationPreferencesView';
|
||||
|
||||
// Admin Stack
|
||||
import AdminPanelView from '../views/AdminPanelView';
|
||||
|
@ -220,6 +222,21 @@ const SettingsStackNavigator = () => {
|
|||
component={ScreenLockConfigView}
|
||||
options={ScreenLockConfigView.navigationOptions}
|
||||
/>
|
||||
<SettingsStack.Screen
|
||||
name='PreferencesView'
|
||||
component={PreferencesView}
|
||||
options={PreferencesView.navigationOptions}
|
||||
/>
|
||||
<SettingsStack.Screen
|
||||
name='UserNotificationPrefView'
|
||||
component={UserNotificationPrefView}
|
||||
options={UserNotificationPrefView.navigationOptions}
|
||||
/>
|
||||
<SettingsStack.Screen
|
||||
name='PickerView'
|
||||
component={PickerView}
|
||||
options={PickerView.navigationOptions}
|
||||
/>
|
||||
</SettingsStack.Navigator>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -42,6 +42,8 @@ import AdminPanelView from '../../views/AdminPanelView';
|
|||
import NewMessageView from '../../views/NewMessageView';
|
||||
import CreateChannelView from '../../views/CreateChannelView';
|
||||
import QueueListView from '../../views/QueueListView';
|
||||
import PreferencesView from '../../views/PreferencesView';
|
||||
import UserNotificationPrefView from '../../views/UserNotificationPreferencesView';
|
||||
|
||||
// InsideStackNavigator
|
||||
import AttachmentView from '../../views/AttachmentView';
|
||||
|
@ -253,6 +255,16 @@ const ModalStackNavigator = React.memo(({ navigation }) => {
|
|||
name='CreateDiscussionView'
|
||||
component={CreateDiscussionView}
|
||||
/>
|
||||
<ModalStack.Screen
|
||||
name='PreferencesView'
|
||||
component={PreferencesView}
|
||||
options={PreferencesView.navigationOptions}
|
||||
/>
|
||||
<ModalStack.Screen
|
||||
name='UserNotificationPrefView'
|
||||
component={UserNotificationPrefView}
|
||||
options={UserNotificationPrefView.navigationOptions}
|
||||
/>
|
||||
</ModalStack.Navigator>
|
||||
</ModalContainer>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
Text
|
||||
} from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import styles from './styles';
|
||||
import { themes } from '../../constants/colors';
|
||||
|
||||
const Info = React.memo(({ info, theme }) => (
|
||||
<Text
|
||||
style={[
|
||||
styles.infoText,
|
||||
{
|
||||
color: themes[theme].infoText,
|
||||
backgroundColor: themes[theme].auxiliaryBackground
|
||||
}
|
||||
]}
|
||||
>
|
||||
{info}
|
||||
</Text>
|
||||
));
|
||||
|
||||
Info.propTypes = {
|
||||
info: PropTypes.string,
|
||||
theme: PropTypes.string
|
||||
};
|
||||
|
||||
export default Info;
|
|
@ -0,0 +1,23 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
View
|
||||
} from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import styles from './styles';
|
||||
import { themes } from '../../constants/colors';
|
||||
|
||||
const SectionSeparator = React.memo(({ theme }) => (
|
||||
<View
|
||||
style={[
|
||||
styles.sectionSeparatorBorder,
|
||||
{ backgroundColor: themes[theme].auxiliaryBackground }
|
||||
]}
|
||||
/>
|
||||
));
|
||||
|
||||
SectionSeparator.propTypes = {
|
||||
theme: PropTypes.string
|
||||
};
|
||||
|
||||
export default SectionSeparator;
|
|
@ -0,0 +1,29 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
Text
|
||||
} from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import styles from './styles';
|
||||
import { themes } from '../../constants/colors';
|
||||
|
||||
const SectionTitle = React.memo(({ title, theme }) => (
|
||||
<Text
|
||||
style={[
|
||||
styles.sectionTitle,
|
||||
{
|
||||
backgroundColor: themes[theme].auxiliaryBackground,
|
||||
color: themes[theme].infoText
|
||||
}
|
||||
]}
|
||||
>
|
||||
{title}
|
||||
</Text>
|
||||
));
|
||||
|
||||
SectionTitle.propTypes = {
|
||||
title: PropTypes.string,
|
||||
theme: PropTypes.string
|
||||
};
|
||||
|
||||
export default SectionTitle;
|
|
@ -17,126 +17,10 @@ import { withTheme } from '../../theme';
|
|||
import protectedFunction from '../../lib/methods/helpers/protectedFunction';
|
||||
import SafeAreaView from '../../containers/SafeAreaView';
|
||||
import log, { events, logEvent } from '../../utils/log';
|
||||
|
||||
const SectionTitle = React.memo(({ title, theme }) => (
|
||||
<Text
|
||||
style={[
|
||||
styles.sectionTitle,
|
||||
{
|
||||
backgroundColor: themes[theme].auxiliaryBackground,
|
||||
color: themes[theme].infoText
|
||||
}
|
||||
]}
|
||||
>
|
||||
{title}
|
||||
</Text>
|
||||
));
|
||||
|
||||
const SectionSeparator = React.memo(({ theme }) => (
|
||||
<View
|
||||
style={[
|
||||
styles.sectionSeparatorBorder,
|
||||
{ backgroundColor: themes[theme].auxiliaryBackground }
|
||||
]}
|
||||
/>
|
||||
));
|
||||
|
||||
const Info = React.memo(({ info, theme }) => (
|
||||
<Text
|
||||
style={[
|
||||
styles.infoText,
|
||||
{
|
||||
color: themes[theme].infoText,
|
||||
backgroundColor: themes[theme].auxiliaryBackground
|
||||
}
|
||||
]}
|
||||
>
|
||||
{info}
|
||||
</Text>
|
||||
));
|
||||
|
||||
SectionTitle.propTypes = {
|
||||
title: PropTypes.string,
|
||||
theme: PropTypes.string
|
||||
};
|
||||
|
||||
SectionSeparator.propTypes = {
|
||||
theme: PropTypes.string
|
||||
};
|
||||
|
||||
Info.propTypes = {
|
||||
info: PropTypes.string,
|
||||
theme: PropTypes.string
|
||||
};
|
||||
|
||||
const OPTIONS = {
|
||||
desktopNotifications: [{
|
||||
label: 'Default', value: 'default'
|
||||
}, {
|
||||
label: 'All_Messages', value: 'all'
|
||||
}, {
|
||||
label: 'Mentions', value: 'mentions'
|
||||
}, {
|
||||
label: 'Nothing', value: 'nothing'
|
||||
}],
|
||||
audioNotifications: [{
|
||||
label: 'Default', value: 'default'
|
||||
}, {
|
||||
label: 'All_Messages', value: 'all'
|
||||
}, {
|
||||
label: 'Mentions', value: 'mentions'
|
||||
}, {
|
||||
label: 'Nothing', value: 'nothing'
|
||||
}],
|
||||
mobilePushNotifications: [{
|
||||
label: 'Default', value: 'default'
|
||||
}, {
|
||||
label: 'All_Messages', value: 'all'
|
||||
}, {
|
||||
label: 'Mentions', value: 'mentions'
|
||||
}, {
|
||||
label: 'Nothing', value: 'nothing'
|
||||
}],
|
||||
emailNotifications: [{
|
||||
label: 'Default', value: 'default'
|
||||
}, {
|
||||
label: 'All_Messages', value: 'all'
|
||||
}, {
|
||||
label: 'Mentions', value: 'mentions'
|
||||
}, {
|
||||
label: 'Nothing', value: 'nothing'
|
||||
}],
|
||||
desktopNotificationDuration: [{
|
||||
label: 'Default', value: 0
|
||||
}, {
|
||||
label: 'Seconds', second: 1, value: 1
|
||||
}, {
|
||||
label: 'Seconds', second: 2, value: 2
|
||||
}, {
|
||||
label: 'Seconds', second: 3, value: 3
|
||||
}, {
|
||||
label: 'Seconds', second: 4, value: 4
|
||||
}, {
|
||||
label: 'Seconds', second: 5, value: 5
|
||||
}],
|
||||
audioNotificationValue: [{
|
||||
label: 'None', value: 'none None'
|
||||
}, {
|
||||
label: 'Default', value: '0 Default'
|
||||
}, {
|
||||
label: 'Beep', value: 'beep Beep'
|
||||
}, {
|
||||
label: 'Ding', value: 'ding Ding'
|
||||
}, {
|
||||
label: 'Chelle', value: 'chelle Chelle'
|
||||
}, {
|
||||
label: 'Droplet', value: 'droplet Droplet'
|
||||
}, {
|
||||
label: 'Highbell', value: 'highbell Highbell'
|
||||
}, {
|
||||
label: 'Seasons', value: 'seasons Seasons'
|
||||
}]
|
||||
};
|
||||
import SectionTitle from './SectionTitle';
|
||||
import SectionSeparator from './SectionSeparator';
|
||||
import Info from './Info';
|
||||
import { OPTIONS } from './options';
|
||||
|
||||
class NotificationPreferencesView extends React.Component {
|
||||
static navigationOptions = () => ({
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
export const OPTIONS = {
|
||||
desktopNotifications: [{
|
||||
label: 'Default', value: 'default'
|
||||
}, {
|
||||
label: 'All_Messages', value: 'all'
|
||||
}, {
|
||||
label: 'Mentions', value: 'mentions'
|
||||
}, {
|
||||
label: 'Nothing', value: 'nothing'
|
||||
}],
|
||||
audioNotifications: [{
|
||||
label: 'Default', value: 'default'
|
||||
}, {
|
||||
label: 'All_Messages', value: 'all'
|
||||
}, {
|
||||
label: 'Mentions', value: 'mentions'
|
||||
}, {
|
||||
label: 'Nothing', value: 'nothing'
|
||||
}],
|
||||
mobilePushNotifications: [{
|
||||
label: 'Default', value: 'default'
|
||||
}, {
|
||||
label: 'All_Messages', value: 'all'
|
||||
}, {
|
||||
label: 'Mentions', value: 'mentions'
|
||||
}, {
|
||||
label: 'Nothing', value: 'nothing'
|
||||
}],
|
||||
emailNotifications: [{
|
||||
label: 'Default', value: 'default'
|
||||
}, {
|
||||
label: 'All_Messages', value: 'all'
|
||||
}, {
|
||||
label: 'Mentions', value: 'mentions'
|
||||
}, {
|
||||
label: 'Nothing', value: 'nothing'
|
||||
}],
|
||||
desktopNotificationDuration: [{
|
||||
label: 'Default', value: 0
|
||||
}, {
|
||||
label: 'Seconds', second: 1, value: 1
|
||||
}, {
|
||||
label: 'Seconds', second: 2, value: 2
|
||||
}, {
|
||||
label: 'Seconds', second: 3, value: 3
|
||||
}, {
|
||||
label: 'Seconds', second: 4, value: 4
|
||||
}, {
|
||||
label: 'Seconds', second: 5, value: 5
|
||||
}],
|
||||
audioNotificationValue: [{
|
||||
label: 'None', value: 'none None'
|
||||
}, {
|
||||
label: 'Default', value: '0 Default'
|
||||
}, {
|
||||
label: 'Beep', value: 'beep Beep'
|
||||
}, {
|
||||
label: 'Ding', value: 'ding Ding'
|
||||
}, {
|
||||
label: 'Chelle', value: 'chelle Chelle'
|
||||
}, {
|
||||
label: 'Droplet', value: 'droplet Droplet'
|
||||
}, {
|
||||
label: 'Highbell', value: 'highbell Highbell'
|
||||
}, {
|
||||
label: 'Seasons', value: 'seasons Seasons'
|
||||
}]
|
||||
};
|
|
@ -0,0 +1,65 @@
|
|||
import React from 'react';
|
||||
import { ScrollView } from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import I18n from '../../i18n';
|
||||
import {
|
||||
logEvent, events
|
||||
} from '../../utils/log';
|
||||
import scrollPersistTaps from '../../utils/scrollPersistTaps';
|
||||
import Separator from '../../containers/Separator';
|
||||
import SafeAreaView from '../../containers/SafeAreaView';
|
||||
import StatusBar from '../../containers/StatusBar';
|
||||
import ListItem from '../../containers/ListItem';
|
||||
import { DisclosureImage } from '../../containers/DisclosureIndicator';
|
||||
import { withTheme } from '../../theme';
|
||||
|
||||
class PreferencesView extends React.Component {
|
||||
static navigationOptions = () => ({
|
||||
title: I18n.t('Preferences')
|
||||
});
|
||||
|
||||
static propTypes = {
|
||||
navigation: PropTypes.object,
|
||||
theme: PropTypes.string
|
||||
}
|
||||
|
||||
renderDisclosure = () => {
|
||||
const { theme } = this.props;
|
||||
return <DisclosureImage theme={theme} />;
|
||||
}
|
||||
|
||||
navigateToScreen = (screen, params) => {
|
||||
logEvent(events[`SE_GO_${ screen.replace('View', '').toUpperCase() }`]);
|
||||
const { navigation } = this.props;
|
||||
navigation.navigate(screen, params);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { theme } = this.props;
|
||||
|
||||
return (
|
||||
<SafeAreaView testID='preferences-view' theme={theme}>
|
||||
<StatusBar theme={theme} />
|
||||
<ScrollView
|
||||
{...scrollPersistTaps}
|
||||
contentContainerStyle={{ paddingVertical: 36 }}
|
||||
showsVerticalScrollIndicator={false}
|
||||
testID='preferences-view-list'
|
||||
>
|
||||
<ListItem
|
||||
title={I18n.t('Notifications')}
|
||||
onPress={() => this.navigateToScreen('UserNotificationPrefView')}
|
||||
showActionIndicator
|
||||
testID='preferences-view-notifications'
|
||||
right={this.renderDisclosure}
|
||||
theme={theme}
|
||||
/>
|
||||
<Separator theme={theme} />
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTheme(PreferencesView);
|
|
@ -24,7 +24,7 @@ import Button from '../../containers/Button';
|
|||
import Avatar from '../../containers/Avatar';
|
||||
import { setUser as setUserAction } from '../../actions/login';
|
||||
import { CustomIcon } from '../../lib/Icons';
|
||||
import { DrawerButton } from '../../containers/HeaderButton';
|
||||
import { DrawerButton, PreferencesButton } from '../../containers/HeaderButton';
|
||||
import StatusBar from '../../containers/StatusBar';
|
||||
import { themes } from '../../constants/colors';
|
||||
import { withTheme } from '../../theme';
|
||||
|
@ -39,6 +39,9 @@ class ProfileView extends React.Component {
|
|||
if (!isMasterDetail) {
|
||||
options.headerLeft = () => <DrawerButton navigation={navigation} />;
|
||||
}
|
||||
options.headerRight = () => (
|
||||
<PreferencesButton onPress={() => navigation.navigate('PreferencesView')} testID='preferences-view-open' />
|
||||
);
|
||||
return options;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
Text
|
||||
} from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import styles from './styles';
|
||||
import { themes } from '../../constants/colors';
|
||||
|
||||
const Info = React.memo(({ info, theme }) => (
|
||||
<Text
|
||||
style={[
|
||||
styles.infoText,
|
||||
{
|
||||
color: themes[theme].infoText,
|
||||
backgroundColor: themes[theme].auxiliaryBackground
|
||||
}
|
||||
]}
|
||||
>
|
||||
{info}
|
||||
</Text>
|
||||
));
|
||||
|
||||
Info.propTypes = {
|
||||
info: PropTypes.string,
|
||||
theme: PropTypes.string
|
||||
};
|
||||
|
||||
export default Info;
|
|
@ -0,0 +1,23 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
View
|
||||
} from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import styles from './styles';
|
||||
import { themes } from '../../constants/colors';
|
||||
|
||||
const SectionSeparator = React.memo(({ theme }) => (
|
||||
<View
|
||||
style={[
|
||||
styles.sectionSeparatorBorder,
|
||||
{ backgroundColor: themes[theme].auxiliaryBackground }
|
||||
]}
|
||||
/>
|
||||
));
|
||||
|
||||
SectionSeparator.propTypes = {
|
||||
theme: PropTypes.string
|
||||
};
|
||||
|
||||
export default SectionSeparator;
|
|
@ -0,0 +1,29 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
Text
|
||||
} from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import styles from './styles';
|
||||
import { themes } from '../../constants/colors';
|
||||
|
||||
const SectionTitle = React.memo(({ title, theme }) => (
|
||||
<Text
|
||||
style={[
|
||||
styles.sectionTitle,
|
||||
{
|
||||
backgroundColor: themes[theme].auxiliaryBackground,
|
||||
color: themes[theme].infoText
|
||||
}
|
||||
]}
|
||||
>
|
||||
{title}
|
||||
</Text>
|
||||
));
|
||||
|
||||
SectionTitle.propTypes = {
|
||||
title: PropTypes.string,
|
||||
theme: PropTypes.string
|
||||
};
|
||||
|
||||
export default SectionTitle;
|
|
@ -0,0 +1,168 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
View, ScrollView, Text
|
||||
} from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { themes } from '../../constants/colors';
|
||||
import StatusBar from '../../containers/StatusBar';
|
||||
import ListItem from '../../containers/ListItem';
|
||||
import Separator from '../../containers/Separator';
|
||||
import I18n from '../../i18n';
|
||||
import scrollPersistTaps from '../../utils/scrollPersistTaps';
|
||||
import styles from './styles';
|
||||
import RocketChat from '../../lib/rocketchat';
|
||||
import { withTheme } from '../../theme';
|
||||
import SafeAreaView from '../../containers/SafeAreaView';
|
||||
import SectionTitle from './SectionTitle';
|
||||
import SectionSeparator from './SectionSeparator';
|
||||
import Info from './Info';
|
||||
import { OPTIONS } from './options';
|
||||
import ActivityIndicator from '../../containers/ActivityIndicator';
|
||||
import { DisclosureImage } from '../../containers/DisclosureIndicator';
|
||||
import { getUserSelector } from '../../selectors/login';
|
||||
|
||||
class UserNotificationPreferencesView extends React.Component {
|
||||
static navigationOptions = () => ({
|
||||
title: I18n.t('Notification_Preferences')
|
||||
})
|
||||
|
||||
static propTypes = {
|
||||
navigation: PropTypes.object,
|
||||
route: PropTypes.object,
|
||||
theme: PropTypes.string,
|
||||
user: PropTypes.shape({
|
||||
id: PropTypes.string
|
||||
})
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
preferences: {},
|
||||
loading: false
|
||||
};
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
const { user } = this.props;
|
||||
const { id } = user;
|
||||
const result = await RocketChat.getUserPreferences(id);
|
||||
const { preferences } = result;
|
||||
this.setState({ preferences, loading: true });
|
||||
}
|
||||
|
||||
findOption = (key) => {
|
||||
const { preferences } = this.state;
|
||||
const option = preferences[key] ? OPTIONS[key].find(item => item.value === preferences[key]) : OPTIONS[key][0];
|
||||
return option;
|
||||
}
|
||||
|
||||
renderPickerOption = (key) => {
|
||||
const { theme } = this.props;
|
||||
const text = this.findOption(key);
|
||||
return <Text style={[styles.pickerText, { color: themes[theme].actionTintColor }]}>{I18n.t(text?.label, { defaultValue: text?.label, second: text?.second })}</Text>;
|
||||
}
|
||||
|
||||
pickerSelection = (title, key) => {
|
||||
const { preferences } = this.state;
|
||||
const { navigation } = this.props;
|
||||
let values = OPTIONS[key];
|
||||
if (OPTIONS[key][0]?.value !== 'default') {
|
||||
values = [{ label: `${ I18n.t('Default') } (${ I18n.t(this.findOption(key).label) })`, value: preferences[key]?.value }, ...OPTIONS[key]];
|
||||
}
|
||||
navigation.navigate('PickerView', {
|
||||
title,
|
||||
data: values,
|
||||
value: preferences[key],
|
||||
onChangeValue: value => this.onValueChangePicker(key, value)
|
||||
});
|
||||
}
|
||||
|
||||
onValueChangePicker = (key, value) => this.saveNotificationPreferences({ [key]: value.toString() });
|
||||
|
||||
saveNotificationPreferences = async(params) => {
|
||||
const { user } = this.props;
|
||||
const { id } = user;
|
||||
const result = await RocketChat.setUserPreferences(id, params);
|
||||
const { user: { settings } } = result;
|
||||
this.setState({ preferences: settings.preferences });
|
||||
}
|
||||
|
||||
renderDisclosure = () => {
|
||||
const { theme } = this.props;
|
||||
return <DisclosureImage theme={theme} />;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { theme } = this.props;
|
||||
const { loading } = this.state;
|
||||
return (
|
||||
<SafeAreaView testID='user-notification-preference-view' theme={theme}>
|
||||
<StatusBar theme={theme} />
|
||||
<ScrollView
|
||||
{...scrollPersistTaps}
|
||||
style={{ backgroundColor: themes[theme].auxiliaryBackground }}
|
||||
contentContainerStyle={styles.contentContainer}
|
||||
testID='user-notification-preference-view-list'
|
||||
>
|
||||
{loading
|
||||
? (
|
||||
<>
|
||||
<SectionSeparator theme={theme} />
|
||||
<SectionTitle title={I18n.t('DESKTOP_NOTIFICATIONS')} theme={theme} />
|
||||
|
||||
<ListItem
|
||||
title={I18n.t('Alert')}
|
||||
testID='user-notification-preference-view-alert'
|
||||
onPress={title => this.pickerSelection(title, 'desktopNotifications')}
|
||||
right={() => this.renderPickerOption('desktopNotifications')}
|
||||
theme={theme}
|
||||
/>
|
||||
<Separator theme={theme} />
|
||||
<Info info={I18n.t('Desktop_Alert_info')} theme={theme} />
|
||||
|
||||
<SectionSeparator theme={theme} />
|
||||
<SectionTitle title={I18n.t('PUSH_NOTIFICATIONS')} theme={theme} />
|
||||
<Separator theme={theme} />
|
||||
|
||||
<ListItem
|
||||
title={I18n.t('Alert')}
|
||||
testID='user-notification-preference-view-push-notification'
|
||||
onPress={title => this.pickerSelection(title, 'mobileNotifications')}
|
||||
right={() => this.renderPickerOption('mobileNotifications')}
|
||||
theme={theme}
|
||||
/>
|
||||
<Separator theme={theme} />
|
||||
<Info info={I18n.t('Push_Notifications_Alert_Info')} theme={theme} />
|
||||
|
||||
<SectionSeparator theme={theme} />
|
||||
<SectionTitle title={I18n.t('EMAIL')} theme={theme} />
|
||||
<Separator theme={theme} />
|
||||
|
||||
<ListItem
|
||||
title={I18n.t('Alert')}
|
||||
testID='user-notification-preference-view-email-alert'
|
||||
onPress={title => this.pickerSelection(title, 'emailNotificationMode')}
|
||||
right={() => this.renderPickerOption('emailNotificationMode')}
|
||||
theme={theme}
|
||||
/>
|
||||
|
||||
<Separator theme={theme} />
|
||||
<Info info={I18n.t('You_need_to_verifiy_your_email_address_to_get_notications')} theme={theme} />
|
||||
</>
|
||||
) : <ActivityIndicator theme={theme} />
|
||||
}
|
||||
<View style={[styles.marginBottom, { backgroundColor: themes[theme].auxiliaryBackground }]} />
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
user: getUserSelector(state)
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(withTheme(UserNotificationPreferencesView));
|
|
@ -0,0 +1,19 @@
|
|||
const commonOptions = [{
|
||||
label: 'Default', value: 'default'
|
||||
}, {
|
||||
label: 'All_Messages', value: 'all'
|
||||
}, {
|
||||
label: 'Mentions', value: 'mentions'
|
||||
}, {
|
||||
label: 'Nothing', value: 'nothing'
|
||||
}];
|
||||
|
||||
export const OPTIONS = {
|
||||
desktopNotifications: commonOptions,
|
||||
mobileNotifications: commonOptions,
|
||||
emailNotificationMode: [{
|
||||
label: 'Email_Notification_Mode_All', value: 'mentions'
|
||||
}, {
|
||||
label: 'Email_Notification_Mode_Disabled', value: 'nothing'
|
||||
}]
|
||||
};
|
|
@ -0,0 +1,31 @@
|
|||
import { StyleSheet } from 'react-native';
|
||||
|
||||
import sharedStyles from '../Styles';
|
||||
|
||||
export default StyleSheet.create({
|
||||
sectionSeparatorBorder: {
|
||||
height: 10
|
||||
},
|
||||
marginBottom: {
|
||||
height: 30
|
||||
},
|
||||
contentContainer: {
|
||||
marginVertical: 10
|
||||
},
|
||||
infoText: {
|
||||
...sharedStyles.textRegular,
|
||||
fontSize: 13,
|
||||
paddingHorizontal: 15,
|
||||
paddingVertical: 10
|
||||
},
|
||||
sectionTitle: {
|
||||
...sharedStyles.separatorBottom,
|
||||
paddingHorizontal: 15,
|
||||
paddingVertical: 10,
|
||||
fontSize: 14
|
||||
},
|
||||
pickerText: {
|
||||
...sharedStyles.textRegular,
|
||||
fontSize: 16
|
||||
}
|
||||
});
|
Loading…
Reference in New Issue