Some checks failed
Close stale issues and PRs / stale (push) Has been cancelled
223 lines
6.8 KiB
TypeScript
223 lines
6.8 KiB
TypeScript
import React, { useCallback, useContext, useEffect, useRef } from 'react';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { Animated, Text, TextStyle, View, ViewStyle } from 'react-native';
|
|
|
|
import Icon from '../../../base/icons/components/Icon';
|
|
import {
|
|
IconCloseLarge,
|
|
IconInfoCircle,
|
|
IconUsers,
|
|
IconWarning
|
|
} from '../../../base/icons/svg';
|
|
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
|
|
import Button from '../../../base/ui/components/native/Button';
|
|
import IconButton from '../../../base/ui/components/native/IconButton';
|
|
import { BUTTON_MODES, BUTTON_TYPES } from '../../../base/ui/constants.native';
|
|
import { CHAR_LIMIT } from '../../../chat/constants';
|
|
import { replaceNonUnicodeEmojis } from '../../../chat/functions';
|
|
import { NOTIFICATION_ICON, NOTIFICATION_TYPE } from '../../constants';
|
|
import { INotificationProps } from '../../types';
|
|
import { NotificationsTransitionContext } from '../NotificationsTransition';
|
|
|
|
import styles from './styles';
|
|
|
|
|
|
/**
|
|
* Secondary colors for notification icons.
|
|
*
|
|
* @type {{error, info, normal, success, warning}}
|
|
*/
|
|
|
|
const ICON_COLOR = {
|
|
error: BaseTheme.palette.iconError,
|
|
normal: BaseTheme.palette.iconNormal,
|
|
success: BaseTheme.palette.iconSuccess,
|
|
warning: BaseTheme.palette.iconWarning
|
|
};
|
|
|
|
|
|
export interface IProps extends INotificationProps {
|
|
_participants: ArrayLike<any>;
|
|
onDismissed: Function;
|
|
}
|
|
|
|
const Notification = ({
|
|
appearance = NOTIFICATION_TYPE.NORMAL,
|
|
customActionHandler,
|
|
customActionNameKey,
|
|
customActionType,
|
|
description,
|
|
descriptionArguments,
|
|
descriptionKey,
|
|
icon,
|
|
onDismissed,
|
|
title,
|
|
titleArguments,
|
|
titleKey,
|
|
uid
|
|
}: IProps) => {
|
|
const { t } = useTranslation();
|
|
const notificationOpacityAnimation = useRef(new Animated.Value(0)).current;
|
|
const { unmounting } = useContext(NotificationsTransitionContext);
|
|
|
|
useEffect(() => {
|
|
Animated.timing(
|
|
notificationOpacityAnimation,
|
|
{
|
|
toValue: 1,
|
|
duration: 200,
|
|
useNativeDriver: true
|
|
})
|
|
.start();
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (unmounting.get(uid ?? '')) {
|
|
Animated.timing(
|
|
notificationOpacityAnimation,
|
|
{
|
|
toValue: 0,
|
|
duration: 200,
|
|
useNativeDriver: true
|
|
})
|
|
.start();
|
|
}
|
|
}, [ unmounting ]);
|
|
|
|
const onDismiss = useCallback(() => {
|
|
onDismissed(uid);
|
|
}, [ onDismissed, uid ]);
|
|
|
|
const mapAppearanceToButtons = () => {
|
|
if (customActionNameKey?.length && customActionHandler?.length && customActionType?.length) {
|
|
return customActionNameKey?.map((customAction: string, index: number) => (
|
|
<Button
|
|
accessibilityLabel = { customAction }
|
|
key = { index }
|
|
labelKey = { customAction }
|
|
mode = { BUTTON_MODES.TEXT }
|
|
// eslint-disable-next-line react/jsx-no-bind
|
|
onClick = { () => {
|
|
if (customActionHandler[index]()) {
|
|
onDismiss();
|
|
}
|
|
} }
|
|
style = { styles.btn }
|
|
|
|
// @ts-ignore
|
|
type = { customActionType[index] } />
|
|
));
|
|
}
|
|
|
|
return [];
|
|
};
|
|
|
|
const getIcon = () => {
|
|
let src;
|
|
|
|
switch (icon || appearance) {
|
|
case NOTIFICATION_ICON.PARTICIPANT:
|
|
src = IconInfoCircle;
|
|
break;
|
|
case NOTIFICATION_ICON.PARTICIPANTS:
|
|
src = IconUsers;
|
|
break;
|
|
case NOTIFICATION_ICON.WARNING:
|
|
src = IconWarning;
|
|
break;
|
|
default:
|
|
src = IconInfoCircle;
|
|
break;
|
|
}
|
|
|
|
return src;
|
|
};
|
|
|
|
const _getDescription = () => {
|
|
const descriptionArray = [];
|
|
|
|
descriptionKey
|
|
&& descriptionArray.push(t(descriptionKey, descriptionArguments));
|
|
|
|
description && descriptionArray.push(description);
|
|
|
|
return descriptionArray;
|
|
};
|
|
|
|
// eslint-disable-next-line react/no-multi-comp
|
|
const _renderContent = () => {
|
|
const titleText = title || (titleKey && t(titleKey, titleArguments));
|
|
const descriptionArray = _getDescription();
|
|
|
|
if (descriptionArray?.length) {
|
|
return (
|
|
<>
|
|
<Text
|
|
numberOfLines = { 1 }
|
|
style = { styles.contentTextTitleDescription as TextStyle }>
|
|
{ titleText }
|
|
</Text>
|
|
{
|
|
descriptionArray.map((line, index) => (
|
|
<Text
|
|
key = { index }
|
|
style = { styles.contentText }>
|
|
{ line.length >= CHAR_LIMIT ? line : replaceNonUnicodeEmojis(line) }
|
|
</Text>
|
|
))
|
|
}
|
|
</>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Text
|
|
numberOfLines = { 1 }
|
|
style = { styles.contentTextTitle as TextStyle }>
|
|
{ titleText }
|
|
</Text>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<Animated.View
|
|
pointerEvents = 'box-none'
|
|
style = { [
|
|
_getDescription()?.length
|
|
? styles.notificationWithDescription
|
|
: styles.notification,
|
|
{
|
|
opacity: notificationOpacityAnimation
|
|
}
|
|
] as ViewStyle[] }>
|
|
<View
|
|
style = { (icon === NOTIFICATION_ICON.PARTICIPANTS
|
|
? styles.contentColumn
|
|
: styles.interactiveContentColumn) as ViewStyle }>
|
|
<View style = { styles.iconContainer as ViewStyle }>
|
|
<Icon
|
|
color = { ICON_COLOR[appearance as keyof typeof ICON_COLOR] }
|
|
size = { 24 }
|
|
src = { getIcon() } />
|
|
</View>
|
|
<View
|
|
pointerEvents = 'box-none'
|
|
style = { styles.contentContainer }>
|
|
{ _renderContent() }
|
|
</View>
|
|
<View style = { styles.btnContainer as ViewStyle }>
|
|
{ mapAppearanceToButtons() }
|
|
</View>
|
|
</View>
|
|
<IconButton
|
|
color = { BaseTheme.palette.icon04 }
|
|
onPress = { onDismiss }
|
|
src = { IconCloseLarge }
|
|
type = { BUTTON_TYPES.TERTIARY } />
|
|
</Animated.View>
|
|
);
|
|
|
|
};
|
|
|
|
export default Notification;
|