Merge branch 'develop' into update.enable-multiline-android-tablets
This commit is contained in:
commit
ac61087940
|
@ -5,15 +5,15 @@ import { themes } from '../constants/colors';
|
|||
import sharedStyles from '../views/Styles';
|
||||
import scrollPersistTaps from '../utils/scrollPersistTaps';
|
||||
import KeyboardView from '../presentation/KeyboardView';
|
||||
import { useTheme } from '../theme';
|
||||
import StatusBar from './StatusBar';
|
||||
import AppVersion from './AppVersion';
|
||||
import { isTablet } from '../utils/deviceInfo';
|
||||
import SafeAreaView from './SafeAreaView';
|
||||
|
||||
interface IFormContainer extends ScrollViewProps {
|
||||
theme: string;
|
||||
testID: string;
|
||||
children: React.ReactNode;
|
||||
children: React.ReactElement | React.ReactElement[] | null;
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
|
@ -22,27 +22,31 @@ const styles = StyleSheet.create({
|
|||
}
|
||||
});
|
||||
|
||||
export const FormContainerInner = ({ children }: { children: React.ReactNode }): JSX.Element => (
|
||||
export const FormContainerInner = ({ children }: { children: (React.ReactElement | null)[] }) => (
|
||||
<View style={[sharedStyles.container, isTablet && sharedStyles.tabletScreenContent]}>{children}</View>
|
||||
);
|
||||
|
||||
const FormContainer = ({ children, theme, testID, ...props }: IFormContainer): JSX.Element => (
|
||||
<KeyboardView
|
||||
style={{ backgroundColor: themes[theme].backgroundColor }}
|
||||
contentContainerStyle={sharedStyles.container}
|
||||
keyboardVerticalOffset={128}>
|
||||
<StatusBar />
|
||||
<ScrollView
|
||||
style={sharedStyles.container}
|
||||
contentContainerStyle={[sharedStyles.containerScrollView, styles.scrollView]}
|
||||
{...scrollPersistTaps}
|
||||
{...props}>
|
||||
<SafeAreaView testID={testID} style={{ backgroundColor: themes[theme].backgroundColor }}>
|
||||
{children}
|
||||
<AppVersion theme={theme} />
|
||||
</SafeAreaView>
|
||||
</ScrollView>
|
||||
</KeyboardView>
|
||||
);
|
||||
const FormContainer = ({ children, testID, ...props }: IFormContainer) => {
|
||||
const { theme } = useTheme();
|
||||
|
||||
return (
|
||||
<KeyboardView
|
||||
style={{ backgroundColor: themes[theme].backgroundColor }}
|
||||
contentContainerStyle={sharedStyles.container}
|
||||
keyboardVerticalOffset={128}>
|
||||
<StatusBar />
|
||||
<ScrollView
|
||||
style={sharedStyles.container}
|
||||
contentContainerStyle={[sharedStyles.containerScrollView, styles.scrollView]}
|
||||
{...scrollPersistTaps}
|
||||
{...props}>
|
||||
<SafeAreaView testID={testID} style={{ backgroundColor: themes[theme].backgroundColor }}>
|
||||
{children}
|
||||
<AppVersion theme={theme} />
|
||||
</SafeAreaView>
|
||||
</ScrollView>
|
||||
</KeyboardView>
|
||||
);
|
||||
};
|
||||
|
||||
export default FormContainer;
|
||||
|
|
|
@ -11,7 +11,7 @@ const styles = StyleSheet.create({
|
|||
});
|
||||
|
||||
interface IListContainer {
|
||||
children: React.ReactNode;
|
||||
children: (React.ReactElement | null)[] | React.ReactElement | null;
|
||||
testID?: string;
|
||||
}
|
||||
const ListContainer = React.memo(({ children, ...props }: IListContainer) => (
|
||||
|
|
|
@ -25,6 +25,7 @@ interface IListHeader {
|
|||
|
||||
const ListHeader = React.memo(({ title, translateTitle = true }: IListHeader) => {
|
||||
const { theme } = useTheme();
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={[styles.title, { color: themes[theme].infoText }]} numberOfLines={1}>
|
||||
|
|
|
@ -3,11 +3,10 @@ import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native';
|
|||
|
||||
import { themes } from '../../constants/colors';
|
||||
import { CustomIcon } from '../../lib/Icons';
|
||||
import { withTheme } from '../../theme';
|
||||
import { useTheme } from '../../theme';
|
||||
import { ICON_SIZE } from './constants';
|
||||
|
||||
interface IListIcon {
|
||||
theme?: string;
|
||||
name: string;
|
||||
color?: string;
|
||||
style?: StyleProp<ViewStyle>;
|
||||
|
@ -21,12 +20,16 @@ const styles = StyleSheet.create({
|
|||
}
|
||||
});
|
||||
|
||||
const ListIcon = React.memo(({ theme, name, color, style, testID }: IListIcon) => (
|
||||
<View style={[styles.icon, style]}>
|
||||
<CustomIcon name={name} color={color ?? themes[theme!].auxiliaryText} size={ICON_SIZE} testID={testID} />
|
||||
</View>
|
||||
));
|
||||
const ListIcon = React.memo(({ name, color, style, testID }: IListIcon) => {
|
||||
const { theme } = useTheme();
|
||||
|
||||
return (
|
||||
<View style={[styles.icon, style]}>
|
||||
<CustomIcon name={name} color={color ?? themes[theme].auxiliaryText} size={ICON_SIZE} testID={testID} />
|
||||
</View>
|
||||
);
|
||||
});
|
||||
|
||||
ListIcon.displayName = 'List.Icon';
|
||||
|
||||
export default withTheme(ListIcon);
|
||||
export default ListIcon;
|
||||
|
|
|
@ -4,11 +4,11 @@ import { I18nManager, StyleSheet, Text, View } from 'react-native';
|
|||
import Touch from '../../utils/touch';
|
||||
import { themes } from '../../constants/colors';
|
||||
import sharedStyles from '../../views/Styles';
|
||||
import { withTheme } from '../../theme';
|
||||
import { useTheme } from '../../theme';
|
||||
import I18n from '../../i18n';
|
||||
import { Icon } from '.';
|
||||
import { BASE_HEIGHT, ICON_SIZE, PADDING_HORIZONTAL } from './constants';
|
||||
import { withDimensions } from '../../dimensions';
|
||||
import { useDimensions } from '../../dimensions';
|
||||
import { CustomIcon } from '../../lib/Icons';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
|
@ -59,13 +59,12 @@ interface IListItemContent {
|
|||
left?: () => JSX.Element | null;
|
||||
right?: () => JSX.Element | null;
|
||||
disabled?: boolean;
|
||||
theme: string;
|
||||
testID?: string;
|
||||
theme?: string;
|
||||
color?: string;
|
||||
translateTitle?: boolean;
|
||||
translateSubtitle?: boolean;
|
||||
showActionIndicator?: boolean;
|
||||
fontScale?: number;
|
||||
alert?: boolean;
|
||||
}
|
||||
|
||||
|
@ -78,78 +77,85 @@ const Content = React.memo(
|
|||
left,
|
||||
right,
|
||||
color,
|
||||
theme,
|
||||
fontScale,
|
||||
alert,
|
||||
translateTitle = true,
|
||||
translateSubtitle = true,
|
||||
showActionIndicator = false
|
||||
}: IListItemContent) => (
|
||||
<View style={[styles.container, disabled && styles.disabled, { height: BASE_HEIGHT * fontScale! }]} testID={testID}>
|
||||
{left ? <View style={styles.leftContainer}>{left()}</View> : null}
|
||||
<View style={styles.textContainer}>
|
||||
<View style={styles.textAlertContainer}>
|
||||
<Text style={[styles.title, { color: color || themes[theme!].titleText }]} numberOfLines={1}>
|
||||
{translateTitle ? I18n.t(title) : title}
|
||||
</Text>
|
||||
{alert ? (
|
||||
<CustomIcon style={[styles.alertIcon, { color: themes[theme!].dangerColor }]} size={ICON_SIZE} name='info' />
|
||||
showActionIndicator = false,
|
||||
theme
|
||||
}: IListItemContent) => {
|
||||
const { fontScale } = useDimensions();
|
||||
|
||||
return (
|
||||
<View style={[styles.container, disabled && styles.disabled, { height: BASE_HEIGHT * fontScale }]} testID={testID}>
|
||||
{left ? <View style={styles.leftContainer}>{left()}</View> : null}
|
||||
<View style={styles.textContainer}>
|
||||
<View style={styles.textAlertContainer}>
|
||||
<Text style={[styles.title, { color: color || themes[theme].titleText }]} numberOfLines={1}>
|
||||
{translateTitle ? I18n.t(title) : title}
|
||||
</Text>
|
||||
{alert ? (
|
||||
<CustomIcon style={[styles.alertIcon, { color: themes[theme].dangerColor }]} size={ICON_SIZE} name='info' />
|
||||
) : null}
|
||||
</View>
|
||||
{subtitle ? (
|
||||
<Text style={[styles.subtitle, { color: themes[theme].auxiliaryText }]} numberOfLines={1}>
|
||||
{translateSubtitle ? I18n.t(subtitle) : subtitle}
|
||||
</Text>
|
||||
) : null}
|
||||
</View>
|
||||
{subtitle ? (
|
||||
<Text style={[styles.subtitle, { color: themes[theme!].auxiliaryText }]} numberOfLines={1}>
|
||||
{translateSubtitle ? I18n.t(subtitle) : subtitle}
|
||||
</Text>
|
||||
{right || showActionIndicator ? (
|
||||
<View style={styles.rightContainer}>
|
||||
{right ? right() : null}
|
||||
{showActionIndicator ? <Icon name='chevron-right' style={styles.actionIndicator} /> : null}
|
||||
</View>
|
||||
) : null}
|
||||
</View>
|
||||
{right || showActionIndicator ? (
|
||||
<View style={styles.rightContainer}>
|
||||
{right ? right() : null}
|
||||
{showActionIndicator ? <Icon name='chevron-right' style={styles.actionIndicator} /> : null}
|
||||
</View>
|
||||
) : null}
|
||||
</View>
|
||||
)
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
interface IListButtonPress {
|
||||
onPress?: Function;
|
||||
interface IListButtonPress extends IListItemButton {
|
||||
onPress: Function;
|
||||
}
|
||||
|
||||
interface IListItemButton extends IListButtonPress {
|
||||
interface IListItemButton {
|
||||
title?: string;
|
||||
disabled?: boolean;
|
||||
theme?: string;
|
||||
theme: string;
|
||||
backgroundColor?: string;
|
||||
underlayColor?: string;
|
||||
}
|
||||
|
||||
const Button = React.memo<IListItemButton>(({ onPress, backgroundColor, underlayColor, ...props }: IListItemButton) => (
|
||||
const Button = React.memo(({ onPress, backgroundColor, underlayColor, ...props }: IListButtonPress) => (
|
||||
<Touch
|
||||
onPress={() => onPress!(props.title)}
|
||||
style={{ backgroundColor: backgroundColor || themes[props.theme!].backgroundColor }}
|
||||
onPress={() => onPress(props.title)}
|
||||
style={{ backgroundColor: backgroundColor || themes[props.theme].backgroundColor }}
|
||||
underlayColor={underlayColor}
|
||||
enabled={!props.disabled}
|
||||
theme={props.theme!}>
|
||||
theme={props.theme}>
|
||||
<Content {...props} />
|
||||
</Touch>
|
||||
));
|
||||
|
||||
interface IListItem extends IListItemContent, IListItemButton {
|
||||
interface IListItem extends Omit<IListItemContent, 'theme'>, Omit<IListItemButton, 'theme'> {
|
||||
backgroundColor?: string;
|
||||
onPress?: Function;
|
||||
}
|
||||
|
||||
const ListItem = React.memo<IListItem>(({ ...props }: IListItem) => {
|
||||
const ListItem = React.memo(({ ...props }: IListItem) => {
|
||||
const { theme } = useTheme();
|
||||
|
||||
if (props.onPress) {
|
||||
return <Button {...props} />;
|
||||
const { onPress } = props;
|
||||
return <Button {...props} theme={theme} onPress={onPress} />;
|
||||
}
|
||||
return (
|
||||
<View style={{ backgroundColor: props.backgroundColor || themes[props.theme!].backgroundColor }}>
|
||||
<Content {...props} />
|
||||
<View style={{ backgroundColor: props.backgroundColor || themes[theme].backgroundColor }}>
|
||||
<Content {...props} theme={theme} />
|
||||
</View>
|
||||
);
|
||||
});
|
||||
|
||||
ListItem.displayName = 'List.Item';
|
||||
|
||||
export default withTheme(withDimensions(ListItem));
|
||||
export default ListItem;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import React from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
|
||||
import { withTheme } from '../../theme';
|
||||
import { Header } from '.';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
|
@ -11,7 +10,7 @@ const styles = StyleSheet.create({
|
|||
});
|
||||
|
||||
interface IListSection {
|
||||
children: React.ReactNode;
|
||||
children: (React.ReactElement | null)[] | React.ReactElement | null;
|
||||
title?: string;
|
||||
translateTitle?: boolean;
|
||||
}
|
||||
|
@ -25,4 +24,4 @@ const ListSection = React.memo(({ children, title, translateTitle }: IListSectio
|
|||
|
||||
ListSection.displayName = 'List.Section';
|
||||
|
||||
export default withTheme(ListSection);
|
||||
export default ListSection;
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import { StyleSheet, View, ViewStyle } from 'react-native';
|
||||
|
||||
import { themes } from '../../constants/colors';
|
||||
import { withTheme } from '../../theme';
|
||||
import { useTheme } from '../../theme';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
separator: {
|
||||
|
@ -12,13 +12,14 @@ const styles = StyleSheet.create({
|
|||
|
||||
interface IListSeparator {
|
||||
style?: ViewStyle;
|
||||
theme?: string;
|
||||
}
|
||||
|
||||
const ListSeparator = React.memo(({ style, theme }: IListSeparator) => (
|
||||
<View style={[styles.separator, style, { backgroundColor: themes[theme!].separatorColor }]} />
|
||||
));
|
||||
const ListSeparator = React.memo(({ style }: IListSeparator) => {
|
||||
const { theme } = useTheme();
|
||||
|
||||
return <View style={[styles.separator, style, { backgroundColor: themes[theme].separatorColor }]} />;
|
||||
});
|
||||
|
||||
ListSeparator.displayName = 'List.Separator';
|
||||
|
||||
export default withTheme(ListSeparator);
|
||||
export default ListSeparator;
|
||||
|
|
|
@ -7,6 +7,7 @@ import { themes } from '../../constants/colors';
|
|||
import { MarkdownPreview } from '../markdown';
|
||||
import RoomTypeIcon from '../RoomTypeIcon';
|
||||
import { withTheme } from '../../theme';
|
||||
import { TUserStatus } from '../../definitions';
|
||||
|
||||
const HIT_SLOP = {
|
||||
top: 5,
|
||||
|
@ -67,7 +68,7 @@ interface IRoomHeader {
|
|||
prid: string;
|
||||
tmid: string;
|
||||
teamMain: boolean;
|
||||
status: string;
|
||||
status: TUserStatus;
|
||||
theme?: string;
|
||||
usersTyping: [];
|
||||
isGroupChat: boolean;
|
||||
|
|
|
@ -2,7 +2,7 @@ import { dequal } from 'dequal';
|
|||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IApplicationState } from '../../definitions';
|
||||
import { IApplicationState, TUserStatus } from '../../definitions';
|
||||
import { withDimensions } from '../../dimensions';
|
||||
import I18n from '../../i18n';
|
||||
import RoomHeader from './RoomHeader';
|
||||
|
@ -15,7 +15,7 @@ interface IRoomHeaderContainerProps {
|
|||
tmid: string;
|
||||
teamMain: boolean;
|
||||
usersTyping: [];
|
||||
status: string;
|
||||
status: TUserStatus;
|
||||
statusText: string;
|
||||
connecting: boolean;
|
||||
connected: boolean;
|
||||
|
@ -140,7 +140,7 @@ const mapStateToProps = (state: IApplicationState, ownProps: any) => {
|
|||
connecting: state.meteor.connecting || state.server.loading,
|
||||
connected: state.meteor.connected,
|
||||
usersTyping: state.usersTyping,
|
||||
status,
|
||||
status: status as TUserStatus,
|
||||
statusText
|
||||
};
|
||||
};
|
||||
|
|
|
@ -5,6 +5,7 @@ import { CustomIcon } from '../lib/Icons';
|
|||
import { STATUS_COLORS, themes } from '../constants/colors';
|
||||
import Status from './Status/Status';
|
||||
import { withTheme } from '../theme';
|
||||
import { TUserStatus } from '../definitions';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
icon: {
|
||||
|
@ -17,7 +18,7 @@ interface IRoomTypeIcon {
|
|||
type: string;
|
||||
isGroupChat?: boolean;
|
||||
teamMain?: boolean;
|
||||
status?: string;
|
||||
status?: TUserStatus;
|
||||
size?: number;
|
||||
style?: ViewStyle;
|
||||
}
|
||||
|
@ -31,9 +32,10 @@ const RoomTypeIcon = React.memo(({ type, isGroupChat, status, style, theme, team
|
|||
const iconStyle = [styles.icon, { color }, style];
|
||||
|
||||
if (type === 'd' && !isGroupChat) {
|
||||
return (
|
||||
<Status style={[iconStyle, { color: STATUS_COLORS[status!] ?? STATUS_COLORS.offline }]} size={size} status={status!} />
|
||||
);
|
||||
if (!status) {
|
||||
status = 'offline';
|
||||
}
|
||||
return <Status style={[iconStyle, { color: STATUS_COLORS[status] }]} size={size} status={status} />;
|
||||
}
|
||||
|
||||
// TODO: move this to a separate function
|
||||
|
|
|
@ -3,15 +3,9 @@ import { StyleProp, TextStyle } from 'react-native';
|
|||
|
||||
import { CustomIcon } from '../../lib/Icons';
|
||||
import { STATUS_COLORS } from '../../constants/colors';
|
||||
import { IStatus } from './definition';
|
||||
|
||||
interface IStatus {
|
||||
status: string;
|
||||
size: number;
|
||||
style?: StyleProp<TextStyle>;
|
||||
testID?: string;
|
||||
}
|
||||
|
||||
const Status = React.memo(({ style, status = 'offline', size = 32, ...props }: IStatus) => {
|
||||
const Status = React.memo(({ style, status = 'offline', size = 32, ...props }: Omit<IStatus, 'id'>) => {
|
||||
const name = `status-${status}`;
|
||||
const isNameValid = CustomIcon.hasIcon(name);
|
||||
const iconName = isNameValid ? name : 'status-offline';
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
import { TextProps } from 'react-native';
|
||||
|
||||
import { TUserStatus } from '../../definitions';
|
||||
|
||||
export interface IStatus extends TextProps {
|
||||
id: string;
|
||||
size: number;
|
||||
status: TUserStatus;
|
||||
}
|
|
@ -1,20 +1,15 @@
|
|||
import React, { memo } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { IApplicationState, TUserStatus } from '../../definitions';
|
||||
import Status from './Status';
|
||||
import { IStatus } from './definition';
|
||||
|
||||
interface IStatusContainer {
|
||||
style: any;
|
||||
size: number;
|
||||
status: string;
|
||||
}
|
||||
const StatusContainer = ({ id, style, size = 32, ...props }: Omit<IStatus, 'status'>): React.ReactElement => {
|
||||
const status = useSelector((state: IApplicationState) =>
|
||||
state.meteor.connected ? state.activeUsers[id] && state.activeUsers[id].status : 'loading'
|
||||
) as TUserStatus;
|
||||
return <Status size={size} style={style} status={status} {...props} />;
|
||||
};
|
||||
|
||||
const StatusContainer = memo(({ style, size = 32, status }: IStatusContainer) => (
|
||||
<Status size={size} style={style} status={status} />
|
||||
));
|
||||
|
||||
const mapStateToProps = (state: any, ownProps: any) => ({
|
||||
status: state.meteor.connected ? state.activeUsers[ownProps.id] && state.activeUsers[ownProps.id].status : 'loading'
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(StatusContainer);
|
||||
export default StatusContainer;
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
import React from 'react';
|
||||
import { Text } from 'react-native';
|
||||
import { StyleProp, Text, TextStyle } from 'react-native';
|
||||
|
||||
import { useTheme } from '../../theme';
|
||||
import { themes } from '../../constants/colors';
|
||||
import styles from './styles';
|
||||
import { events, logEvent } from '../../utils/log';
|
||||
import { IUserMention } from './interfaces';
|
||||
|
||||
interface IAtMention {
|
||||
mention: string;
|
||||
username?: string;
|
||||
navToRoomInfo?: Function;
|
||||
style?: any;
|
||||
style?: StyleProp<TextStyle>[];
|
||||
useRealName?: boolean;
|
||||
mentions: any;
|
||||
mentions?: IUserMention[];
|
||||
}
|
||||
|
||||
const AtMention = React.memo(({ mention, mentions, username, navToRoomInfo, style = [], useRealName }: IAtMention) => {
|
||||
|
@ -23,7 +24,7 @@ const AtMention = React.memo(({ mention, mentions, username, navToRoomInfo, styl
|
|||
style={[
|
||||
styles.mention,
|
||||
{
|
||||
color: themes[theme!].mentionGroupColor
|
||||
color: themes[theme].mentionGroupColor
|
||||
},
|
||||
...style
|
||||
]}>
|
||||
|
@ -35,11 +36,11 @@ const AtMention = React.memo(({ mention, mentions, username, navToRoomInfo, styl
|
|||
let mentionStyle = {};
|
||||
if (mention === username) {
|
||||
mentionStyle = {
|
||||
color: themes[theme!].mentionMeColor
|
||||
color: themes[theme].mentionMeColor
|
||||
};
|
||||
} else {
|
||||
mentionStyle = {
|
||||
color: themes[theme!].mentionOtherColor
|
||||
color: themes[theme].mentionOtherColor
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -64,7 +65,7 @@ const AtMention = React.memo(({ mention, mentions, username, navToRoomInfo, styl
|
|||
);
|
||||
}
|
||||
|
||||
return <Text style={[styles.text, { color: themes[theme!].bodyText }, ...style]}>{`@${mention}`}</Text>;
|
||||
return <Text style={[styles.text, { color: themes[theme].bodyText }, ...style]}>{`@${mention}`}</Text>;
|
||||
});
|
||||
|
||||
export default AtMention;
|
||||
|
|
|
@ -5,7 +5,7 @@ import { themes } from '../../constants/colors';
|
|||
import styles from './styles';
|
||||
|
||||
interface IBlockQuote {
|
||||
children: JSX.Element;
|
||||
children: React.ReactElement | null;
|
||||
theme: string;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { Text, TextStyle, StyleProp } from 'react-native';
|
||||
import { StyleProp, Text, TextStyle } from 'react-native';
|
||||
|
||||
import { themes } from '../../constants/colors';
|
||||
import { useTheme } from '../../theme';
|
||||
|
@ -33,7 +33,7 @@ const Hashtag = React.memo(({ hashtag, channels, navToRoomInfo, style = [] }: IH
|
|||
style={[
|
||||
styles.mention,
|
||||
{
|
||||
color: themes[theme!].mentionOtherColor
|
||||
color: themes[theme].mentionOtherColor
|
||||
},
|
||||
...style
|
||||
]}
|
||||
|
@ -42,7 +42,7 @@ const Hashtag = React.memo(({ hashtag, channels, navToRoomInfo, style = [] }: IH
|
|||
</Text>
|
||||
);
|
||||
}
|
||||
return <Text style={[styles.text, { color: themes[theme!].bodyText }, ...style]}>{`#${hashtag}`}</Text>;
|
||||
return <Text style={[styles.text, { color: themes[theme].bodyText }, ...style]}>{`#${hashtag}`}</Text>;
|
||||
});
|
||||
|
||||
export default Hashtag;
|
||||
|
|
|
@ -10,7 +10,7 @@ import openLink from '../../utils/openLink';
|
|||
import { TOnLinkPress } from './interfaces';
|
||||
|
||||
interface ILink {
|
||||
children: JSX.Element;
|
||||
children: React.ReactElement | null;
|
||||
link: string;
|
||||
theme: string;
|
||||
onLinkPress?: TOnLinkPress;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
|
||||
interface IList {
|
||||
children: JSX.Element;
|
||||
children: React.ReactElement[] | null;
|
||||
ordered: boolean;
|
||||
start: number;
|
||||
tight: boolean;
|
||||
|
@ -11,9 +11,8 @@ interface IList {
|
|||
const List = React.memo(({ children, ordered, tight, start = 1, numberOfLines = 0 }: IList) => {
|
||||
let bulletWidth = 15;
|
||||
|
||||
if (ordered) {
|
||||
// @ts-ignore
|
||||
const lastNumber = start + children.length - 1;
|
||||
if (ordered && children) {
|
||||
const lastNumber = start + children?.length - 1;
|
||||
bulletWidth = 9 * lastNumber.toString().length + 7;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ const style = StyleSheet.create({
|
|||
});
|
||||
|
||||
interface IListItem {
|
||||
children: JSX.Element;
|
||||
children: React.ReactElement | null;
|
||||
bulletWidth: number;
|
||||
level: number;
|
||||
ordered: boolean;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { StyleProp, Text, TextStyle } from 'react-native';
|
||||
import { Text, TextStyle } from 'react-native';
|
||||
import removeMarkdown from 'remove-markdown';
|
||||
|
||||
import shortnameToUnicode from '../../utils/shortnameToUnicode';
|
||||
|
@ -13,10 +13,10 @@ interface IMarkdownPreview {
|
|||
msg?: string;
|
||||
numberOfLines?: number;
|
||||
testID?: string;
|
||||
style?: StyleProp<TextStyle>[];
|
||||
style?: TextStyle[];
|
||||
}
|
||||
|
||||
const MarkdownPreview = ({ msg, numberOfLines = 1, testID, style = [] }: IMarkdownPreview): React.ReactElement | null => {
|
||||
const MarkdownPreview = ({ msg, numberOfLines = 1, testID, style = [] }: IMarkdownPreview) => {
|
||||
if (!msg) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import I18n from '../../i18n';
|
|||
import { themes } from '../../constants/colors';
|
||||
|
||||
interface ITable {
|
||||
children: JSX.Element;
|
||||
children: React.ReactElement | null;
|
||||
numColumns: number;
|
||||
theme: string;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import styles from './styles';
|
|||
|
||||
interface ITableCell {
|
||||
align: '' | 'left' | 'center' | 'right';
|
||||
children: JSX.Element;
|
||||
children: React.ReactElement | null;
|
||||
isLastCell: boolean;
|
||||
theme: string;
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import { themes } from '../../constants/colors';
|
|||
import styles from './styles';
|
||||
|
||||
interface ITableRow {
|
||||
children: JSX.Element;
|
||||
children: React.ReactElement | null;
|
||||
isLastRow: boolean;
|
||||
theme: string;
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ const TableRow = React.memo(({ isLastRow, children: _children, theme }: ITableRo
|
|||
rowStyle.push(styles.rowBottomBorder);
|
||||
}
|
||||
|
||||
const children: any = React.Children.toArray(_children);
|
||||
const children = React.Children.toArray(_children) as React.ReactElement[];
|
||||
children[children.length - 1] = React.cloneElement(children[children.length - 1], {
|
||||
isLastCell: true
|
||||
});
|
||||
|
|
|
@ -2,6 +2,7 @@ export interface IUserMention {
|
|||
_id: string;
|
||||
username: string;
|
||||
name?: string;
|
||||
type?: string;
|
||||
}
|
||||
|
||||
export interface IUserChannel {
|
||||
|
|
|
@ -14,7 +14,7 @@ const styles = StyleSheet.create({
|
|||
}
|
||||
});
|
||||
|
||||
const BigEmoji = ({ value }: IBigEmojiProps): JSX.Element => (
|
||||
const BigEmoji = ({ value }: IBigEmojiProps) => (
|
||||
<View style={styles.container}>
|
||||
{value.map(block => (
|
||||
<Emoji value={block.value} isBigEmoji />
|
||||
|
|
|
@ -18,7 +18,7 @@ const styles = StyleSheet.create({
|
|||
}
|
||||
});
|
||||
|
||||
const Bold = ({ value }: IBoldProps): JSX.Element => (
|
||||
const Bold = ({ value }: IBoldProps) => (
|
||||
<Text style={styles.text}>
|
||||
{value.map(block => {
|
||||
switch (block.type) {
|
||||
|
|
|
@ -11,7 +11,7 @@ interface ICodeProps {
|
|||
value: CodeProps['value'];
|
||||
}
|
||||
|
||||
const Code = ({ value }: ICodeProps): JSX.Element => {
|
||||
const Code = ({ value }: ICodeProps) => {
|
||||
const { theme } = useTheme();
|
||||
|
||||
return (
|
||||
|
@ -19,9 +19,9 @@ const Code = ({ value }: ICodeProps): JSX.Element => {
|
|||
style={[
|
||||
styles.codeBlock,
|
||||
{
|
||||
color: themes[theme!].bodyText,
|
||||
backgroundColor: themes[theme!].bannerBackground,
|
||||
borderColor: themes[theme!].borderColor
|
||||
color: themes[theme].bodyText,
|
||||
backgroundColor: themes[theme].bannerBackground,
|
||||
borderColor: themes[theme].borderColor
|
||||
}
|
||||
]}>
|
||||
{value.map(block => {
|
||||
|
|
|
@ -6,7 +6,7 @@ interface ICodeLineProps {
|
|||
value: CodeLineProps['value'];
|
||||
}
|
||||
|
||||
const CodeLine = ({ value }: ICodeLineProps): JSX.Element | null => {
|
||||
const CodeLine = ({ value }: ICodeLineProps) => {
|
||||
if (value.type !== 'PLAIN_TEXT') {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ interface IEmojiProps {
|
|||
isBigEmoji?: boolean;
|
||||
}
|
||||
|
||||
const Emoji = ({ value, isBigEmoji }: IEmojiProps): JSX.Element => {
|
||||
const Emoji = ({ value, isBigEmoji }: IEmojiProps) => {
|
||||
const { theme } = useTheme();
|
||||
const { baseUrl, getCustomEmoji } = useContext(MarkdownContext);
|
||||
const emojiUnicode = shortnameToUnicode(`:${value.value}:`);
|
||||
|
@ -23,7 +23,7 @@ const Emoji = ({ value, isBigEmoji }: IEmojiProps): JSX.Element => {
|
|||
if (emoji) {
|
||||
return <CustomEmoji baseUrl={baseUrl} style={[isBigEmoji ? styles.customEmojiBig : styles.customEmoji]} emoji={emoji} />;
|
||||
}
|
||||
return <Text style={[{ color: themes[theme!].bodyText }, isBigEmoji ? styles.textBig : styles.text]}>{emojiUnicode}</Text>;
|
||||
return <Text style={[{ color: themes[theme].bodyText }, isBigEmoji ? styles.textBig : styles.text]}>{emojiUnicode}</Text>;
|
||||
};
|
||||
|
||||
export default Emoji;
|
||||
|
|
|
@ -11,12 +11,12 @@ interface IHeadingProps {
|
|||
level: HeadingProps['level'];
|
||||
}
|
||||
|
||||
const Heading = ({ value, level }: IHeadingProps): JSX.Element => {
|
||||
const Heading = ({ value, level }: IHeadingProps) => {
|
||||
const { theme } = useTheme();
|
||||
const textStyle = styles[`heading${level}`];
|
||||
|
||||
return (
|
||||
<Text style={[textStyle, { color: themes[theme!].bodyText }]}>
|
||||
<Text style={[textStyle, { color: themes[theme].bodyText }]}>
|
||||
{value.map(block => {
|
||||
switch (block.type) {
|
||||
case 'PLAIN_TEXT':
|
||||
|
|
|
@ -31,11 +31,11 @@ const MessageImage = ({ img, theme }: TMessageImage) => (
|
|||
/>
|
||||
);
|
||||
|
||||
const Image = ({ value }: IImageProps): JSX.Element => {
|
||||
const Image = ({ value }: IImageProps) => {
|
||||
const { theme } = useTheme();
|
||||
const { src } = value;
|
||||
|
||||
return <MessageImage img={src.value} theme={theme!} />;
|
||||
return <MessageImage img={src.value} theme={theme} />;
|
||||
};
|
||||
|
||||
export default Image;
|
||||
|
|
|
@ -19,7 +19,7 @@ interface IParagraphProps {
|
|||
value: ParagraphProps['value'];
|
||||
}
|
||||
|
||||
const Inline = ({ value }: IParagraphProps): JSX.Element => {
|
||||
const Inline = ({ value }: IParagraphProps) => {
|
||||
const { useRealName, username, navToRoomInfo, mentions, channels } = useContext(MarkdownContext);
|
||||
return (
|
||||
<Text style={styles.inline}>
|
||||
|
|
|
@ -10,7 +10,7 @@ interface IInlineCodeProps {
|
|||
value: InlineCodeProps['value'];
|
||||
}
|
||||
|
||||
const InlineCode = ({ value }: IInlineCodeProps): JSX.Element => {
|
||||
const InlineCode = ({ value }: IInlineCodeProps) => {
|
||||
const { theme } = useTheme();
|
||||
|
||||
return (
|
||||
|
@ -18,9 +18,9 @@ const InlineCode = ({ value }: IInlineCodeProps): JSX.Element => {
|
|||
style={[
|
||||
styles.codeInline,
|
||||
{
|
||||
color: themes[theme!].bodyText,
|
||||
backgroundColor: themes[theme!].bannerBackground,
|
||||
borderColor: themes[theme!].borderColor
|
||||
color: themes[theme].bodyText,
|
||||
backgroundColor: themes[theme].bannerBackground,
|
||||
borderColor: themes[theme].borderColor
|
||||
}
|
||||
]}>
|
||||
{(block => {
|
||||
|
|
|
@ -17,7 +17,7 @@ const styles = StyleSheet.create({
|
|||
}
|
||||
});
|
||||
|
||||
const Italic = ({ value }: IItalicProps): JSX.Element => (
|
||||
const Italic = ({ value }: IItalicProps) => (
|
||||
<Text style={styles.text}>
|
||||
{value.map(block => {
|
||||
switch (block.type) {
|
||||
|
|
|
@ -18,7 +18,7 @@ interface ILinkProps {
|
|||
value: LinkProps['value'];
|
||||
}
|
||||
|
||||
const Link = ({ value }: ILinkProps): JSX.Element => {
|
||||
const Link = ({ value }: ILinkProps) => {
|
||||
const { theme } = useTheme();
|
||||
const { onLinkPress } = useContext(MarkdownContext);
|
||||
const { src, label } = value;
|
||||
|
@ -38,7 +38,7 @@ const Link = ({ value }: ILinkProps): JSX.Element => {
|
|||
};
|
||||
|
||||
return (
|
||||
<Text onPress={handlePress} onLongPress={onLongPress} style={[styles.link, { color: themes[theme!].actionTintColor }]}>
|
||||
<Text onPress={handlePress} onLongPress={onLongPress} style={[styles.link, { color: themes[theme].actionTintColor }]}>
|
||||
{(block => {
|
||||
switch (block.type) {
|
||||
case 'PLAIN_TEXT':
|
||||
|
|
|
@ -11,13 +11,13 @@ interface IOrderedListProps {
|
|||
value: OrderedListProps['value'];
|
||||
}
|
||||
|
||||
const OrderedList = ({ value }: IOrderedListProps): JSX.Element => {
|
||||
const OrderedList = ({ value }: IOrderedListProps) => {
|
||||
const { theme } = useTheme();
|
||||
return (
|
||||
<View>
|
||||
{value.map((item, index) => (
|
||||
<View style={styles.row}>
|
||||
<Text style={[styles.text, { color: themes[theme!].bodyText }]}>{index + 1}. </Text>
|
||||
<Text style={[styles.text, { color: themes[theme].bodyText }]}>{index + 1}. </Text>
|
||||
<Inline value={item.value} />
|
||||
</View>
|
||||
))}
|
||||
|
|
|
@ -11,10 +11,10 @@ interface IParagraphProps {
|
|||
value: ParagraphProps['value'];
|
||||
}
|
||||
|
||||
const Paragraph = ({ value }: IParagraphProps): JSX.Element => {
|
||||
const Paragraph = ({ value }: IParagraphProps) => {
|
||||
const { theme } = useTheme();
|
||||
return (
|
||||
<Text style={[styles.text, { color: themes[theme!].bodyText }]}>
|
||||
<Text style={[styles.text, { color: themes[theme].bodyText }]}>
|
||||
<Inline value={value} />
|
||||
</Text>
|
||||
);
|
||||
|
|
|
@ -10,10 +10,10 @@ interface IPlainProps {
|
|||
value: PlainProps['value'];
|
||||
}
|
||||
|
||||
const Plain = ({ value }: IPlainProps): JSX.Element => {
|
||||
const Plain = ({ value }: IPlainProps) => {
|
||||
const { theme } = useTheme();
|
||||
return (
|
||||
<Text accessibilityLabel={value} style={[styles.plainText, { color: themes[theme!].bodyText }]}>
|
||||
<Text accessibilityLabel={value} style={[styles.plainText, { color: themes[theme].bodyText }]}>
|
||||
{value}
|
||||
</Text>
|
||||
);
|
||||
|
|
|
@ -11,11 +11,11 @@ interface IQuoteProps {
|
|||
value: QuoteProps['value'];
|
||||
}
|
||||
|
||||
const Quote = ({ value }: IQuoteProps): JSX.Element => {
|
||||
const Quote = ({ value }: IQuoteProps) => {
|
||||
const { theme } = useTheme();
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={[styles.quote, { backgroundColor: themes[theme!].borderColor }]} />
|
||||
<View style={[styles.quote, { backgroundColor: themes[theme].borderColor }]} />
|
||||
<View style={styles.childContainer}>
|
||||
{value.map(item => (
|
||||
<Paragraph value={item.value} />
|
||||
|
|
|
@ -17,7 +17,7 @@ const styles = StyleSheet.create({
|
|||
}
|
||||
});
|
||||
|
||||
const Strike = ({ value }: IStrikeProps): JSX.Element => (
|
||||
const Strike = ({ value }: IStrikeProps) => (
|
||||
<Text style={styles.text}>
|
||||
{value.map(block => {
|
||||
switch (block.type) {
|
||||
|
|
|
@ -11,13 +11,13 @@ interface ITasksProps {
|
|||
value: TasksProps['value'];
|
||||
}
|
||||
|
||||
const TaskList = ({ value = [] }: ITasksProps): JSX.Element => {
|
||||
const TaskList = ({ value = [] }: ITasksProps) => {
|
||||
const { theme } = useTheme();
|
||||
return (
|
||||
<View>
|
||||
{value.map(item => (
|
||||
<View style={styles.row}>
|
||||
<Text style={[styles.text, { color: themes[theme!].bodyText }]}>{item.status ? '- [x] ' : '- [ ] '}</Text>
|
||||
<Text style={[styles.text, { color: themes[theme].bodyText }]}>{item.status ? '- [x] ' : '- [ ] '}</Text>
|
||||
<Inline value={item.value} />
|
||||
</View>
|
||||
))}
|
||||
|
|
|
@ -11,13 +11,13 @@ interface IUnorderedListProps {
|
|||
value: UnorderedListProps['value'];
|
||||
}
|
||||
|
||||
const UnorderedList = ({ value }: IUnorderedListProps): JSX.Element => {
|
||||
const UnorderedList = ({ value }: IUnorderedListProps) => {
|
||||
const { theme } = useTheme();
|
||||
return (
|
||||
<View>
|
||||
{value.map(item => (
|
||||
<View style={styles.row}>
|
||||
<Text style={[styles.text, { color: themes[theme!].bodyText }]}>- </Text>
|
||||
<Text style={[styles.text, { color: themes[theme].bodyText }]}>- </Text>
|
||||
<Inline value={item.value} />
|
||||
</View>
|
||||
))}
|
||||
|
|
|
@ -35,7 +35,7 @@ const Body = ({
|
|||
getCustomEmoji,
|
||||
baseUrl,
|
||||
onLinkPress
|
||||
}: IBodyProps): React.ReactElement | null => {
|
||||
}: IBodyProps) => {
|
||||
if (isEmpty(tokens)) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -82,7 +82,7 @@ const Attachments = React.memo(
|
|||
if (file && file.actions && file.actions.length > 0) {
|
||||
return <AttachedActions attachment={file} />;
|
||||
}
|
||||
if (file.title) {
|
||||
if (typeof file.collapsed === 'boolean') {
|
||||
return (
|
||||
<CollapsibleQuote key={index} index={index} attachment={file} timeFormat={timeFormat} getCustomEmoji={getCustomEmoji} />
|
||||
);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import Model from '@nozbe/watermelondb/Model';
|
||||
|
||||
import { IUserEmail, IUserSettings } from './IUser';
|
||||
import { UserStatus } from './UserStatus';
|
||||
import { TUserStatus } from './TUserStatus';
|
||||
|
||||
export interface ILoggedUser {
|
||||
id: string;
|
||||
|
@ -9,7 +9,7 @@ export interface ILoggedUser {
|
|||
username: string;
|
||||
name: string;
|
||||
language?: string;
|
||||
status: UserStatus;
|
||||
status: TUserStatus;
|
||||
statusText?: string;
|
||||
customFields?: {
|
||||
[key: string]: any;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Model from '@nozbe/watermelondb/Model';
|
||||
|
||||
import { UserStatus } from './UserStatus';
|
||||
import { TUserStatus } from './TUserStatus';
|
||||
import { IRocketChatRecord } from './IRocketChatRecord';
|
||||
import { ILoggedUser } from './ILoggedUser';
|
||||
|
||||
|
@ -25,7 +25,7 @@ export interface IPersonalAccessToken extends ILoginToken {
|
|||
export interface IUserRegistered {
|
||||
_id: string;
|
||||
type: string;
|
||||
status: UserStatus;
|
||||
status: TUserStatus;
|
||||
active: boolean;
|
||||
name: string;
|
||||
username: string;
|
||||
|
@ -133,14 +133,14 @@ export interface IUser extends IRocketChatRecord, Omit<ILoggedUser, 'username' |
|
|||
name?: string;
|
||||
services?: IUserServices;
|
||||
emails?: IUserEmail[];
|
||||
status: UserStatus;
|
||||
status: TUserStatus;
|
||||
statusConnection?: string;
|
||||
lastLogin?: Date;
|
||||
avatarOrigin?: string;
|
||||
avatarETag?: string;
|
||||
utcOffset?: number;
|
||||
language?: string;
|
||||
statusDefault?: UserStatus;
|
||||
statusDefault?: TUserStatus;
|
||||
statusText?: string;
|
||||
oauth?: {
|
||||
authorizedClients: string[];
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
export const STATUSES = ['offline', 'online', 'away', 'busy'] as const;
|
||||
|
||||
export type TUserStatus = typeof STATUSES[number];
|
|
@ -1,6 +0,0 @@
|
|||
export enum UserStatus {
|
||||
ONLINE = 'online',
|
||||
AWAY = 'away',
|
||||
OFFLINE = 'offline',
|
||||
BUSY = 'busy'
|
||||
}
|
|
@ -26,6 +26,7 @@ export * from './ICertificate';
|
|||
export * from './IUrl';
|
||||
export * from './ICredentials';
|
||||
export * from './ISearch';
|
||||
export * from './TUserStatus';
|
||||
|
||||
export interface IBaseScreen<T extends Record<string, object | undefined>, S extends string> {
|
||||
navigation: StackNavigationProp<T, S>;
|
||||
|
|
|
@ -8,7 +8,7 @@ export interface IDimensionsContextProps {
|
|||
width: number;
|
||||
height: number;
|
||||
scale?: number;
|
||||
fontScale?: number;
|
||||
fontScale: number;
|
||||
setDimensions?: ({
|
||||
width,
|
||||
height,
|
||||
|
@ -22,7 +22,9 @@ export interface IDimensionsContextProps {
|
|||
}) => void;
|
||||
}
|
||||
|
||||
export const DimensionsContext = React.createContext<IDimensionsContextProps>(Dimensions.get('window'));
|
||||
export const DimensionsContext = React.createContext<IDimensionsContextProps>(
|
||||
Dimensions.get('window') as IDimensionsContextProps
|
||||
);
|
||||
|
||||
export function withDimensions<T extends object>(Component: React.ComponentType<T> & TNavigationOptions): typeof Component {
|
||||
const DimensionsComponent = (props: T) => (
|
||||
|
|
|
@ -73,7 +73,6 @@ export const THEME_PREFERENCES_KEY = 'RC_THEME_PREFERENCES_KEY';
|
|||
export const CRASH_REPORT_KEY = 'RC_CRASH_REPORT_KEY';
|
||||
export const ANALYTICS_EVENTS_KEY = 'RC_ANALYTICS_EVENTS_KEY';
|
||||
export const MIN_ROCKETCHAT_VERSION = '0.70.0';
|
||||
export const STATUSES = ['offline', 'online', 'away', 'busy'];
|
||||
|
||||
const RocketChat = {
|
||||
TOKEN_KEY,
|
||||
|
|
|
@ -6,7 +6,6 @@ import { Q } from '@nozbe/watermelondb';
|
|||
|
||||
import log from '../../../utils/log';
|
||||
import { onRolesChanged } from '../../methods/getRoles';
|
||||
import { UserStatus } from '../../../definitions/UserStatus';
|
||||
import { setActiveUsers } from '../../../actions/activeUsers';
|
||||
import protectedFunction from '../../methods/helpers/protectedFunction';
|
||||
import database from '../../database';
|
||||
|
@ -17,8 +16,8 @@ import { store } from '../../auxStore';
|
|||
import { loginRequest, setLoginServices, setUser } from '../../../actions/login';
|
||||
import sdk from './sdk';
|
||||
import I18n from '../../../i18n';
|
||||
import RocketChat, { MIN_ROCKETCHAT_VERSION, STATUSES } from '../rocketchat';
|
||||
import { ICredentials, ILoggedUser, IRocketChat } from '../../../definitions';
|
||||
import RocketChat, { MIN_ROCKETCHAT_VERSION } from '../rocketchat';
|
||||
import { ICredentials, ILoggedUser, IRocketChat, STATUSES } from '../../../definitions';
|
||||
import { isIOS } from '../../../utils/deviceInfo';
|
||||
import { connectRequest, connectSuccess, disconnect as disconnectAction } from '../../../actions/connect';
|
||||
import { updatePermission } from '../../../actions/permissions';
|
||||
|
@ -195,7 +194,7 @@ function connect(
|
|||
|
||||
const { user: loggedUser } = store.getState().login;
|
||||
if (loggedUser && loggedUser.id === id) {
|
||||
store.dispatch(setUser({ status: STATUSES[status] as UserStatus, statusText }));
|
||||
store.dispatch(setUser({ status: STATUSES[status], statusText }));
|
||||
}
|
||||
} else if (/updateAvatar/.test(eventName)) {
|
||||
const { username, etag } = ddpMessage.fields.args[0];
|
||||
|
|
|
@ -12,6 +12,7 @@ import Touchable from './Touchable';
|
|||
import Tag from './Tag';
|
||||
import I18n from '../../i18n';
|
||||
import { DisplayMode } from '../../constants/constantDisplayMode';
|
||||
import { TUserStatus } from '../../definitions';
|
||||
|
||||
interface IRoomItem {
|
||||
rid: string;
|
||||
|
@ -24,7 +25,7 @@ interface IRoomItem {
|
|||
avatarSize: number;
|
||||
testID: string;
|
||||
width: number;
|
||||
status: string;
|
||||
status: TUserStatus;
|
||||
useRealName: boolean;
|
||||
theme: string;
|
||||
isFocused: boolean;
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import React from 'react';
|
||||
|
||||
import { TUserStatus } from '../../definitions';
|
||||
import RoomTypeIcon from '../../containers/RoomTypeIcon';
|
||||
|
||||
interface ITypeIcon {
|
||||
type: string;
|
||||
status: string;
|
||||
status: TUserStatus;
|
||||
prid: string;
|
||||
isGroupChat: boolean;
|
||||
teamMain: boolean;
|
||||
|
|
|
@ -5,6 +5,7 @@ import I18n from '../../i18n';
|
|||
import { ROW_HEIGHT, ROW_HEIGHT_CONDENSED } from './styles';
|
||||
import { formatDate } from '../../utils/room';
|
||||
import RoomItem from './RoomItem';
|
||||
import { TUserStatus } from '../../definitions';
|
||||
|
||||
export { ROW_HEIGHT, ROW_HEIGHT_CONDENSED };
|
||||
interface IRoomItemContainerProps {
|
||||
|
@ -16,7 +17,7 @@ interface IRoomItemContainerProps {
|
|||
username: string;
|
||||
avatarSize: number;
|
||||
width: number;
|
||||
status: string;
|
||||
status: TUserStatus;
|
||||
toggleFav(): void;
|
||||
toggleRead(): void;
|
||||
hideChannel(): void;
|
||||
|
@ -53,7 +54,7 @@ class RoomItemContainer extends React.Component<IRoomItemContainerProps, any> {
|
|||
|
||||
private roomSubscription: any;
|
||||
|
||||
static defaultProps = {
|
||||
static defaultProps: Partial<IRoomItemContainerProps> = {
|
||||
avatarSize: 48,
|
||||
status: 'offline',
|
||||
getUserPresence: () => {},
|
||||
|
@ -233,7 +234,7 @@ const mapStateToProps = (state: any, ownProps: any) => {
|
|||
}
|
||||
return {
|
||||
connected: state.meteor.connected,
|
||||
status
|
||||
status: status as TUserStatus
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { clearActiveUsers, setActiveUsers } from '../actions/activeUsers';
|
||||
import { UserStatus } from '../definitions/UserStatus';
|
||||
import { IActiveUsers, initialState } from './activeUsers';
|
||||
import { mockedStore } from './mockedStore';
|
||||
|
||||
|
@ -9,7 +8,7 @@ describe('test reducer', () => {
|
|||
expect(state).toEqual(initialState);
|
||||
});
|
||||
it('should return modified store after action', () => {
|
||||
const activeUsers: IActiveUsers = { any: { status: UserStatus.ONLINE, statusText: 'any' } };
|
||||
const activeUsers: IActiveUsers = { any: { status: 'online', statusText: 'any' } };
|
||||
mockedStore.dispatch(setActiveUsers(activeUsers));
|
||||
const state = mockedStore.getState().activeUsers;
|
||||
expect(state).toEqual({ ...activeUsers });
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { ACTIVE_USERS } from '../actions/actionsTypes';
|
||||
import { TApplicationActions } from '../definitions';
|
||||
import { UserStatus } from '../definitions/UserStatus';
|
||||
import { TApplicationActions, TUserStatus } from '../definitions';
|
||||
|
||||
export interface IActiveUser {
|
||||
status: UserStatus;
|
||||
status: TUserStatus;
|
||||
statusText: string;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { TUserStatus } from '../definitions';
|
||||
import {
|
||||
clearUser,
|
||||
loginFailure,
|
||||
|
@ -8,7 +9,6 @@ import {
|
|||
setLoginServices,
|
||||
setUser
|
||||
} from '../actions/login';
|
||||
import { UserStatus } from '../definitions/UserStatus';
|
||||
import { initialState } from './login';
|
||||
import { mockedStore } from './mockedStore';
|
||||
|
||||
|
@ -49,7 +49,7 @@ describe('test selectedUsers reducer', () => {
|
|||
isFromWebView: false,
|
||||
showMessageInMainThread: false,
|
||||
enableMessageParserEarlyAdoption: false,
|
||||
status: UserStatus.ONLINE,
|
||||
status: 'online' as TUserStatus,
|
||||
statusText: 'online'
|
||||
};
|
||||
mockedStore.dispatch(loginSuccess(user));
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { UserStatus } from '../definitions/UserStatus';
|
||||
import * as types from '../actions/actionsTypes';
|
||||
import { TActionsLogin } from '../actions/login';
|
||||
import { IUser } from '../definitions';
|
||||
import { IUser, TUserStatus } from '../definitions';
|
||||
|
||||
export interface IUserLogin {
|
||||
id: string;
|
||||
|
@ -9,7 +8,7 @@ export interface IUserLogin {
|
|||
username: string;
|
||||
name: string;
|
||||
language?: string;
|
||||
status: UserStatus;
|
||||
status: TUserStatus;
|
||||
statusText: string;
|
||||
roles: string[];
|
||||
avatarETag?: string;
|
||||
|
|
|
@ -6,7 +6,6 @@ import { CompositeNavigationProp } from '@react-navigation/core';
|
|||
|
||||
import * as List from '../containers/List';
|
||||
import StatusBar from '../containers/StatusBar';
|
||||
import { useTheme } from '../theme';
|
||||
import * as HeaderButton from '../containers/HeaderButton';
|
||||
import SafeAreaView from '../containers/SafeAreaView';
|
||||
import I18n from '../i18n';
|
||||
|
@ -41,7 +40,6 @@ const setHeader = ({
|
|||
|
||||
const AddChannelTeamView = ({ navigation, route, isMasterDetail }: IAddChannelTeamView) => {
|
||||
const { teamId, teamChannels } = route.params;
|
||||
const { theme } = useTheme();
|
||||
|
||||
useEffect(() => {
|
||||
setHeader({ navigation, isMasterDetail });
|
||||
|
@ -67,7 +65,6 @@ const AddChannelTeamView = ({ navigation, route, isMasterDetail }: IAddChannelTe
|
|||
testID='add-channel-team-view-create-channel'
|
||||
left={() => <List.Icon name='team' />}
|
||||
right={() => <List.Icon name='chevron-right' />}
|
||||
theme={theme}
|
||||
/>
|
||||
<List.Separator />
|
||||
<List.Item
|
||||
|
@ -76,7 +73,6 @@ const AddChannelTeamView = ({ navigation, route, isMasterDetail }: IAddChannelTe
|
|||
testID='add-channel-team-view-add-existing'
|
||||
left={() => <List.Icon name='channel-public' />}
|
||||
right={() => <List.Icon name='chevron-right' />}
|
||||
theme={theme}
|
||||
/>
|
||||
<List.Separator />
|
||||
</List.Container>
|
||||
|
|
|
@ -92,7 +92,7 @@ class ForgotPasswordView extends React.Component<IForgotPasswordViewProps, IForg
|
|||
const { theme } = this.props;
|
||||
|
||||
return (
|
||||
<FormContainer theme={theme} testID='forgot-password-view'>
|
||||
<FormContainer testID='forgot-password-view'>
|
||||
<FormContainerInner>
|
||||
<Text style={[sharedStyles.loginTitle, sharedStyles.textBold, { color: themes[theme].titleText }]}>
|
||||
{I18n.t('Forgot_password')}
|
||||
|
|
|
@ -229,7 +229,7 @@ class LoginView extends React.Component<ILoginViewProps, any> {
|
|||
render() {
|
||||
const { Accounts_ShowFormLogin, theme, navigation } = this.props;
|
||||
return (
|
||||
<FormContainer theme={theme} testID='login-view'>
|
||||
<FormContainer testID='login-view'>
|
||||
<FormContainerInner>
|
||||
<LoginServices separator={Accounts_ShowFormLogin} navigation={navigation} theme={theme} />
|
||||
{this.renderUserForm()}
|
||||
|
|
|
@ -321,7 +321,7 @@ class NewServerView extends React.Component<INewServerViewProps, INewServerViewS
|
|||
const marginTop = previousServer ? 0 : 35;
|
||||
|
||||
return (
|
||||
<FormContainer theme={theme} testID='new-server-view' keyboardShouldPersistTaps='never'>
|
||||
<FormContainer testID='new-server-view' keyboardShouldPersistTaps='never'>
|
||||
<FormContainerInner>
|
||||
<Image
|
||||
style={[
|
||||
|
|
|
@ -172,60 +172,64 @@ class RegisterView extends React.Component<IProps, any> {
|
|||
return null;
|
||||
}
|
||||
try {
|
||||
return Object.keys(this.parsedCustomFields).map((key, index, array) => {
|
||||
if (this.parsedCustomFields[key].type === 'select') {
|
||||
const options = this.parsedCustomFields[key].options.map((option: string) => ({ label: option, value: option }));
|
||||
return (
|
||||
<RNPickerSelect
|
||||
key={key}
|
||||
items={options}
|
||||
onValueChange={value => {
|
||||
const newValue: { [key: string]: string | number } = {};
|
||||
newValue[key] = value;
|
||||
this.setState({ customFields: { ...customFields, ...newValue } });
|
||||
}}
|
||||
value={customFields[key]}>
|
||||
return (
|
||||
<>
|
||||
{Object.keys(this.parsedCustomFields).map((key, index, array) => {
|
||||
if (this.parsedCustomFields[key].type === 'select') {
|
||||
const options = this.parsedCustomFields[key].options.map((option: string) => ({ label: option, value: option }));
|
||||
return (
|
||||
<RNPickerSelect
|
||||
key={key}
|
||||
items={options}
|
||||
onValueChange={value => {
|
||||
const newValue: { [key: string]: string | number } = {};
|
||||
newValue[key] = value;
|
||||
this.setState({ customFields: { ...customFields, ...newValue } });
|
||||
}}
|
||||
value={customFields[key]}>
|
||||
<TextInput
|
||||
inputRef={(e: any) => {
|
||||
// @ts-ignore
|
||||
this[key] = e;
|
||||
}}
|
||||
placeholder={key}
|
||||
value={customFields[key]}
|
||||
testID='register-view-custom-picker'
|
||||
theme={theme}
|
||||
/>
|
||||
</RNPickerSelect>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<TextInput
|
||||
inputRef={e => {
|
||||
// @ts-ignore
|
||||
this[key] = e;
|
||||
}}
|
||||
key={key}
|
||||
label={key}
|
||||
placeholder={key}
|
||||
value={customFields[key]}
|
||||
testID='register-view-custom-picker'
|
||||
onChangeText={(value: string) => {
|
||||
const newValue: { [key: string]: string | number } = {};
|
||||
newValue[key] = value;
|
||||
this.setState({ customFields: { ...customFields, ...newValue } });
|
||||
}}
|
||||
onSubmitEditing={() => {
|
||||
if (array.length - 1 > index) {
|
||||
// @ts-ignore
|
||||
return this[array[index + 1]].focus();
|
||||
}
|
||||
this.avatarUrl.focus();
|
||||
}}
|
||||
containerStyle={styles.inputContainer}
|
||||
theme={theme}
|
||||
/>
|
||||
</RNPickerSelect>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<TextInput
|
||||
inputRef={(e: any) => {
|
||||
// @ts-ignore
|
||||
this[key] = e;
|
||||
}}
|
||||
key={key}
|
||||
label={key}
|
||||
placeholder={key}
|
||||
value={customFields[key]}
|
||||
onChangeText={(value: string) => {
|
||||
const newValue: { [key: string]: string | number } = {};
|
||||
newValue[key] = value;
|
||||
this.setState({ customFields: { ...customFields, ...newValue } });
|
||||
}}
|
||||
onSubmitEditing={() => {
|
||||
if (array.length - 1 > index) {
|
||||
// @ts-ignore
|
||||
return this[array[index + 1]].focus();
|
||||
}
|
||||
this.avatarUrl.focus();
|
||||
}}
|
||||
containerStyle={styles.inputContainer}
|
||||
theme={theme}
|
||||
/>
|
||||
);
|
||||
});
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
|
@ -235,7 +239,7 @@ class RegisterView extends React.Component<IProps, any> {
|
|||
const { saving } = this.state;
|
||||
const { theme, showLoginButton, navigation } = this.props;
|
||||
return (
|
||||
<FormContainer theme={theme} testID='register-view'>
|
||||
<FormContainer testID='register-view'>
|
||||
<FormContainerInner>
|
||||
<LoginServices navigation={navigation} theme={theme} separator />
|
||||
<Text style={[styles.title, sharedStyles.textBold, { color: themes[theme].titleText }]}>{I18n.t('Sign_Up')}</Text>
|
||||
|
|
|
@ -223,7 +223,7 @@ class ScreenLockConfigView extends React.Component<IScreenLockConfigViewProps, I
|
|||
return (
|
||||
<List.Section>
|
||||
<List.Separator />
|
||||
{items.map(item => this.renderItem({ item }))}
|
||||
<>{items.map(item => this.renderItem({ item }))}</>
|
||||
</List.Section>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -62,7 +62,7 @@ const SendEmailConfirmationView = ({ navigation, route }: ISendEmailConfirmation
|
|||
}, []);
|
||||
|
||||
return (
|
||||
<FormContainer theme={theme} testID='send-email-confirmation-view'>
|
||||
<FormContainer testID='send-email-confirmation-view'>
|
||||
<FormContainerInner>
|
||||
<TextInput
|
||||
autoFocus
|
||||
|
|
|
@ -19,6 +19,7 @@ import Navigation from '../../lib/Navigation';
|
|||
import SidebarItem from './SidebarItem';
|
||||
import styles from './styles';
|
||||
import { DrawerParamList } from '../../stacks/types';
|
||||
import { TUserStatus } from '../../definitions';
|
||||
|
||||
interface ISeparatorProps {
|
||||
theme: string;
|
||||
|
@ -40,7 +41,7 @@ interface ISidebarProps {
|
|||
Site_Name: string;
|
||||
user: {
|
||||
statusText: string;
|
||||
status: string;
|
||||
status: TUserStatus;
|
||||
username: string;
|
||||
name: string;
|
||||
roles: string[];
|
||||
|
|
|
@ -2,7 +2,6 @@ import React from 'react';
|
|||
import { FlatList, StyleSheet } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { UserStatus } from '../definitions/UserStatus';
|
||||
import { setUser } from '../actions/login';
|
||||
import * as HeaderButton from '../containers/HeaderButton';
|
||||
import * as List from '../containers/List';
|
||||
|
@ -11,7 +10,7 @@ import SafeAreaView from '../containers/SafeAreaView';
|
|||
import Status from '../containers/Status/Status';
|
||||
import TextInput from '../containers/TextInput';
|
||||
import { LISTENER } from '../containers/Toast';
|
||||
import { IApplicationState, IBaseScreen, IUser } from '../definitions';
|
||||
import { IApplicationState, IBaseScreen, IUser, TUserStatus } from '../definitions';
|
||||
import I18n from '../i18n';
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
import { getUserSelector } from '../selectors/login';
|
||||
|
@ -20,7 +19,12 @@ import EventEmitter from '../utils/events';
|
|||
import { showErrorAlert } from '../utils/info';
|
||||
import log, { events, logEvent } from '../utils/log';
|
||||
|
||||
const STATUS = [
|
||||
interface IStatus {
|
||||
id: TUserStatus;
|
||||
name: string;
|
||||
}
|
||||
|
||||
const STATUS: IStatus[] = [
|
||||
{
|
||||
id: 'online',
|
||||
name: 'Online'
|
||||
|
@ -135,7 +139,7 @@ class StatusView extends React.Component<IStatusViewProps, IStatusViewState> {
|
|||
value={statusText}
|
||||
containerStyle={styles.inputContainer}
|
||||
onChangeText={text => this.setState({ statusText: text })}
|
||||
left={<Status testID={`status-view-current-${user.status}`} style={styles.inputLeft} status={user.status!} size={24} />}
|
||||
left={<Status testID={`status-view-current-${user.status}`} style={styles.inputLeft} status={user.status} size={24} />}
|
||||
inputStyle={styles.inputStyle}
|
||||
placeholder={I18n.t('What_are_you_doing_right_now')}
|
||||
testID='status-view-input'
|
||||
|
@ -145,7 +149,7 @@ class StatusView extends React.Component<IStatusViewProps, IStatusViewState> {
|
|||
);
|
||||
};
|
||||
|
||||
renderItem = ({ item }: { item: { id: string; name: string } }) => {
|
||||
renderItem = ({ item }: { item: IStatus }) => {
|
||||
const { statusText } = this.state;
|
||||
const { user, dispatch } = this.props;
|
||||
const { id, name } = item;
|
||||
|
@ -159,7 +163,7 @@ class StatusView extends React.Component<IStatusViewProps, IStatusViewState> {
|
|||
try {
|
||||
const result = await RocketChat.setUserStatus(item.id, statusText);
|
||||
if (result.success) {
|
||||
dispatch(setUser({ status: item.id as UserStatus }));
|
||||
dispatch(setUser({ status: item.id }));
|
||||
}
|
||||
} catch (e: any) {
|
||||
showErrorAlert(I18n.t(e.data.errorType));
|
||||
|
|
|
@ -131,11 +131,11 @@ class ThemeView extends React.Component<IThemeViewProps> {
|
|||
<List.Container>
|
||||
<List.Section title='Theme'>
|
||||
<List.Separator />
|
||||
{themeGroup.map(item => this.renderItem({ item }))}
|
||||
<>{themeGroup.map(item => this.renderItem({ item }))}</>
|
||||
</List.Section>
|
||||
<List.Section title='Dark_level'>
|
||||
<List.Separator />
|
||||
{darkGroup.map(item => this.renderItem({ item }))}
|
||||
<>{darkGroup.map(item => this.renderItem({ item }))}</>
|
||||
</List.Section>
|
||||
</List.Container>
|
||||
</SafeAreaView>
|
||||
|
|
|
@ -74,7 +74,7 @@ class WorkspaceView extends React.Component<IWorkSpaceProp, any> {
|
|||
const { theme, Site_Name, Site_Url, Assets_favicon_512, server, showLoginButton } = this.props;
|
||||
|
||||
return (
|
||||
<FormContainer theme={theme} testID='workspace-view'>
|
||||
<FormContainer testID='workspace-view'>
|
||||
<FormContainerInner>
|
||||
<View style={styles.alignItemsCenter}>
|
||||
<ServerAvatar theme={theme} url={server} image={Assets_favicon_512?.url ?? Assets_favicon_512?.defaultUrl} />
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue