Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[IMPROVEMENT] Use react-native-notifier for in-app notifications #2139

Merged
merged 26 commits into from
Jun 16, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
ac539ab
[NEW] Using react-native-notifier for notifications inApp
EzequielDeOliveira May 22, 2020
1067f25
Removing useless comment
EzequielDeOliveira May 22, 2020
0b839aa
Merge branch 'develop' into notificationsInApp
diegolmello May 25, 2020
bea3c74
Using EventEmitter for in App notifications
EzequielDeOliveira Jun 3, 2020
60bad2d
Merge branch 'notificationsInApp' of https://github.com/EzequielDeOli…
EzequielDeOliveira Jun 3, 2020
0934a52
Using correct lifecycle to remove listener
EzequielDeOliveira Jun 3, 2020
16a8ded
Merge branch 'develop' into notificationsInApp
diegolmello Jun 5, 2020
5d17539
Merge branch 'develop' into notificationsInApp
diegolmello Jun 16, 2020
6de12e3
Make it work
diegolmello Jun 16, 2020
7f34d80
Remove from redux
diegolmello Jun 16, 2020
0e5f423
Use built-in hide function
diegolmello Jun 16, 2020
ff94dde
Reset timer https://github.com/seniv/react-native-notifier/pull/15
diegolmello Jun 16, 2020
accef16
Rename
diegolmello Jun 16, 2020
a36f180
Patch-package
diegolmello Jun 16, 2020
7dd17d4
Fix navigation
diegolmello Jun 16, 2020
b2cdb4e
Use Touchable
diegolmello Jun 16, 2020
49111c7
Refactor
diegolmello Jun 16, 2020
aa4e274
Export emitter constant
diegolmello Jun 16, 2020
4a61767
Use hooks
diegolmello Jun 16, 2020
13e1549
Update lib
diegolmello Jun 16, 2020
8fbb893
Memo
diegolmello Jun 16, 2020
7162ff3
Remove unnecessary config
diegolmello Jun 16, 2020
8ed4932
memo
diegolmello Jun 16, 2020
9b03f1d
Change style
diegolmello Jun 16, 2020
5e9206a
Refactor
diegolmello Jun 16, 2020
9ef6030
lint
diegolmello Jun 16, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
194 changes: 20 additions & 174 deletions app/notifications/inApp/index.js
Original file line number Diff line number Diff line change
@@ -1,88 +1,23 @@
import React from 'react';
import {
View, Text, StyleSheet, TouchableOpacity, Animated, Easing
} from 'react-native';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import equal from 'deep-equal';
import { NotifierRoot, Notifier, Easing } from 'react-native-notifier';
import { responsive } from 'react-native-responsive-ui';
import Touchable from 'react-native-platform-touchable';

import { hasNotch, isIOS, isTablet } from '../../utils/deviceInfo';
import { CustomIcon } from '../../lib/Icons';
import { themes } from '../../constants/colors';
import Avatar from '../../containers/Avatar';
import { removeNotification as removeNotificationAction } from '../../actions/notification';
import sharedStyles from '../../views/Styles';
import { ROW_HEIGHT } from '../../presentation/RoomItem';
import { withTheme } from '../../theme';
import { getUserSelector } from '../../selectors/login';
import NotifierComponent from './notifierComponent';

const AVATAR_SIZE = 48;
const ANIMATION_DURATION = 300;
const NOTIFICATION_DURATION = 3000;
const BUTTON_HIT_SLOP = {
top: 12, right: 12, bottom: 12, left: 12
};
const ANIMATION_PROPS = {
duration: ANIMATION_DURATION,
easing: Easing.inOut(Easing.quad),
useNativeDriver: true
};

const styles = StyleSheet.create({
container: {
height: ROW_HEIGHT,
paddingHorizontal: 14,
flex: 1,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
position: 'absolute',
zIndex: 2,
width: '100%',
borderBottomWidth: StyleSheet.hairlineWidth
},
content: {
flex: 1,
flexDirection: 'row',
alignItems: 'center'
},
inner: {
flex: 1
},
avatar: {
marginRight: 10
},
roomName: {
fontSize: 17,
lineHeight: 20,
...sharedStyles.textMedium
},
message: {
fontSize: 14,
lineHeight: 17,
...sharedStyles.textRegular
},
close: {
marginLeft: 10
}
});

class NotificationBadge extends React.Component {
static propTypes = {
navigation: PropTypes.object,
baseUrl: PropTypes.string,
user: PropTypes.object,
notification: PropTypes.object,
window: PropTypes.object,
removeNotification: PropTypes.func,
theme: PropTypes.string
}

constructor(props) {
super(props);
this.animatedValue = new Animated.Value(0);
theme: PropTypes.string,
removeNotification: PropTypes.func
}

shouldComponentUpdate(nextProps) {
Expand All @@ -109,45 +44,25 @@ class NotificationBadge extends React.Component {
if (navState && navState.routeName === 'RoomView' && navState.params && navState.params.rid === payload.rid) {
return;
}
this.show();
Notifier.showNotification({
showAnimationDuration: ANIMATION_DURATION,
hideAnimationDuration: ANIMATION_DURATION,
duration: NOTIFICATION_DURATION,
hideOnPress: false,
showEasing: Easing.inOut(Easing.quad),
Component: NotifierComponent,
componentProps: {
navigation,
hideNotification: this.hide
}
});
}
}

componentWillUnmount() {
this.clearTimeout();
}

show = () => {
Animated.timing(
this.animatedValue,
{
toValue: 1,
...ANIMATION_PROPS
}
).start(() => {
this.clearTimeout();
this.timeout = setTimeout(() => {
this.hide();
}, NOTIFICATION_DURATION);
});
}

hide = () => {
const { removeNotification } = this.props;
Animated.timing(
this.animatedValue,
{
toValue: 0,
...ANIMATION_PROPS
}
).start();
setTimeout(removeNotification, ANIMATION_DURATION);
}

clearTimeout = () => {
if (this.timeout) {
clearTimeout(this.timeout);
}
Notifier.hideNotification();
removeNotification();
}

getNavState = (routes) => {
Expand All @@ -157,88 +72,19 @@ class NotificationBadge extends React.Component {
return this.getNavState(routes.routes[routes.index]);
}

goToRoom = async() => {
const { notification, navigation, baseUrl } = this.props;
const { payload } = notification;
const { rid, type, prid } = payload;
if (!rid) {
return;
}
const name = type === 'd' ? payload.sender.username : payload.name;
// if sub is not on local database, title will be null, so we use payload from notification
const { title = name } = notification;
await navigation.navigate('RoomsListView');
navigation.navigate('RoomView', {
rid, name: title, t: type, prid, baseUrl
});
this.hide();
}

render() {
const {
baseUrl, user: { id: userId, token }, notification, window, theme
} = this.props;
const { message, payload } = notification;
const { type } = payload;
const name = type === 'd' ? payload.sender.username : payload.name;
// if sub is not on local database, title and avatar will be null, so we use payload from notification
const { title = name, avatar = name } = notification;

let top = 0;
if (isIOS) {
const portrait = window.height > window.width;
if (portrait) {
top = hasNotch ? 45 : 20;
} else {
top = isTablet ? 20 : 0;
}
}

const translateY = this.animatedValue.interpolate({
inputRange: [0, 1],
outputRange: [-top - ROW_HEIGHT, top]
});
return (
<Animated.View
style={[
styles.container,
{
transform: [{ translateY }],
backgroundColor: themes[theme].focusedBackground,
borderColor: themes[theme].separatorColor
}
]}
>
<Touchable
style={styles.content}
onPress={this.goToRoom}
hitSlop={BUTTON_HIT_SLOP}
background={Touchable.SelectableBackgroundBorderless()}
>
<>
<Avatar text={avatar} size={AVATAR_SIZE} type={type} baseUrl={baseUrl} style={styles.avatar} userId={userId} token={token} />
<View style={styles.inner}>
<Text style={[styles.roomName, { color: themes[theme].titleText }]} numberOfLines={1}>{title}</Text>
<Text style={[styles.message, { color: themes[theme].titleText }]} numberOfLines={1}>{message}</Text>
</View>
</>
</Touchable>
<TouchableOpacity onPress={this.hide}>
<CustomIcon name='circle-cross' style={[styles.close, { color: themes[theme].titleText }]} size={20} />
</TouchableOpacity>
</Animated.View>
<NotifierRoot />
);
}
}

const mapStateToProps = state => ({
user: getUserSelector(state),
baseUrl: state.server.server,
notification: state.notification
});

const mapDispatchToProps = dispatch => ({
removeNotification: () => dispatch(removeNotificationAction())
});

export default responsive(connect(mapStateToProps, mapDispatchToProps)(withTheme(NotificationBadge)));
export default responsive(connect(mapStateToProps, mapDispatchToProps)((NotificationBadge)));
138 changes: 138 additions & 0 deletions app/notifications/inApp/notifierComponent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import React from 'react';
import {
StyleSheet, SafeAreaView, View, Text, TouchableOpacity
} from 'react-native';
import PropTypes from 'prop-types';
import Touchable from 'react-native-platform-touchable';
import { connect } from 'react-redux';

import Avatar from '../../containers/Avatar';
import { CustomIcon } from '../../lib/Icons';
import sharedStyles from '../../views/Styles';
import { themes } from '../../constants/colors';
import { withTheme } from '../../theme';
import { getUserSelector } from '../../selectors/login';
import { ROW_HEIGHT } from '../../presentation/RoomItem';


const AVATAR_SIZE = 48;
const BUTTON_HIT_SLOP = {
top: 12, right: 12, bottom: 12, left: 12
};

const styles = StyleSheet.create({
container: {
height: ROW_HEIGHT,
paddingHorizontal: 14,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
width: '100%',
borderBottomWidth: StyleSheet.hairlineWidth
},
content: {
flex: 1,
flexDirection: 'row',
alignItems: 'center'
},
inner: {
flex: 1
},
avatar: {
marginRight: 10
},
roomName: {
fontSize: 17,
lineHeight: 20,
...sharedStyles.textMedium
},
message: {
fontSize: 14,
lineHeight: 17,
...sharedStyles.textRegular
},
close: {
marginLeft: 10
}
});


class NotifierComponent extends React.Component {
static propTypes = {
navigation: PropTypes.object,
baseUrl: PropTypes.string,
user: PropTypes.object,
notification: PropTypes.object,
theme: PropTypes.string,
hideNotification: PropTypes.func
}

goToRoom = async() => {
const {
notification, navigation, baseUrl, hideNotification
} = this.props;
const { payload } = notification;
const { rid, type, prid } = payload;
if (!rid) {
return;
}
const name = type === 'd' ? payload.sender.username : payload.name;
// if sub is not on local database, title will be null, so we use payload from notification
const { title = name } = notification;
await navigation.navigate('RoomsListView');
navigation.navigate('RoomView', {
rid, name: title, t: type, prid, baseUrl
});
hideNotification();
}

render() {
const {
baseUrl, user: { id: userId, token }, notification, theme, hideNotification
} = this.props;
const { message, payload } = notification;
const { type } = payload;
const name = type === 'd' ? payload.sender.username : payload.name;
// if sub is not on local database, title and avatar will be null, so we use payload from notification
const { title = name, avatar = name } = notification;

return (
<SafeAreaView>
<View style={[
styles.container,
{
backgroundColor: themes[theme].focusedBackground,
borderColor: themes[theme].separatorColor
}
]}
>
<Touchable
style={styles.content}
onPress={this.goToRoom}
hitSlop={BUTTON_HIT_SLOP}
background={Touchable.SelectableBackgroundBorderless()}
>
<>
<Avatar text={avatar} size={AVATAR_SIZE} type={type} baseUrl={baseUrl} style={styles.avatar} userId={userId} token={token} />
<View style={styles.inner}>
<Text style={[styles.roomName, { color: themes[theme].titleText }]} numberOfLines={1}>{title}</Text>
<Text style={[styles.message, { color: themes[theme].titleText }]} numberOfLines={1}>{message}</Text>
</View>
</>
</Touchable>
<TouchableOpacity onPress={hideNotification}>
<CustomIcon name='circle-cross' style={[styles.close, { color: themes[theme].titleText }]} size={20} />
</TouchableOpacity>
</View>
</SafeAreaView>
);
}
}

const mapStateToProps = state => ({
user: getUserSelector(state),
baseUrl: state.server.server,
notification: state.notification
});

export default connect(mapStateToProps)(withTheme(NotifierComponent));
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
"react-native-modal": "11.5.6",
"react-native-navigation-bar-color": "2.0.1",
"react-native-notifications": "2.1.7",
"react-native-notifier": "^1.1.0",
"react-native-orientation-locker": "1.1.8",
"react-native-picker-select": "7.0.0",
"react-native-platform-touchable": "^1.1.1",
Expand Down
Loading