RN-138 Android notification preferences (#889)

* Fix Android and iOS flow

* Own review

* RN-138 Android notification preferences

* Feedback review

* Feedback review 2
This commit is contained in:
enahum
2017-09-13 12:00:16 -03:00
committed by GitHub
parent 7aebcbd52e
commit e0cfefcdff
17 changed files with 632 additions and 48 deletions

View File

@@ -92,7 +92,7 @@ android {
buildToolsVersion "25.0.1"
defaultConfig {
applicationId "com.mattermost.rn"
applicationId "com.mattermost.rnbeta"
minSdkVersion 16
targetSdkVersion 23
versionCode 49
@@ -152,10 +152,6 @@ android {
}
dependencies {
compile project(':react-native-youtube')
compile project(':react-native-sentry')
compile project(':react-native-exception-handler')
compile project(':react-native-fetch-blob')
compile fileTree(dir: "libs", include: ["*.jar"])
compile "com.android.support:appcompat-v7:25.0.1"
compile "com.facebook.react:react-native:+" // From node_modules
@@ -174,6 +170,10 @@ dependencies {
compile project(':react-native-svg')
compile project(':react-native-local-auth')
compile project(':jail-monkey')
compile project(':react-native-youtube')
compile project(':react-native-sentry')
compile project(':react-native-exception-handler')
compile project(':react-native-fetch-blob')
// For animated GIF support
compile 'com.facebook.fresco:animated-base-support:1.0.1'

View File

@@ -7,6 +7,10 @@ import android.content.res.Resources;
import android.content.pm.ApplicationInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.media.AudioManager;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Build;
import android.app.Notification;
@@ -23,6 +27,8 @@ import com.wix.reactnativenotifications.core.AppLifecycleFacade;
import com.wix.reactnativenotifications.core.JsIOHelper;
import com.wix.reactnativenotifications.helpers.ApplicationBadgeHelper;
import android.util.Log;
import static com.wix.reactnativenotifications.Defs.NOTIFICATION_RECEIVED_EVENT_NAME;
public class CustomPushNotification extends PushNotification {
@@ -99,7 +105,13 @@ public class CustomPushNotification extends PushNotification {
@Override
protected void postNotification(int id, Notification notification) {
if (!mAppLifecycleFacade.isAppVisible()) {
boolean force = false;
Bundle bundle = notification.extras;
if (bundle != null) {
force = bundle.getBoolean("localTest");
}
if (!mAppLifecycleFacade.isAppVisible() || force) {
super.postNotification(id, notification);
}
}
@@ -108,9 +120,10 @@ public class CustomPushNotification extends PushNotification {
protected Notification.Builder getNotificationBuilder(PendingIntent intent) {
final Resources res = mContext.getResources();
String packageName = mContext.getPackageName();
NotificationPreferencesModule notificationPreferences = NotificationPreferencesModule.getInstance();
// First, get a builder initialized with defaults from the core class.
final Notification.Builder notification = super.getNotificationBuilder(intent);
final Notification.Builder notification = new Notification.Builder(mContext);
Bundle bundle = mNotificationProps.asBundle();
String title = bundle.getString("title");
if (title == null) {
@@ -120,13 +133,18 @@ public class CustomPushNotification extends PushNotification {
String channelId = bundle.getString("channel_id");
String postId = bundle.getString("post_id");
int notificationId = channelId.hashCode();
int notificationId = channelId != null ? channelId.hashCode() : MESSAGE_NOTIFICATION_ID;
String message = bundle.getString("message");
String subText = bundle.getString("subText");
String numberString = bundle.getString("badge");
String smallIcon = bundle.getString("smallIcon");
String largeIcon = bundle.getString("largeIcon");
Bundle b = bundle.getBundle("userInfo");
if (b != null) {
notification.addExtras(b);
}
int smallIconResId;
int largeIconResId;
@@ -157,10 +175,12 @@ public class CustomPushNotification extends PushNotification {
int numMessages = getMessageCountInChannel(channelId);
notification
.setContentIntent(intent)
.setGroupSummary(true)
.setSmallIcon(smallIconResId)
.setVisibility(Notification.VISIBILITY_PRIVATE)
.setPriority(Notification.PRIORITY_HIGH);
.setPriority(Notification.PRIORITY_HIGH)
.setAutoCancel(true);
if (numMessages == 1) {
notification
@@ -223,6 +243,27 @@ public class CustomPushNotification extends PushNotification {
notification.setSubText(subText);
}
String soundUri = notificationPreferences.getNotificationSound();
if (soundUri != null) {
if (soundUri != "none") {
notification.setSound(Uri.parse(soundUri), AudioManager.STREAM_NOTIFICATION);
}
} else {
Uri defaultUri = RingtoneManager.getActualDefaultRingtoneUri(mContext, RingtoneManager.TYPE_NOTIFICATION);
notification.setSound(defaultUri, AudioManager.STREAM_NOTIFICATION);
}
boolean vibrate = notificationPreferences.getShouldVibrate();
if (vibrate) {
// Each element then alternates between delay, vibrate, sleep, vibrate, sleep
notification.setVibrate(new long[] {1000, 1000, 500, 1000, 500});
}
boolean blink = notificationPreferences.getShouldBlink();
if (blink) {
notification.setLights(Color.CYAN, 500, 500);
}
return notification;
}
@@ -236,7 +277,7 @@ public class CustomPushNotification extends PushNotification {
return (Integer)objCount;
}
return 0;
return 1;
}
private void cancelNotification(Bundle data, int notificationId) {

View File

@@ -63,7 +63,7 @@ public class MainApplication extends NavigationApplication implements INotificat
new LocalAuthPackage(),
new JailMonkeyPackage(),
new RNFetchBlobPackage(),
new MattermostManagedPackage(),
new MattermostPackage(this),
new RNSentryPackage(this),
new ReactNativeExceptionHandlerPackage(),
new ReactNativeYouTube()

View File

@@ -10,10 +10,19 @@ import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import com.facebook.react.bridge.JavaScriptModule;
public class MattermostManagedPackage implements ReactPackage {
public class MattermostPackage implements ReactPackage {
private final MainApplication mApplication;
public MattermostPackage(MainApplication application) {
mApplication = application;
}
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return Arrays.<NativeModule>asList(MattermostManagedModule.getInstance(reactContext));
return Arrays.<NativeModule>asList(
MattermostManagedModule.getInstance(reactContext),
NotificationPreferencesModule.getInstance(mApplication, reactContext)
);
}
@Override

View File

@@ -0,0 +1,130 @@
package com.mattermost.rnbeta;
import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.os.Bundle;
import android.net.Uri;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
public class NotificationPreferencesModule extends ReactContextBaseJavaModule {
private static NotificationPreferencesModule instance;
private final MainApplication mApplication;
private SharedPreferences mSharedPreferences;
private final String SHARED_NAME = "NotificationPreferences";
private final String SOUND_PREF = "NotificationSound";
private final String VIBRATE_PREF = "NotificationVibrate";
private final String BLINK_PREF = "NotificationLights";
private NotificationPreferencesModule(MainApplication application, ReactApplicationContext reactContext) {
super(reactContext);
mApplication = application;
Context context = mApplication.getApplicationContext();
mSharedPreferences = context.getSharedPreferences(SHARED_NAME, Context.MODE_PRIVATE);
}
public static NotificationPreferencesModule getInstance(MainApplication application, ReactApplicationContext reactContext) {
if (instance == null) {
instance = new NotificationPreferencesModule(application, reactContext);
}
return instance;
}
public static NotificationPreferencesModule getInstance() {
return instance;
}
@Override
public String getName() {
return "NotificationPreferences";
}
@ReactMethod
public void getPreferences(final Promise promise) {
try {
Context context = mApplication.getApplicationContext();
RingtoneManager manager = new RingtoneManager(context);
manager.setType(RingtoneManager.TYPE_NOTIFICATION);
Cursor cursor = manager.getCursor();
WritableMap result = Arguments.createMap();
WritableArray sounds = Arguments.createArray();
while (cursor.moveToNext()) {
String notificationTitle = cursor.getString(RingtoneManager.TITLE_COLUMN_INDEX);
String notificationId = cursor.getString(RingtoneManager.ID_COLUMN_INDEX);
String notificationUri = cursor.getString(RingtoneManager.URI_COLUMN_INDEX);
WritableMap map = Arguments.createMap();
map.putString("name", notificationTitle);
map.putString("uri", (notificationUri + "/" + notificationId));
sounds.pushMap(map);
}
Uri defaultUri = RingtoneManager.getActualDefaultRingtoneUri(context, RingtoneManager.TYPE_NOTIFICATION);
result.putString("defaultUri", Uri.decode(defaultUri.toString()));
result.putString("selectedUri", getNotificationSound());
result.putBoolean("shouldVibrate", getShouldVibrate());
result.putBoolean("shouldBlink", getShouldBlink());
result.putArray("sounds", sounds);
promise.resolve(result);
} catch (Exception e) {
promise.reject("no notification sounds found", e);
}
}
@ReactMethod
public void previewSound(String url) {
Context context = mApplication.getApplicationContext();
Uri uri = Uri.parse(url);
Ringtone r = RingtoneManager.getRingtone(context, uri);
r.play();
}
@ReactMethod
public void setNotificationSound(String soundUri) {
SharedPreferences.Editor editor = mSharedPreferences.edit();
editor.putString(SOUND_PREF, soundUri);
editor.commit();
}
@ReactMethod
public void setShouldVibrate(boolean vibrate) {
SharedPreferences.Editor editor = mSharedPreferences.edit();
editor.putBoolean(VIBRATE_PREF, vibrate);
editor.commit();
}
@ReactMethod
public void setShouldBlink(boolean vibrate) {
SharedPreferences.Editor editor = mSharedPreferences.edit();
editor.putBoolean(BLINK_PREF, vibrate);
editor.commit();
}
public String getNotificationSound() {
return mSharedPreferences.getString(SOUND_PREF, null);
}
public boolean getShouldVibrate() {
return mSharedPreferences.getBoolean(VIBRATE_PREF, true);
}
public boolean getShouldBlink() {
return mSharedPreferences.getBoolean(BLINK_PREF, false);
}
}

View File

@@ -0,0 +1,5 @@
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import NotificationPreferences from './notification_preferences';
export default NotificationPreferences;

View File

@@ -0,0 +1,14 @@
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import {NativeModules} from 'react-native';
const {NotificationPreferences} = NativeModules;
export default {
getPreferences: NotificationPreferences.getPreferences,
setNotificationSound: NotificationPreferences.setNotificationSound,
setShouldVibrate: NotificationPreferences.setShouldVibrate,
setShouldBlink: NotificationPreferences.setShouldBlink,
play: NotificationPreferences.previewSound
};

View File

@@ -0,0 +1,10 @@
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
export default {
getPreferences: async () => null,
setNotificationSound: () => null,
setShouldVibrate: () => null,
setShouldBlink: () => null,
play: () => null
};

View File

@@ -92,6 +92,10 @@ class PushNotification {
}
}
localNotification(notification) {
NotificationsAndroid.localNotification(notification);
}
cancelAllLocalNotifications() {
NotificationsAndroid.cancelAllLocalNotifications();
}

View File

@@ -19,6 +19,7 @@ import {getPreferencesByCategory} from 'mattermost-redux/utils/preference_utils'
import FormattedText from 'app/components/formatted_text';
import {RadioButton, RadioButtonGroup} from 'app/components/radio_button';
import StatusBar from 'app/components/status_bar';
import NotificationPreferences from 'app/notification_preferences';
import SettingsItem from 'app/screens/settings/settings_item';
import {preventDoubleTap} from 'app/utils/tap';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
@@ -110,21 +111,25 @@ class NotificationSettings extends PureComponent {
goToNotificationSettingsMobile = () => {
const {currentUser, intl, navigator, theme} = this.props;
navigator.push({
backButtonTitle: '',
screen: 'NotificationSettingsMobile',
title: intl.formatMessage({id: 'mobile.notification_settings.mobile_title', defaultMessage: 'Mobile Notifications'}),
animated: true,
navigatorStyle: {
navBarTextColor: theme.sidebarHeaderTextColor,
navBarBackgroundColor: theme.sidebarHeaderBg,
navBarButtonColor: theme.sidebarHeaderTextColor,
screenBackgroundColor: theme.centerChannelBg
},
passProps: {
currentUser,
onBack: this.saveNotificationProps
}
NotificationPreferences.getPreferences().then((notificationPreferences) => {
navigator.push({
backButtonTitle: '',
screen: 'NotificationSettingsMobile',
title: intl.formatMessage({id: 'mobile.notification_settings.mobile_title', defaultMessage: 'Mobile Notifications'}),
animated: true,
navigatorStyle: {
navBarTextColor: theme.sidebarHeaderTextColor,
navBarBackgroundColor: theme.sidebarHeaderBg,
navBarButtonColor: theme.sidebarHeaderTextColor,
screenBackgroundColor: theme.centerChannelBg
},
passProps: {
currentUser,
onBack: this.saveNotificationProps,
notificationPreferences
}
});
});
};

View File

@@ -371,8 +371,8 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
},
separator: {
backgroundColor: changeOpacity(theme.centerChannelColor, 0.1),
flex: 1,
height: 1
height: 1,
width: '100%'
},
scrollView: {
flex: 1,

View File

@@ -6,16 +6,18 @@ import {injectIntl} from 'react-intl';
import {
Modal,
ScrollView,
Text,
TouchableOpacity,
View
} from 'react-native';
import FormattedText from 'app/components/formatted_text';
import {RadioButton, RadioButtonGroup} from 'app/components/radio_button';
import NotificationPreferences from 'app/notification_preferences';
import PushNotifications from 'app/push_notifications';
import StatusBar from 'app/components/status_bar';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import SectionItem from 'app/screens/settings/section_item';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import NotificationSettingsMobileBase from './notification_settings_mobile_base';
@@ -30,6 +32,11 @@ class NotificationSettingsMobileAndroid extends NotificationSettingsMobileBase {
this.pushStatus = this.state.push_status;
};
cancelMobileSoundsModal = () => {
this.setState({showMobileSoundsModal: false});
this.sound = this.state.selectedUri;
};
onMobilePushChanged = (value) => {
this.push = value;
};
@@ -38,6 +45,41 @@ class NotificationSettingsMobileAndroid extends NotificationSettingsMobileBase {
this.pushStatus = value;
};
onMobileSoundChanged = (value) => {
this.sound = value;
if (value && value !== 'none') {
NotificationPreferences.play(value);
}
};
renderMobileBlinkSection(style) {
const {config, theme} = this.props;
const {shouldBlink} = this.state;
const showSection = config.SendPushNotifications === 'true' && this.state.push !== 'none';
if (!showSection) {
return null;
}
return (
<View>
<SectionItem
label={(
<FormattedText
id='mobile.notification_settings_mobile.light'
defaultMessage='Light'
/>
)}
action={this.toggleBlink}
actionType='toggle'
selected={shouldBlink}
theme={theme}
/>
<View style={style.separator}/>
</View>
);
}
renderMobilePushModal(style) {
const {config, intl} = this.props;
const pushNotificationsEnabled = config.SendPushNotifications === 'true';
@@ -149,7 +191,7 @@ class NotificationSettingsMobileAndroid extends NotificationSettingsMobileBase {
<View style={style.modalBody}>
<View style={style.modalTitleContainer}>
<FormattedText
id='mobile.notification_settings_model.push_status_android'
id='mobile.notification_settings_mobile.push_status_android'
defaultMessage='Trigger push notifications when'
style={style.modalTitle}
/>
@@ -216,6 +258,95 @@ class NotificationSettingsMobileAndroid extends NotificationSettingsMobileBase {
);
}
renderMobileSoundsModal(style) {
const {defaultSound, selectedUri} = this.state;
const {intl, notificationPreferences} = this.props;
const {defaultUri, sounds} = notificationPreferences;
const soundsArray = sounds.filter((s) => s.uri !== defaultUri).map((s, i) => {
return (
<RadioButton
key={`sound-${i}`}
label={s.name}
value={s.uri}
checked={s.uri === selectedUri}
/>
);
});
return (
<Modal
animationType='slide'
transparent={true}
visible={this.state.showMobileSoundsModal}
onRequestClose={this.cancelMobileSoundsModal}
>
<View style={style.modalOverlay}>
<View style={style.modal}>
<View style={style.modalBody}>
<View style={style.modalTitleContainer}>
<FormattedText
id='mobile.notification_settings_mobile.sounds_title'
defaultMessage='Notification sound'
style={style.modalTitle}
/>
</View>
<ScrollView>
<RadioButtonGroup
name='soundsSettings'
onSelect={this.onMobileSoundChanged}
>
<RadioButton
label={intl.formatMessage({
id: 'mobile.notification_settings_mobile.default_sound',
defaultMessage: 'Default ({sound})'
}, {sound: defaultSound})}
value={defaultUri}
checked={selectedUri === null}
/>
<RadioButton
label={intl.formatMessage({
id: 'mobile.notification_settings_mobile.no_sound',
defaultMessage: 'None'
})}
value='none'
checked={selectedUri === 'none'}
/>
{soundsArray}
</RadioButtonGroup>
</ScrollView>
</View>
<View style={style.modalFooter}>
<View style={style.separator}/>
<View style={style.modalFooterContainer}>
<TouchableOpacity
style={style.modalFooterOptionContainer}
onPress={this.cancelMobileSoundsModal}
>
<FormattedText
id='mobile.notification_settings.modal_cancel'
defaultMessage='CANCEL'
style={style.modalFooterOption}
/>
</TouchableOpacity>
<View style={{marginRight: 10}}/>
<TouchableOpacity
style={style.modalFooterOptionContainer}
onPress={this.saveMobileSoundsModal}
>
<FormattedText
id='mobile.notification_settings.modal_save'
defaultMessage='SAVE'
style={style.modalFooterOption}
/>
</TouchableOpacity>
</View>
</View>
</View>
</View>
</Modal>
);
}
renderMobilePushSection() {
const {config, theme} = this.props;
@@ -306,7 +437,7 @@ class NotificationSettingsMobileAndroid extends NotificationSettingsMobileBase {
)}
label={(
<FormattedText
id='mobile.notification_settings_model.push_status_android'
id='mobile.notification_settings_mobile.push_status_android'
defaultMessage='Trigger push notifications when'
/>
)}
@@ -319,6 +450,114 @@ class NotificationSettingsMobileAndroid extends NotificationSettingsMobileBase {
);
}
renderMobileSoundSection(style) {
const {config, theme} = this.props;
const {defaultSound, sound} = this.state;
const showSection = config.SendPushNotifications === 'true' && this.state.push !== 'none';
if (!showSection) {
return null;
}
let description;
if (sound === 'none') {
description = (
<FormattedText
id='mobile.notification_settings_mobile.no_sound'
defaultMessage='None'
/>
);
} else if (sound) {
description = (
<Text>
{sound}
</Text>
);
} else {
description = (
<FormattedText
id='mobile.notification_settings_mobile.default_sound'
defaultMessage='Default ({sound})'
values={{
sound: defaultSound
}}
/>
);
}
return (
<View>
<SectionItem
description={description}
label={(
<FormattedText
id='mobile.notification_settings_mobile.sound'
defaultMessage='Sound'
/>
)}
action={this.showMobileSoundsModal}
actionType='default'
theme={theme}
/>
<View style={style.separator}/>
</View>
);
}
renderMobileTestSection(style) {
const {config, theme} = this.props;
const showSection = config.SendPushNotifications === 'true' && this.state.push !== 'none';
if (!showSection) {
return null;
}
return (
<View>
<SectionItem
label={(
<FormattedText
id='mobile.notification_settings_mobile.test'
defaultMessage='Send me a test notification'
/>
)}
action={this.sendTestNotification}
actionType='default'
theme={theme}
/>
<View style={style.separator}/>
</View>
);
}
renderMobileVibrateSection(style) {
const {config, theme} = this.props;
const {shouldVibrate} = this.state;
const showSection = config.SendPushNotifications === 'true' && this.state.push !== 'none';
if (!showSection) {
return null;
}
return (
<View>
<SectionItem
label={(
<FormattedText
id='mobile.notification_settings_mobile.vibrate'
defaultMessage='Vibrate'
/>
)}
action={this.toggleVibrate}
actionType='toggle'
selected={shouldVibrate}
theme={theme}
/>
<View style={style.separator}/>
</View>
);
}
saveMobilePushModal = () => {
this.setState({showMobilePushModal: false});
this.setMobilePush(this.push);
@@ -329,6 +568,46 @@ class NotificationSettingsMobileAndroid extends NotificationSettingsMobileBase {
this.setMobilePushStatus(this.pushStatus);
};
saveMobileSoundsModal = () => {
const {defaultSound} = this.state;
const {intl, notificationPreferences} = this.props;
const {defaultUri, sounds} = notificationPreferences;
let sound;
let selectedUri = null;
if (this.sound === defaultUri) {
sound = defaultSound;
} else if (this.sound === 'none') {
selectedUri = this.sound;
sound = intl.formatMessage({
id: 'mobile.notification_settings_mobile.no_sound',
defaultMessage: 'None'
});
} else {
selectedUri = this.sound;
const selected = sounds.find((s) => s.uri === selectedUri);
sound = selected.name;
}
NotificationPreferences.setNotificationSound(selectedUri);
this.setState({selectedUri, sound, showMobileSoundsModal: false});
};
sendTestNotification = () => {
const {intl} = this.props;
PushNotifications.localNotification({
message: intl.formatMessage({
id: 'mobile.notification_settings_mobile.test_push',
defaultMessage: 'This is a test push notification'
}),
userInfo: {
localNotification: true,
localTest: true
}
});
};
showMobilePushModal = () => {
this.setState({showMobilePushModal: true});
};
@@ -337,6 +616,20 @@ class NotificationSettingsMobileAndroid extends NotificationSettingsMobileBase {
this.setState({showMobilePushStatusModal: true});
};
showMobileSoundsModal = () => {
this.setState({showMobileSoundsModal: true});
};
toggleBlink = () => {
NotificationPreferences.setShouldBlink(this.state.shouldBlink);
this.setState({shouldBlink: !this.state.shouldBlink});
};
toggleVibrate = () => {
NotificationPreferences.setShouldVibrate(this.state.shouldVibrate);
this.setState({shouldVibrate: !this.state.shouldVibrate});
};
render() {
const {theme} = this.props;
const style = getStyleSheet(theme);
@@ -351,9 +644,14 @@ class NotificationSettingsMobileAndroid extends NotificationSettingsMobileBase {
{this.renderMobilePushSection()}
<View style={style.separator}/>
{this.renderMobilePushStatusSection(style)}
{this.renderMobileSoundSection(style)}
{this.renderMobileVibrateSection(style)}
{this.renderMobileBlinkSection(style)}
{this.renderMobileTestSection(style)}
</ScrollView>
{this.renderMobilePushModal(style)}
{this.renderMobilePushStatusModal(style)}
{this.renderMobileSoundsModal(style)}
</View>
);
}
@@ -372,8 +670,8 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
},
separator: {
backgroundColor: changeOpacity(theme.centerChannelColor, 0.1),
flex: 1,
height: 1
height: 1,
width: '100%'
},
scrollView: {
flex: 1,
@@ -394,6 +692,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
width: '95%'
},
modalBody: {
maxHeight: '80%',
paddingHorizontal: 24
},
modalTitleContainer: {

View File

@@ -94,7 +94,7 @@ class NotificationSettingsMobileIos extends NotificationSettingsMobileBase {
return (
<Section
headerId='mobile.notification_settings_model.push_status'
headerId='mobile.notification_settings_mobile.push_status'
headerDefaultMessage='TRIGGER PUSH NOTIFICATIONS WHEN'
theme={theme}
>

View File

@@ -2,6 +2,7 @@
// See License.txt for license information.
import {PureComponent} from 'react';
import {Platform} from 'react-native';
import PropTypes from 'prop-types';
import {intlShape} from 'react-intl';
@@ -11,6 +12,7 @@ export default class NotificationSettingsMobileBase extends PureComponent {
currentUser: PropTypes.object.isRequired,
intl: intlShape.isRequired,
navigator: PropTypes.object,
notificationPreferences: PropTypes.object,
onBack: PropTypes.func.isRequired,
theme: PropTypes.object.isRequired
};
@@ -23,14 +25,49 @@ export default class NotificationSettingsMobileBase extends PureComponent {
this.state = {
...notifyProps,
...this.getNotificationPreferences(props),
showMobilePushModal: false,
showMobilePushStatusModal: false
showMobilePushStatusModal: false,
showMobileSoundsModal: false
};
this.push = this.state.push;
this.pushStatus = this.state.push_status;
props.navigator.setOnNavigatorEvent(this.onNavigatorEvent);
}
getNotificationPreferences = (props) => {
if (Platform.OS === 'android') {
const {
defaultUri,
shouldBlink,
shouldVibrate,
selectedUri,
sounds
} = props.notificationPreferences;
const defSound = sounds.find((s) => s.uri === defaultUri);
const defaultSound = defSound.name;
let sound;
if (selectedUri && selectedUri === 'none') {
sound = 'none';
} else if (selectedUri) {
const selected = sounds.find((s) => s.uri === selectedUri);
sound = selected.name;
}
return {
defaultSound,
shouldVibrate,
shouldBlink,
selectedUri,
sound
};
}
return {};
};
onNavigatorEvent = (event) => {
if (event.type === 'ScreenChangedEvent') {
switch (event.id) {
@@ -50,12 +87,28 @@ export default class NotificationSettingsMobileBase extends PureComponent {
};
saveUserNotifyProps = () => {
const props = {...this.state};
const {
channel,
comments,
desktop,
desktop_duration,
email,
first_name,
mention_keys,
push,
push_status
} = this.state;
Reflect.deleteProperty(props, 'showMobilePushModal');
Reflect.deleteProperty(props, 'showMobilePushStatusModal');
this.props.onBack({
...props,
channel,
comments,
desktop,
desktop_duration,
email,
first_name,
mention_keys,
push,
push_status,
user_id: this.props.currentUser.id
});
};

View File

@@ -5,7 +5,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import {
Switch,
TouchableWithoutFeedback,
TouchableOpacity,
View
} from 'react-native';
import FontAwesomeIcon from 'react-native-vector-icons/FontAwesome';
@@ -83,9 +83,9 @@ function sectionItem(props) {
if (actionType === ActionTypes.DEFAULT || actionType === ActionTypes.SELECT || actionType === ActionTypes.ARROW) {
return (
<TouchableWithoutFeedback onPress={() => action(actionValue)}>
<TouchableOpacity onPress={() => action(actionValue)}>
{component}
</TouchableWithoutFeedback>
</TouchableOpacity>
);
}

View File

@@ -1925,10 +1925,18 @@
"mobile.notification_settings_mentions.keywordsDescription": "Other words that trigger a mention",
"mobile.notification_settings_mentions.keywordsHelp": "Keywords are non-case sensitive and should be separated by a comma.",
"mobile.notification_settings_mentions.wordsTrigger": "WORDS THAT TRIGGER MENTIONS",
"mobile.notification_settings_mobile.light": "Light",
"mobile.notification_settings_mobile.no_sound": "None",
"mobile.notification_settings_mobile.push_activity": "SEND NOTIFICATIONS FOR",
"mobile.notification_settings_mobile.push_activity_android": "Send notifications for",
"mobile.notification_settings_model.push_status": "TRIGGER PUSH NOTIFICATIONS WHEN",
"mobile.notification_settings_model.push_status_android": "Trigger push notifications when",
"mobile.notification_settings_mobile.push_status": "TRIGGER PUSH NOTIFICATIONS WHEN",
"mobile.notification_settings_mobile.push_status_android": "Trigger push notifications when",
"mobile.notification_settings_mobile.default_sound": "Default ({sound})",
"mobile.notification_settings_mobile.sound": "Sound",
"mobile.notification_settings_mobile.sounds_title": "Notification sound",
"mobile.notification_settings_mobile.test": "Send me a test notification",
"mobile.notification_settings_mobile.test_push": "This is a test push notification",
"mobile.notification_settings_mobile.vibrate": "Vibrate",
"mobile.offlineIndicator.connected": "Connected",
"mobile.offlineIndicator.connecting": "Connecting...",
"mobile.offlineIndicator.offline": "No internet connection",

View File

@@ -417,6 +417,12 @@ platform :android do
new_string: 'package com.mattermost.rn;'
)
find_replace_string(
path_to_file: './android/app/src/main/java/com/mattermost/rn/NotificationPreferencesModule.java',
old_string: 'package com.mattermost.rnbeta;',
new_string: 'package com.mattermost.rn;'
)
find_replace_string(
path_to_file: './android/app/src/main/java/com/mattermost/rn/MattermostManagedModule.java',
old_string: 'package com.mattermost.rnbeta;',
@@ -424,7 +430,7 @@ platform :android do
)
find_replace_string(
path_to_file: './android/app/src/main/java/com/mattermost/rn/MattermostManagedPackage.java',
path_to_file: './android/app/src/main/java/com/mattermost/rn/MattermostPackage.java',
old_string: 'package com.mattermost.rnbeta;',
new_string: 'package com.mattermost.rn;'
)