[IMPROVEMENT] Use react-native-notifier for in-app notifications (#2139)
Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com> Co-authored-by: Diego Mello <diegolmello@gmail.com>
This commit is contained in:
parent
089e4bf3eb
commit
2632ef50f5
|
@ -3,14 +3,12 @@ import PropTypes from 'prop-types';
|
|||
import { NavigationContainer } from '@react-navigation/native';
|
||||
import { createStackNavigator } from '@react-navigation/stack';
|
||||
import { connect } from 'react-redux';
|
||||
import { SafeAreaProvider, initialWindowMetrics } from 'react-native-safe-area-context';
|
||||
|
||||
import Navigation from './lib/Navigation';
|
||||
import { defaultHeader, getActiveRouteName, navigationTheme } from './utils/navigation';
|
||||
import {
|
||||
ROOT_LOADING, ROOT_OUTSIDE, ROOT_NEW_SERVER, ROOT_INSIDE, ROOT_SET_USERNAME, ROOT_BACKGROUND
|
||||
} from './actions/app';
|
||||
import { ActionSheetProvider } from './containers/ActionSheet';
|
||||
|
||||
// Stacks
|
||||
import AuthLoadingView from './views/AuthLoadingView';
|
||||
|
@ -53,57 +51,53 @@ const App = React.memo(({ root, isMasterDetail }) => {
|
|||
}, []);
|
||||
|
||||
return (
|
||||
<SafeAreaProvider initialMetrics={initialWindowMetrics}>
|
||||
<ActionSheetProvider>
|
||||
<NavigationContainer
|
||||
theme={navTheme}
|
||||
ref={Navigation.navigationRef}
|
||||
onStateChange={(state) => {
|
||||
const previousRouteName = Navigation.routeNameRef.current;
|
||||
const currentRouteName = getActiveRouteName(state);
|
||||
if (previousRouteName !== currentRouteName) {
|
||||
setCurrentScreen(currentRouteName);
|
||||
}
|
||||
Navigation.routeNameRef.current = currentRouteName;
|
||||
}}
|
||||
>
|
||||
<Stack.Navigator screenOptions={{ headerShown: false, animationEnabled: false }}>
|
||||
<>
|
||||
{root === ROOT_LOADING || root === ROOT_BACKGROUND ? (
|
||||
<Stack.Screen
|
||||
name='AuthLoading'
|
||||
component={AuthLoadingView}
|
||||
/>
|
||||
) : null}
|
||||
{root === ROOT_OUTSIDE || root === ROOT_NEW_SERVER ? (
|
||||
<Stack.Screen
|
||||
name='OutsideStack'
|
||||
component={OutsideStack}
|
||||
/>
|
||||
) : null}
|
||||
{root === ROOT_INSIDE && isMasterDetail ? (
|
||||
<Stack.Screen
|
||||
name='MasterDetailStack'
|
||||
component={MasterDetailStack}
|
||||
/>
|
||||
) : null}
|
||||
{root === ROOT_INSIDE && !isMasterDetail ? (
|
||||
<Stack.Screen
|
||||
name='InsideStack'
|
||||
component={InsideStack}
|
||||
/>
|
||||
) : null}
|
||||
{root === ROOT_SET_USERNAME ? (
|
||||
<Stack.Screen
|
||||
name='SetUsernameStack'
|
||||
component={SetUsernameStack}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
</Stack.Navigator>
|
||||
</NavigationContainer>
|
||||
</ActionSheetProvider>
|
||||
</SafeAreaProvider>
|
||||
<NavigationContainer
|
||||
theme={navTheme}
|
||||
ref={Navigation.navigationRef}
|
||||
onStateChange={(state) => {
|
||||
const previousRouteName = Navigation.routeNameRef.current;
|
||||
const currentRouteName = getActiveRouteName(state);
|
||||
if (previousRouteName !== currentRouteName) {
|
||||
setCurrentScreen(currentRouteName);
|
||||
}
|
||||
Navigation.routeNameRef.current = currentRouteName;
|
||||
}}
|
||||
>
|
||||
<Stack.Navigator screenOptions={{ headerShown: false, animationEnabled: false }}>
|
||||
<>
|
||||
{root === ROOT_LOADING || root === ROOT_BACKGROUND ? (
|
||||
<Stack.Screen
|
||||
name='AuthLoading'
|
||||
component={AuthLoadingView}
|
||||
/>
|
||||
) : null}
|
||||
{root === ROOT_OUTSIDE || root === ROOT_NEW_SERVER ? (
|
||||
<Stack.Screen
|
||||
name='OutsideStack'
|
||||
component={OutsideStack}
|
||||
/>
|
||||
) : null}
|
||||
{root === ROOT_INSIDE && isMasterDetail ? (
|
||||
<Stack.Screen
|
||||
name='MasterDetailStack'
|
||||
component={MasterDetailStack}
|
||||
/>
|
||||
) : null}
|
||||
{root === ROOT_INSIDE && !isMasterDetail ? (
|
||||
<Stack.Screen
|
||||
name='InsideStack'
|
||||
component={InsideStack}
|
||||
/>
|
||||
) : null}
|
||||
{root === ROOT_SET_USERNAME ? (
|
||||
<Stack.Screen
|
||||
name='SetUsernameStack'
|
||||
component={SetUsernameStack}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
</Stack.Navigator>
|
||||
</NavigationContainer>
|
||||
);
|
||||
});
|
||||
const mapStateToProps = state => ({
|
||||
|
|
|
@ -51,7 +51,6 @@ export const LOGOUT = 'LOGOUT'; // logout is always success
|
|||
export const SNIPPETED_MESSAGES = createRequestTypes('SNIPPETED_MESSAGES', ['OPEN', 'READY', 'CLOSE', 'MESSAGES_RECEIVED']);
|
||||
export const DEEP_LINKING = createRequestTypes('DEEP_LINKING', ['OPEN']);
|
||||
export const SORT_PREFERENCES = createRequestTypes('SORT_PREFERENCES', ['SET_ALL', 'SET']);
|
||||
export const NOTIFICATION = createRequestTypes('NOTIFICATION', ['RECEIVED', 'REMOVE']);
|
||||
export const TOGGLE_CRASH_REPORT = 'TOGGLE_CRASH_REPORT';
|
||||
export const SET_CUSTOM_EMOJIS = 'SET_CUSTOM_EMOJIS';
|
||||
export const SET_ACTIVE_USERS = 'SET_ACTIVE_USERS';
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
import { NOTIFICATION } from './actionsTypes';
|
||||
|
||||
export function notificationReceived(params) {
|
||||
return {
|
||||
type: NOTIFICATION.RECEIVED,
|
||||
payload: {
|
||||
title: params.title,
|
||||
avatar: params.avatar,
|
||||
message: params.text,
|
||||
payload: params.payload
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function removeNotification() {
|
||||
return {
|
||||
type: NOTIFICATION.REMOVE
|
||||
};
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
import React from 'react';
|
||||
import { StyleSheet, View, Text } from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
import Touchable from 'react-native-platform-touchable';
|
||||
import { connect } from 'react-redux';
|
||||
import { Notifier } from 'react-native-notifier';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { useDeviceOrientation } from '@react-native-community/hooks';
|
||||
|
||||
import Avatar from '../Avatar';
|
||||
import { CustomIcon } from '../../lib/Icons';
|
||||
import sharedStyles from '../../views/Styles';
|
||||
import { themes } from '../../constants/colors';
|
||||
import { useTheme } from '../../theme';
|
||||
import { getUserSelector } from '../../selectors/login';
|
||||
import { ROW_HEIGHT } from '../../presentation/RoomItem';
|
||||
import { goRoom } from '../../utils/goRoom';
|
||||
import Navigation from '../../lib/Navigation';
|
||||
|
||||
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',
|
||||
marginHorizontal: 10,
|
||||
borderWidth: StyleSheet.hairlineWidth,
|
||||
borderRadius: 4
|
||||
},
|
||||
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
|
||||
},
|
||||
small: {
|
||||
width: '50%',
|
||||
alignSelf: 'center'
|
||||
}
|
||||
});
|
||||
|
||||
const hideNotification = () => Notifier.hideNotification();
|
||||
|
||||
const NotifierComponent = React.memo(({
|
||||
baseUrl, user, notification, isMasterDetail
|
||||
}) => {
|
||||
const { theme } = useTheme();
|
||||
const insets = useSafeAreaInsets();
|
||||
const { landscape } = useDeviceOrientation();
|
||||
|
||||
const { id: userId, token } = user;
|
||||
const { text, 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;
|
||||
|
||||
const onPress = () => {
|
||||
const { rid, prid } = payload;
|
||||
if (!rid) {
|
||||
return;
|
||||
}
|
||||
const item = {
|
||||
rid, name: title, t: type, prid
|
||||
};
|
||||
|
||||
if (isMasterDetail) {
|
||||
Navigation.navigate('DrawerNavigator');
|
||||
}
|
||||
goRoom({ item, isMasterDetail });
|
||||
hideNotification();
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={[
|
||||
styles.container,
|
||||
(isMasterDetail || landscape) && styles.small,
|
||||
{
|
||||
backgroundColor: themes[theme].focusedBackground,
|
||||
borderColor: themes[theme].separatorColor,
|
||||
marginTop: insets.top
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Touchable
|
||||
style={styles.content}
|
||||
onPress={onPress}
|
||||
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}>{text}</Text>
|
||||
</View>
|
||||
</>
|
||||
</Touchable>
|
||||
<Touchable
|
||||
onPress={hideNotification}
|
||||
hitSlop={BUTTON_HIT_SLOP}
|
||||
background={Touchable.SelectableBackgroundBorderless()}
|
||||
>
|
||||
<CustomIcon name='Cross' style={[styles.close, { color: themes[theme].titleText }]} size={20} />
|
||||
</Touchable>
|
||||
</View>
|
||||
);
|
||||
});
|
||||
|
||||
NotifierComponent.propTypes = {
|
||||
baseUrl: PropTypes.string,
|
||||
user: PropTypes.object,
|
||||
notification: PropTypes.object,
|
||||
isMasterDetail: PropTypes.bool
|
||||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
user: getUserSelector(state),
|
||||
baseUrl: state.server.server,
|
||||
isMasterDetail: state.app.isMasterDetail
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(NotifierComponent);
|
|
@ -0,0 +1,40 @@
|
|||
import React, { memo, useEffect } from 'react';
|
||||
import { NotifierRoot, Notifier, Easing } from 'react-native-notifier';
|
||||
|
||||
import NotifierComponent from './NotifierComponent';
|
||||
import EventEmitter from '../../utils/events';
|
||||
import Navigation from '../../lib/Navigation';
|
||||
import { getActiveRoute } from '../../utils/navigation';
|
||||
|
||||
export const INAPP_NOTIFICATION_EMITTER = 'NotificationInApp';
|
||||
|
||||
const InAppNotification = memo(() => {
|
||||
const show = (notification) => {
|
||||
const { payload } = notification;
|
||||
const state = Navigation.navigationRef.current.getRootState();
|
||||
const route = getActiveRoute(state);
|
||||
if (payload.rid) {
|
||||
if (route?.name === 'RoomView' && route.params?.rid === payload.rid) {
|
||||
return;
|
||||
}
|
||||
Notifier.showNotification({
|
||||
showEasing: Easing.inOut(Easing.quad),
|
||||
Component: NotifierComponent,
|
||||
componentProps: {
|
||||
notification
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
EventEmitter.addEventListener(INAPP_NOTIFICATION_EMITTER, show);
|
||||
return () => {
|
||||
EventEmitter.removeListener(INAPP_NOTIFICATION_EMITTER);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return <NotifierRoot />;
|
||||
});
|
||||
|
||||
export default InAppNotification;
|
43
app/index.js
43
app/index.js
|
@ -5,6 +5,7 @@ import { Provider } from 'react-redux';
|
|||
import RNUserDefaults from 'rn-user-defaults';
|
||||
import { KeyCommandsEmitter } from 'react-native-keycommands';
|
||||
import RNScreens from 'react-native-screens';
|
||||
import { SafeAreaProvider, initialWindowMetrics } from 'react-native-safe-area-context';
|
||||
|
||||
import {
|
||||
defaultTheme,
|
||||
|
@ -30,6 +31,10 @@ import AppContainer from './AppContainer';
|
|||
import TwoFactor from './containers/TwoFactor';
|
||||
import ScreenLockedView from './views/ScreenLockedView';
|
||||
import ChangePasscodeView from './views/ChangePasscodeView';
|
||||
import Toast from './containers/Toast';
|
||||
import InAppNotification from './containers/InAppNotification';
|
||||
import { ActionSheetProvider } from './containers/ActionSheet';
|
||||
|
||||
|
||||
RNScreens.enableScreens();
|
||||
|
||||
|
@ -151,22 +156,28 @@ export default class Root extends React.Component {
|
|||
render() {
|
||||
const { themePreferences, theme } = this.state;
|
||||
return (
|
||||
<AppearanceProvider>
|
||||
<Provider store={store}>
|
||||
<ThemeContext.Provider
|
||||
value={{
|
||||
theme,
|
||||
themePreferences,
|
||||
setTheme: this.setTheme
|
||||
}}
|
||||
>
|
||||
<AppContainer />
|
||||
<TwoFactor />
|
||||
<ScreenLockedView />
|
||||
<ChangePasscodeView />
|
||||
</ThemeContext.Provider>
|
||||
</Provider>
|
||||
</AppearanceProvider>
|
||||
<SafeAreaProvider initialMetrics={initialWindowMetrics}>
|
||||
<AppearanceProvider>
|
||||
<Provider store={store}>
|
||||
<ThemeContext.Provider
|
||||
value={{
|
||||
theme,
|
||||
themePreferences,
|
||||
setTheme: this.setTheme
|
||||
}}
|
||||
>
|
||||
<ActionSheetProvider>
|
||||
<AppContainer />
|
||||
<TwoFactor />
|
||||
<ScreenLockedView />
|
||||
<ChangePasscodeView />
|
||||
<InAppNotification />
|
||||
<Toast />
|
||||
</ActionSheetProvider>
|
||||
</ThemeContext.Provider>
|
||||
</Provider>
|
||||
</AppearanceProvider>
|
||||
</SafeAreaProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,12 +9,12 @@ import log from '../../../utils/log';
|
|||
import random from '../../../utils/random';
|
||||
import store from '../../createStore';
|
||||
import { roomsRequest } from '../../../actions/rooms';
|
||||
import { notificationReceived } from '../../../actions/notification';
|
||||
import { handlePayloadUserInteraction } from '../actions';
|
||||
import buildMessage from '../helpers/buildMessage';
|
||||
import RocketChat from '../../rocketchat';
|
||||
import EventEmmiter from '../../../utils/events';
|
||||
import EventEmitter from '../../../utils/events';
|
||||
import { removedRoom } from '../../../actions/room';
|
||||
import { INAPP_NOTIFICATION_EMITTER } from '../../../containers/InAppNotification';
|
||||
|
||||
const removeListener = listener => listener.stop();
|
||||
|
||||
|
@ -267,7 +267,7 @@ export default function subscribeRooms() {
|
|||
if (data.rid === roomState.rid && roomState.isDeleting) {
|
||||
store.dispatch(removedRoom());
|
||||
} else {
|
||||
EventEmmiter.emit('ROOM_REMOVED', { rid: data.rid });
|
||||
EventEmitter.emit('ROOM_REMOVED', { rid: data.rid });
|
||||
}
|
||||
} catch (e) {
|
||||
log(e);
|
||||
|
@ -320,7 +320,7 @@ export default function subscribeRooms() {
|
|||
} catch (e) {
|
||||
// do nothing
|
||||
}
|
||||
store.dispatch(notificationReceived(notification));
|
||||
EventEmitter.emit(INAPP_NOTIFICATION_EMITTER, notification);
|
||||
}
|
||||
if (/uiInteraction/.test(ev)) {
|
||||
const { type: eventType, ...args } = type;
|
||||
|
|
|
@ -1,248 +0,0 @@
|
|||
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 { 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 { getActiveRoute } from '../../utils/navigation';
|
||||
import Navigation from '../../lib/Navigation';
|
||||
import { goRoom } from '../../utils/goRoom';
|
||||
|
||||
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 = {
|
||||
isMasterDetail: PropTypes.bool,
|
||||
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);
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
const { notification: nextNotification } = nextProps;
|
||||
const {
|
||||
notification: { payload }, window, theme
|
||||
} = this.props;
|
||||
if (nextProps.theme !== theme) {
|
||||
return true;
|
||||
}
|
||||
if (!equal(nextNotification.payload, payload)) {
|
||||
return true;
|
||||
}
|
||||
if (nextProps.window.width !== window.width) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const { notification: { payload } } = this.props;
|
||||
const { notification: { payload: prevPayload } } = prevProps;
|
||||
if (!equal(prevPayload, payload)) {
|
||||
const state = Navigation.navigationRef.current.getRootState();
|
||||
const route = getActiveRoute(state);
|
||||
if (payload.rid) {
|
||||
if (route?.name === 'RoomView' && route.params?.rid === payload.rid) {
|
||||
return;
|
||||
}
|
||||
this.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
goToRoom = () => {
|
||||
const { notification, isMasterDetail, 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;
|
||||
const item = {
|
||||
rid, name: title, t: type, prid, baseUrl
|
||||
};
|
||||
if (isMasterDetail) {
|
||||
Navigation.navigate('DrawerNavigator');
|
||||
}
|
||||
goRoom({ item, isMasterDetail });
|
||||
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='cancel' style={[styles.close, { color: themes[theme].titleText }]} size={20} />
|
||||
</TouchableOpacity>
|
||||
</Animated.View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
user: getUserSelector(state),
|
||||
baseUrl: state.server.server,
|
||||
notification: state.notification,
|
||||
isMasterDetail: PropTypes.bool
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
removeNotification: () => dispatch(removeNotificationAction())
|
||||
});
|
||||
|
||||
export default responsive(connect(mapStateToProps, mapDispatchToProps)(withTheme(NotificationBadge)));
|
|
@ -9,7 +9,6 @@ import selectedUsers from './selectedUsers';
|
|||
import createChannel from './createChannel';
|
||||
import app from './app';
|
||||
import sortPreferences from './sortPreferences';
|
||||
import notification from './notification';
|
||||
import share from './share';
|
||||
import crashReport from './crashReport';
|
||||
import customEmojis from './customEmojis';
|
||||
|
@ -29,7 +28,6 @@ export default combineReducers({
|
|||
room,
|
||||
rooms,
|
||||
sortPreferences,
|
||||
notification,
|
||||
share,
|
||||
crashReport,
|
||||
customEmojis,
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
import { NOTIFICATION } from '../actions/actionsTypes';
|
||||
|
||||
const initialState = {
|
||||
message: '',
|
||||
payload: {
|
||||
type: 'p',
|
||||
name: '',
|
||||
rid: ''
|
||||
}
|
||||
};
|
||||
|
||||
export default function notification(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case NOTIFICATION.RECEIVED:
|
||||
return {
|
||||
...state,
|
||||
...action.payload
|
||||
};
|
||||
case NOTIFICATION.REMOVE:
|
||||
return initialState;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { createStackNavigator } from '@react-navigation/stack';
|
||||
import { createDrawerNavigator } from '@react-navigation/drawer';
|
||||
|
||||
|
@ -7,9 +6,7 @@ import { ThemeContext } from '../theme';
|
|||
import {
|
||||
defaultHeader, themedHeader, ModalAnimation, StackAnimation
|
||||
} from '../utils/navigation';
|
||||
import Toast from '../containers/Toast';
|
||||
import Sidebar from '../views/SidebarView';
|
||||
import NotificationBadge from '../notifications/inApp';
|
||||
|
||||
// Chats Stack
|
||||
import RoomView from '../views/RoomView';
|
||||
|
@ -320,16 +317,4 @@ const InsideStackNavigator = () => {
|
|||
);
|
||||
};
|
||||
|
||||
const RootInsideStack = ({ navigation, route }) => (
|
||||
<>
|
||||
<InsideStackNavigator navigation={navigation} />
|
||||
<NotificationBadge navigation={navigation} route={route} />
|
||||
<Toast />
|
||||
</>
|
||||
);
|
||||
RootInsideStack.propTypes = {
|
||||
navigation: PropTypes.object,
|
||||
route: PropTypes.object
|
||||
};
|
||||
|
||||
export default RootInsideStack;
|
||||
export default InsideStackNavigator;
|
||||
|
|
|
@ -8,8 +8,6 @@ import { ThemeContext } from '../../theme';
|
|||
import {
|
||||
defaultHeader, themedHeader, StackAnimation, FadeFromCenterModal
|
||||
} from '../../utils/navigation';
|
||||
import Toast from '../../containers/Toast';
|
||||
import NotificationBadge from '../../notifications/inApp';
|
||||
import { ModalContainer } from './ModalContainer';
|
||||
|
||||
// Chats Stack
|
||||
|
@ -292,16 +290,4 @@ const InsideStackNavigator = React.memo(() => {
|
|||
);
|
||||
});
|
||||
|
||||
const RootInsideStack = React.memo(({ navigation, route }) => (
|
||||
<>
|
||||
<InsideStackNavigator navigation={navigation} />
|
||||
<NotificationBadge navigation={navigation} route={route} />
|
||||
<Toast />
|
||||
</>
|
||||
));
|
||||
RootInsideStack.propTypes = {
|
||||
navigation: PropTypes.object,
|
||||
route: PropTypes.object
|
||||
};
|
||||
|
||||
export default RootInsideStack;
|
||||
export default InsideStackNavigator;
|
||||
|
|
|
@ -83,6 +83,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.3.1",
|
||||
"react-native-orientation-locker": "1.1.8",
|
||||
"react-native-picker-select": "7.0.0",
|
||||
"react-native-platform-touchable": "^1.1.1",
|
||||
|
|
|
@ -11895,6 +11895,11 @@ react-native-notifications@2.1.7:
|
|||
core-js "^1.0.0"
|
||||
uuid "^2.0.3"
|
||||
|
||||
react-native-notifier@^1.3.1:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/react-native-notifier/-/react-native-notifier-1.3.1.tgz#a878c82c8ee99b04d57818401b1f084232729afd"
|
||||
integrity sha512-w7KOTF5WOYzbhCXQHz6p9tbosOVxhOW+Sh7VAdIuW6r7PSoryRNkF4P6Bzq1+2NPtMK7L6xnojCdKJ+nVnwh+A==
|
||||
|
||||
react-native-orientation-locker@1.1.8:
|
||||
version "1.1.8"
|
||||
resolved "https://registry.yarnpkg.com/react-native-orientation-locker/-/react-native-orientation-locker-1.1.8.tgz#45d1c9e002496b8d286ec8932d6e3e7d341f9c85"
|
||||
|
|
Loading…
Reference in New Issue