Commit c15d95df authored by Wietse Kuipers's avatar Wietse Kuipers
Browse files

Merge branch 'feature/push-notifications' into 'master'

Push Notifications

See merge request !26
parents c22cc3d8 c86a3d63
...@@ -22,6 +22,10 @@ DerivedData ...@@ -22,6 +22,10 @@ DerivedData
*.xcuserstate *.xcuserstate
project.xcworkspace project.xcworkspace
# Pods
#
ios/Pods
# Android/IntelliJ # Android/IntelliJ
# #
build/ build/
......
...@@ -141,11 +141,13 @@ android { ...@@ -141,11 +141,13 @@ android {
} }
dependencies { dependencies {
compile project(':react-native-fcm')
compile project(':react-native-linear-gradient') compile project(':react-native-linear-gradient')
compile project(':react-native-vector-icons') compile project(':react-native-vector-icons')
compile fileTree(dir: "libs", include: ["*.jar"]) compile fileTree(dir: "libs", include: ["*.jar"])
compile "com.android.support:appcompat-v7:23.0.1" compile "com.android.support:appcompat-v7:23.0.1"
compile "com.facebook.react:react-native:+" // From node_modules compile "com.facebook.react:react-native:+" // From node_modules
compile 'com.google.firebase:firebase-core:11.4.0'
} }
// Run this once to be able to run the application with BUCK // Run this once to be able to run the application with BUCK
...@@ -162,3 +164,5 @@ gradle.projectsEvaluated { ...@@ -162,3 +164,5 @@ gradle.projectsEvaluated {
processBuilder.start() processBuilder.start()
} }
} }
apply plugin: 'com.google.gms.google-services'
\ No newline at end of file
{
"project_info": {
"project_number": "488731759938",
"firebase_url": "https://thalia-493a0.firebaseio.com",
"project_id": "thalia-493a0",
"storage_bucket": "thalia-493a0.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:488731759938:android:ce90d9e7effcbbfb",
"android_client_info": {
"package_name": "com.thaliapp"
}
},
"oauth_client": [
{
"client_id": "488731759938-k103fp2m146qhfsf7s3to9bm43qhcous.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyApn30VsmPqjs4N5_kR68tpXzBWSyOxuoM"
}
],
"services": {
"analytics_service": {
"status": 1
},
"appinvite_service": {
"status": 1,
"other_platform_oauth_client": []
},
"ads_service": {
"status": 2
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:488731759938:android:e73d72886d6d4f94",
"android_client_info": {
"package_name": "com.thaliapp.dev"
}
},
"oauth_client": [
{
"client_id": "488731759938-k103fp2m146qhfsf7s3to9bm43qhcous.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyApn30VsmPqjs4N5_kR68tpXzBWSyOxuoM"
}
],
"services": {
"analytics_service": {
"status": 1
},
"appinvite_service": {
"status": 1,
"other_platform_oauth_client": []
},
"ads_service": {
"status": 2
}
}
}
],
"configuration_version": "1"
}
\ No newline at end of file
...@@ -3,12 +3,12 @@ ...@@ -3,12 +3,12 @@
android:versionCode="1" android:versionCode="1"
android:versionName="1.0"> android:versionName="1.0">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-sdk <uses-sdk
android:minSdkVersion="16" android:minSdkVersion="16"
android:targetSdkVersion="22" /> android:targetSdkVersion="22"/>
<application <application
android:name=".MainApplication" android:name=".MainApplication"
...@@ -22,13 +22,30 @@ ...@@ -22,13 +22,30 @@
android:configChanges="keyboard|keyboardHidden|orientation|screenSize" android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:windowSoftInputMode="adjustResize"> android:windowSoftInputMode="adjustResize">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" /> <activity android:name="com.facebook.react.devsupport.DevSettingsActivity"/>
<meta-data android:name="android.max_aspect" android:value="2.1" /> <service android:name="com.evollu.react.fcm.MessagingService" android:enabled="true" android:exported="true">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
<service android:name="com.evollu.react.fcm.InstanceIdService" android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
</intent-filter>
</service>
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@mipmap/ic_notification" />
<meta-data
android:name="android.max_aspect"
android:value="2.1" />
</application> </application>
</manifest> </manifest>
...@@ -3,6 +3,7 @@ package com.thaliapp; ...@@ -3,6 +3,7 @@ package com.thaliapp;
import android.app.Application; import android.app.Application;
import com.facebook.react.ReactApplication; import com.facebook.react.ReactApplication;
import com.evollu.react.fcm.FIRMessagingPackage;
import com.BV.LinearGradient.LinearGradientPackage; import com.BV.LinearGradient.LinearGradientPackage;
import com.oblador.vectoricons.VectorIconsPackage; import com.oblador.vectoricons.VectorIconsPackage;
import com.facebook.react.ReactNativeHost; import com.facebook.react.ReactNativeHost;
...@@ -25,6 +26,7 @@ public class MainApplication extends Application implements ReactApplication { ...@@ -25,6 +26,7 @@ public class MainApplication extends Application implements ReactApplication {
protected List<ReactPackage> getPackages() { protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList( return Arrays.<ReactPackage>asList(
new MainReactPackage(), new MainReactPackage(),
new FIRMessagingPackage(),
new LinearGradientPackage(), new LinearGradientPackage(),
new VectorIconsPackage() new VectorIconsPackage()
); );
......
...@@ -6,6 +6,7 @@ buildscript { ...@@ -6,6 +6,7 @@ buildscript {
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:2.2.3' classpath 'com.android.tools.build:gradle:2.2.3'
classpath 'com.google.gms:google-services:3.1.0'
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files
...@@ -20,5 +21,8 @@ allprojects { ...@@ -20,5 +21,8 @@ allprojects {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url "$rootDir/../node_modules/react-native/android" url "$rootDir/../node_modules/react-native/android"
} }
maven {
url "https://maven.google.com" // Google's Maven repository
}
} }
} }
rootProject.name = 'ThaliApp' rootProject.name = 'ThaliApp'
include ':react-native-fcm'
project(':react-native-fcm').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fcm/android')
include ':react-native-linear-gradient' include ':react-native-linear-gradient'
project(':react-native-linear-gradient').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-linear-gradient/android') project(':react-native-linear-gradient').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-linear-gradient/android')
include ':react-native-vector-icons' include ':react-native-vector-icons'
......
export const REGISTER = 'PUSH_NOTIFICATIONS_REGISTER';
export const INVALIDATE = 'PUSH_NOTIFICATIONS_INVALIDATE';
export function register(token) {
return { type: REGISTER, payload: { token } };
}
export function invalidate() {
return { type: INVALIDATE };
}
...@@ -4,6 +4,7 @@ import { applyMiddleware, combineReducers, createStore } from 'redux'; ...@@ -4,6 +4,7 @@ import { applyMiddleware, combineReducers, createStore } from 'redux';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import thunk from 'redux-thunk'; import thunk from 'redux-thunk';
import createSagaMiddleware from 'redux-saga'; import createSagaMiddleware from 'redux-saga';
import FCM, { FCMEvent } from 'react-native-fcm';
import Moment from 'moment'; import Moment from 'moment';
import 'moment/locale/nl'; import 'moment/locale/nl';
...@@ -11,6 +12,7 @@ import * as reducers from './reducers'; ...@@ -11,6 +12,7 @@ import * as reducers from './reducers';
import sagas from './sagas'; import sagas from './sagas';
import ReduxNavigator from './components/navigator'; import ReduxNavigator from './components/navigator';
import * as loginActions from './actions/login'; import * as loginActions from './actions/login';
import { register } from './actions/pushNotifications';
const createStoreWithMiddleware = applyMiddleware(thunk)(createStore); const createStoreWithMiddleware = applyMiddleware(thunk)(createStore);
const sagaMiddleware = createSagaMiddleware(); const sagaMiddleware = createSagaMiddleware();
...@@ -29,6 +31,20 @@ const pairsToObject = (obj, pair) => { ...@@ -29,6 +31,20 @@ const pairsToObject = (obj, pair) => {
return obj2; return obj2;
}; };
FCM.on(FCMEvent.Notification, async (notif) => {
if (notif.fcm) {
FCM.presentLocalNotification({
title: notif.fcm.title,
body: notif.fcm.body,
color: notif.fcm.color,
icon: notif.fcm.icon === null ? 'ic_notification' : notif.fcm.icon,
action: notif.fcm.action,
tag: notif.fcm.tag,
show_in_foreground: true,
});
}
});
class Main extends Component { class Main extends Component {
componentDidMount() { componentDidMount() {
Moment.locale('nl'); Moment.locale('nl');
...@@ -43,6 +59,7 @@ class Main extends Component { ...@@ -43,6 +59,7 @@ class Main extends Component {
if (username !== null && token !== null) { if (username !== null && token !== null) {
store.dispatch(loginActions.success(username, token, displayName, photo, '')); store.dispatch(loginActions.success(username, token, displayName, photo, ''));
store.dispatch(register(token));
} }
}); });
} }
......
...@@ -5,6 +5,7 @@ import eventSaga from './event'; ...@@ -5,6 +5,7 @@ import eventSaga from './event';
import profileSaga from './profile'; import profileSaga from './profile';
import welcomeSaga from './welcome'; import welcomeSaga from './welcome';
import calendarSaga from './calendar'; import calendarSaga from './calendar';
import pushNotificationsSaga from './pushNotifications';
const sagas = function* sagas() { const sagas = function* sagas() {
yield all([ yield all([
...@@ -13,6 +14,7 @@ const sagas = function* sagas() { ...@@ -13,6 +14,7 @@ const sagas = function* sagas() {
fork(profileSaga), fork(profileSaga),
fork(welcomeSaga), fork(welcomeSaga),
fork(calendarSaga), fork(calendarSaga),
fork(pushNotificationsSaga),
]); ]);
}; };
......
...@@ -4,6 +4,7 @@ import { AsyncStorage } from 'react-native'; ...@@ -4,6 +4,7 @@ import { AsyncStorage } from 'react-native';
import { apiRequest, url } from '../url'; import { apiRequest, url } from '../url';
import * as loginActions from '../actions/login'; import * as loginActions from '../actions/login';
import * as pushNotificationsActions from '../actions/pushNotifications';
const USERNAMEKEY = '@MyStore:username'; const USERNAMEKEY = '@MyStore:username';
const TOKENKEY = '@MyStore:token'; const TOKENKEY = '@MyStore:token';
...@@ -52,6 +53,7 @@ const login = function* login(action) { ...@@ -52,6 +53,7 @@ const login = function* login(action) {
yield put(loginActions.success( yield put(loginActions.success(
user, token, displayName, avatar, user, token, displayName, avatar,
)); ));
yield put(pushNotificationsActions.register(token));
yield delay(2000); yield delay(2000);
yield put(loginActions.reset()); yield put(loginActions.reset());
} catch (error) { } catch (error) {
...@@ -64,6 +66,7 @@ const login = function* login(action) { ...@@ -64,6 +66,7 @@ const login = function* login(action) {
const logout = function* logout() { const logout = function* logout() {
yield call(AsyncStorage.multiRemove, [USERNAMEKEY, TOKENKEY]); yield call(AsyncStorage.multiRemove, [USERNAMEKEY, TOKENKEY]);
yield delay(2000); yield delay(2000);
yield put(pushNotificationsActions.invalidate());
yield put(loginActions.reset()); yield put(loginActions.reset());
}; };
......
import { call, takeEvery } from 'redux-saga/effects';
import { Platform } from 'react-native';
import FCM from 'react-native-fcm';
import { apiRequest } from '../url';
import * as pushNotificationsActions from '../actions/pushNotifications';
const register = function* register(action) {
const { token } = action.payload;
let pushToken;
if (Platform.OS === 'ios') {
pushToken = yield call(FCM.getFCMToken);
} else {
yield call(FCM.requestPermissions);
pushToken = yield call(FCM.getFCMToken);
}
const data = {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: `Token ${token}`,
},
body: JSON.stringify({
registration_id: pushToken,
type: Platform.OS,
}),
};
yield call(apiRequest, 'devices', data);
};
const invalidate = function* invalidate() {
yield call(FCM.deleteInstanceId);
};
const pushNotificationsSaga = function* pushNotificationsSaga() {
yield takeEvery(pushNotificationsActions.REGISTER, register);
yield takeEvery(pushNotificationsActions.INVALIDATE, invalidate);
};
export default pushNotificationsSaga;
# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'
target 'ThaliApp' do
# Uncomment the next line if you're using Swift or would like to use dynamic frameworks
# use_frameworks!
# Pods for ThaliApp
pod 'FirebaseMessaging'
target 'ThaliAppTests' do
inherit! :search_paths
# Pods for testing
end
end
PODS:
- FirebaseAnalytics (3.6.0):
- FirebaseCore (~> 3.4)
- FirebaseInstanceID (~> 1.0)
- GoogleInterchangeUtilities (~> 1.2)
- GoogleSymbolUtilities (~> 1.1)
- GoogleToolboxForMac/NSData+zlib (~> 2.1)
- FirebaseCore (3.4.6):
- GoogleInterchangeUtilities (~> 1.2)
- GoogleToolboxForMac/NSData+zlib (~> 2.1)
- FirebaseInstanceID (1.0.8)
- FirebaseMessaging (1.2.1):
- FirebaseAnalytics (~> 3.4)
- FirebaseInstanceID (~> 1.0)
- GoogleInterchangeUtilities (~> 1.2)
- GoogleSymbolUtilities (~> 1.1)
- GoogleToolboxForMac/Logger (~> 2.1)
- GoogleInterchangeUtilities (1.2.2):
- GoogleSymbolUtilities (~> 1.1)
- GoogleSymbolUtilities (1.1.2)
- GoogleToolboxForMac/Defines (2.1.0)
- GoogleToolboxForMac/Logger (2.1.0):
- GoogleToolboxForMac/Defines (= 2.1.0)
- GoogleToolboxForMac/NSData+zlib (2.1.0):
- GoogleToolboxForMac/Defines (= 2.1.0)
DEPENDENCIES:
- FirebaseMessaging
SPEC CHECKSUMS:
FirebaseAnalytics: 9c67af0ebeb8d2146c9b4ea2616439affa947b58
FirebaseCore: 03da1cb32615569bbc2830a22f9ad753d9a02ef5
FirebaseInstanceID: ba1e640935235e5fac39dfa816fe7660e72e1a8a
FirebaseMessaging: acf66347e43c5637c697060c3001d25c809a4131
GoogleInterchangeUtilities: d5bc4d88d5b661ab72f9d70c58d02ca8c27ad1f7
GoogleSymbolUtilities: 631ee17048aa5e9ab133470d768ea997a5ef9b96
GoogleToolboxForMac: 2b2596cbb7186865e98cadf2b1e262d851c2b168
PODFILE CHECKSUM: e96d08e64ae034c5cc5d47d35f4df47f936e7fc3
COCOAPODS: 1.2.1
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment