forked from Ivasoft/mattermost-mobile
* upgrade reanimated * update devDependencies * upgrade react-intl * update react-native and some dependencies * update react-native-permissions * update RN * use Share sheet for Report a problem * update Sentry * remove step to downloadWebRTC * update detox deps * feedback review
140 lines
3.7 KiB
TypeScript
140 lines
3.7 KiB
TypeScript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
import * as React from 'react';
|
|
import {Animated, Platform, type StyleProp, StyleSheet, type TextStyle} from 'react-native';
|
|
|
|
import {useTheme} from '@context/theme';
|
|
|
|
type Props = {
|
|
backgroundColor?: string;
|
|
borderColor?: string;
|
|
color?: string;
|
|
style?: Animated.WithAnimatedValue<StyleProp<TextStyle>>;
|
|
testID?: string;
|
|
type?: 'Normal' | 'Small';
|
|
|
|
/**
|
|
* Value of the `Badge` for unread dot use a negative value.
|
|
*/
|
|
value: number;
|
|
visible: boolean;
|
|
};
|
|
|
|
export default function Badge({
|
|
borderColor,
|
|
color,
|
|
visible = true,
|
|
type = 'Normal',
|
|
value,
|
|
style,
|
|
testID,
|
|
...rest
|
|
}: Props) {
|
|
const [opacity] = React.useState(() => new Animated.Value(visible ? 1 : 0));
|
|
const [rendered, setRendered] = React.useState(Boolean(visible));
|
|
|
|
const theme = useTheme();
|
|
|
|
React.useEffect(() => {
|
|
if (!rendered) {
|
|
return;
|
|
}
|
|
|
|
Animated.timing(opacity, {
|
|
toValue: visible ? 1 : 0,
|
|
duration: 200,
|
|
useNativeDriver: true,
|
|
}).start(({finished}) => {
|
|
if (finished && !visible) {
|
|
setRendered(false);
|
|
}
|
|
});
|
|
}, [opacity, rendered, visible]);
|
|
|
|
if (visible && !rendered) {
|
|
setRendered(true);
|
|
}
|
|
|
|
if (!visible && !rendered) {
|
|
return null;
|
|
}
|
|
|
|
// @ts-expect-error: backgroundColor definitely exists
|
|
const {backgroundColor = rest.backgroundColor || theme.mentionBg, ...restStyle} =
|
|
StyleSheet.flatten(style) || {};
|
|
const textColor = color || theme.mentionColor;
|
|
let lineHeight = Platform.select({android: 21, ios: 16.5});
|
|
let fontSize = 12;
|
|
let size = value < 0 ? 12 : 22;
|
|
let minWidth = value < 0 ? size : 26;
|
|
let additionalStyle;
|
|
if (type === 'Small') {
|
|
size = value < 0 ? 12 : 20;
|
|
lineHeight = Platform.select({android: 19, ios: 15});
|
|
fontSize = 11;
|
|
minWidth = value < 0 ? size : 24;
|
|
}
|
|
const borderRadius = size / 2;
|
|
|
|
let badge: string = value?.toString();
|
|
if (value < 0) {
|
|
badge = '';
|
|
additionalStyle = {paddingHorizontal: 0};
|
|
} else if (value < 99) {
|
|
badge = value.toString();
|
|
additionalStyle = {paddingHorizontal: 5};
|
|
} else {
|
|
badge = '99+';
|
|
additionalStyle = {paddingLeft: 4, paddingRight: 3};
|
|
}
|
|
|
|
return (
|
|
<Animated.Text
|
|
numberOfLines={1}
|
|
style={[
|
|
{
|
|
opacity,
|
|
transform: [
|
|
{
|
|
scale: opacity.interpolate({
|
|
inputRange: [0, 1],
|
|
outputRange: [0.5, 1],
|
|
}),
|
|
},
|
|
],
|
|
borderColor,
|
|
backgroundColor,
|
|
color: textColor,
|
|
fontSize,
|
|
lineHeight,
|
|
minWidth,
|
|
height: size,
|
|
borderRadius,
|
|
},
|
|
styles.container,
|
|
additionalStyle,
|
|
restStyle,
|
|
]}
|
|
testID={testID}
|
|
{...rest}
|
|
>
|
|
{badge}
|
|
</Animated.Text>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
position: 'absolute',
|
|
top: -1,
|
|
left: 15,
|
|
alignSelf: 'flex-end',
|
|
textAlign: 'center',
|
|
paddingHorizontal: 4,
|
|
overflow: 'hidden',
|
|
borderWidth: 2,
|
|
fontFamily: 'OpenSans-Bold',
|
|
},
|
|
});
|