[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>
|
</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 }) => (
|
export const LegalButton = React.memo(({ navigation, testID }) => (
|
||||||
<MoreButton onPress={() => navigation.navigate('LegalView')} testID={testID} />
|
<MoreButton onPress={() => navigation.navigate('LegalView')} testID={testID} />
|
||||||
));
|
));
|
||||||
|
@ -89,6 +95,10 @@ SaveButton.propTypes = {
|
||||||
onPress: PropTypes.func.isRequired,
|
onPress: PropTypes.func.isRequired,
|
||||||
testID: PropTypes.string.isRequired
|
testID: PropTypes.string.isRequired
|
||||||
};
|
};
|
||||||
|
PreferencesButton.propTypes = {
|
||||||
|
onPress: PropTypes.func.isRequired,
|
||||||
|
testID: PropTypes.string.isRequired
|
||||||
|
};
|
||||||
LegalButton.propTypes = {
|
LegalButton.propTypes = {
|
||||||
navigation: PropTypes.object.isRequired,
|
navigation: PropTypes.object.isRequired,
|
||||||
testID: PropTypes.string.isRequired
|
testID: PropTypes.string.isRequired
|
||||||
|
|
|
@ -181,6 +181,8 @@ export default {
|
||||||
description: 'description',
|
description: 'description',
|
||||||
Description: 'Description',
|
Description: 'Description',
|
||||||
DESKTOP_OPTIONS: 'DESKTOP OPTIONS',
|
DESKTOP_OPTIONS: 'DESKTOP OPTIONS',
|
||||||
|
DESKTOP_NOTIFICATIONS: 'DESKTOP NOTIFICATIONS',
|
||||||
|
Desktop_Alert_info: 'These notifications are delivered in desktop',
|
||||||
Directory: 'Directory',
|
Directory: 'Directory',
|
||||||
Direct_Messages: 'Direct Messages',
|
Direct_Messages: 'Direct Messages',
|
||||||
Disable_notifications: 'Disable notifications',
|
Disable_notifications: 'Disable notifications',
|
||||||
|
@ -197,6 +199,8 @@ export default {
|
||||||
Edit: 'Edit',
|
Edit: 'Edit',
|
||||||
Edit_Status: 'Edit Status',
|
Edit_Status: 'Edit Status',
|
||||||
Edit_Invite: 'Edit Invite',
|
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_or_password_field_is_empty: 'Email or password field is empty',
|
||||||
Email: 'Email',
|
Email: 'Email',
|
||||||
EMAIL: 'EMAIL',
|
EMAIL: 'EMAIL',
|
||||||
|
@ -567,6 +571,7 @@ export default {
|
||||||
You: 'You',
|
You: 'You',
|
||||||
Logged_out_by_server: 'You\'ve been logged out by the server. Please log in again.',
|
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_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_certificate: 'Your Certificate',
|
||||||
Your_message: 'Your message',
|
Your_message: 'Your message',
|
||||||
Your_invite_link_will_expire_after__usesLeft__uses: 'Your invite link will expire after {{usesLeft}} uses.',
|
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',
|
deleting_room: 'excluindo sala',
|
||||||
Direct_Messages: 'Mensagens Diretas',
|
Direct_Messages: 'Mensagens Diretas',
|
||||||
DESKTOP_OPTIONS: 'OPÇÕES DE ÁREA DE TRABALHO',
|
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',
|
Directory: 'Diretório',
|
||||||
description: 'descrição',
|
description: 'descrição',
|
||||||
Description: 'Descrição',
|
Description: 'Descrição',
|
||||||
|
@ -195,6 +197,8 @@ export default {
|
||||||
Email: 'Email',
|
Email: 'Email',
|
||||||
email: 'e-mail',
|
email: 'e-mail',
|
||||||
Empty_title: 'Título vazio',
|
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_Auto_Translate: 'Ativar a tradução automática',
|
||||||
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',
|
||||||
|
@ -508,6 +512,7 @@ export default {
|
||||||
You_are_offline: 'Você está offline',
|
You_are_offline: 'Você está offline',
|
||||||
You_can_search_using_RegExp_eg: 'Você pode usar expressões regulares, por exemplo `/^text$/i`',
|
You_can_search_using_RegExp_eg: 'Você pode usar expressões regulares, por exemplo `/^text$/i`',
|
||||||
Your_message: 'Sua mensagem',
|
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_colon: 'Você: ',
|
||||||
you_were_mentioned: 'você foi mencionado',
|
you_were_mentioned: 'você foi mencionado',
|
||||||
You_were_removed_from_channel: 'Você foi removido de {{channel}}',
|
You_were_removed_from_channel: 'Você foi removido de {{channel}}',
|
||||||
|
|
|
@ -726,6 +726,10 @@ const RocketChat = {
|
||||||
setUserPresenceOnline() {
|
setUserPresenceOnline() {
|
||||||
return this.methodCall('UserPresence:online');
|
return this.methodCall('UserPresence:online');
|
||||||
},
|
},
|
||||||
|
setUserPreferences(userId, data) {
|
||||||
|
// RC 0.62.0
|
||||||
|
return this.sdk.post('users.setPreferences', { userId, data });
|
||||||
|
},
|
||||||
setUserStatus(status, message) {
|
setUserStatus(status, message) {
|
||||||
// RC 1.2.0
|
// RC 1.2.0
|
||||||
return this.post('users.setStatus', { status, message });
|
return this.post('users.setStatus', { status, message });
|
||||||
|
@ -780,6 +784,10 @@ const RocketChat = {
|
||||||
// RC 0.48.0
|
// RC 0.48.0
|
||||||
return this.sdk.get('users.info', { userId });
|
return this.sdk.get('users.info', { userId });
|
||||||
},
|
},
|
||||||
|
getUserPreferences(userId) {
|
||||||
|
// RC 0.62.0
|
||||||
|
return this.sdk.get('users.getPreferences', { userId });
|
||||||
|
},
|
||||||
getRoomInfo(roomId) {
|
getRoomInfo(roomId) {
|
||||||
// RC 0.72.0
|
// RC 0.72.0
|
||||||
return this.sdk.get('rooms.info', { roomId });
|
return this.sdk.get('rooms.info', { roomId });
|
||||||
|
|
|
@ -41,6 +41,8 @@ import LanguageView from '../views/LanguageView';
|
||||||
import ThemeView from '../views/ThemeView';
|
import ThemeView from '../views/ThemeView';
|
||||||
import DefaultBrowserView from '../views/DefaultBrowserView';
|
import DefaultBrowserView from '../views/DefaultBrowserView';
|
||||||
import ScreenLockConfigView from '../views/ScreenLockConfigView';
|
import ScreenLockConfigView from '../views/ScreenLockConfigView';
|
||||||
|
import PreferencesView from '../views/PreferencesView';
|
||||||
|
import UserNotificationPrefView from '../views/UserNotificationPreferencesView';
|
||||||
|
|
||||||
// Admin Stack
|
// Admin Stack
|
||||||
import AdminPanelView from '../views/AdminPanelView';
|
import AdminPanelView from '../views/AdminPanelView';
|
||||||
|
@ -220,6 +222,21 @@ const SettingsStackNavigator = () => {
|
||||||
component={ScreenLockConfigView}
|
component={ScreenLockConfigView}
|
||||||
options={ScreenLockConfigView.navigationOptions}
|
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>
|
</SettingsStack.Navigator>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -42,6 +42,8 @@ import AdminPanelView from '../../views/AdminPanelView';
|
||||||
import NewMessageView from '../../views/NewMessageView';
|
import NewMessageView from '../../views/NewMessageView';
|
||||||
import CreateChannelView from '../../views/CreateChannelView';
|
import CreateChannelView from '../../views/CreateChannelView';
|
||||||
import QueueListView from '../../views/QueueListView';
|
import QueueListView from '../../views/QueueListView';
|
||||||
|
import PreferencesView from '../../views/PreferencesView';
|
||||||
|
import UserNotificationPrefView from '../../views/UserNotificationPreferencesView';
|
||||||
|
|
||||||
// InsideStackNavigator
|
// InsideStackNavigator
|
||||||
import AttachmentView from '../../views/AttachmentView';
|
import AttachmentView from '../../views/AttachmentView';
|
||||||
|
@ -253,6 +255,16 @@ const ModalStackNavigator = React.memo(({ navigation }) => {
|
||||||
name='CreateDiscussionView'
|
name='CreateDiscussionView'
|
||||||
component={CreateDiscussionView}
|
component={CreateDiscussionView}
|
||||||
/>
|
/>
|
||||||
|
<ModalStack.Screen
|
||||||
|
name='PreferencesView'
|
||||||
|
component={PreferencesView}
|
||||||
|
options={PreferencesView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ModalStack.Screen
|
||||||
|
name='UserNotificationPrefView'
|
||||||
|
component={UserNotificationPrefView}
|
||||||
|
options={UserNotificationPrefView.navigationOptions}
|
||||||
|
/>
|
||||||
</ModalStack.Navigator>
|
</ModalStack.Navigator>
|
||||||
</ModalContainer>
|
</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 protectedFunction from '../../lib/methods/helpers/protectedFunction';
|
||||||
import SafeAreaView from '../../containers/SafeAreaView';
|
import SafeAreaView from '../../containers/SafeAreaView';
|
||||||
import log, { events, logEvent } from '../../utils/log';
|
import log, { events, logEvent } from '../../utils/log';
|
||||||
|
import SectionTitle from './SectionTitle';
|
||||||
const SectionTitle = React.memo(({ title, theme }) => (
|
import SectionSeparator from './SectionSeparator';
|
||||||
<Text
|
import Info from './Info';
|
||||||
style={[
|
import { OPTIONS } from './options';
|
||||||
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'
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
|
|
||||||
class NotificationPreferencesView extends React.Component {
|
class NotificationPreferencesView extends React.Component {
|
||||||
static navigationOptions = () => ({
|
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 Avatar from '../../containers/Avatar';
|
||||||
import { setUser as setUserAction } from '../../actions/login';
|
import { setUser as setUserAction } from '../../actions/login';
|
||||||
import { CustomIcon } from '../../lib/Icons';
|
import { CustomIcon } from '../../lib/Icons';
|
||||||
import { DrawerButton } from '../../containers/HeaderButton';
|
import { DrawerButton, PreferencesButton } from '../../containers/HeaderButton';
|
||||||
import StatusBar from '../../containers/StatusBar';
|
import StatusBar from '../../containers/StatusBar';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
|
@ -39,6 +39,9 @@ class ProfileView extends React.Component {
|
||||||
if (!isMasterDetail) {
|
if (!isMasterDetail) {
|
||||||
options.headerLeft = () => <DrawerButton navigation={navigation} />;
|
options.headerLeft = () => <DrawerButton navigation={navigation} />;
|
||||||
}
|
}
|
||||||
|
options.headerRight = () => (
|
||||||
|
<PreferencesButton onPress={() => navigation.navigate('PreferencesView')} testID='preferences-view-open' />
|
||||||
|
);
|
||||||
return options;
|
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