forked from Ivasoft/mattermost-mobile
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:
@@ -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'
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
5
app/notification_preferences/index.js
Normal file
5
app/notification_preferences/index.js
Normal 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;
|
||||
@@ -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
|
||||
};
|
||||
10
app/notification_preferences/notification_preferences.ios.js
Normal file
10
app/notification_preferences/notification_preferences.ios.js
Normal 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
|
||||
};
|
||||
@@ -92,6 +92,10 @@ class PushNotification {
|
||||
}
|
||||
}
|
||||
|
||||
localNotification(notification) {
|
||||
NotificationsAndroid.localNotification(notification);
|
||||
}
|
||||
|
||||
cancelAllLocalNotifications() {
|
||||
NotificationsAndroid.cancelAllLocalNotifications();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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}
|
||||
>
|
||||
|
||||
@@ -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
|
||||
});
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;'
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user