Unverified Commit 03e8df61 authored by Diego Mello's avatar Diego Mello Committed by GitHub
Browse files

Merge 4.26.1 into master (#3979)

parent ed3868ee
......@@ -144,7 +144,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode VERSIONCODE as Integer
versionName "4.26.0"
versionName "4.26.1"
vectorDrawables.useSupportLibrary = true
if (!isFoss) {
manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String]
......
......@@ -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;
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';
......
import { TextProps } from 'react-native';
import { TUserStatus } from '../../definitions';
export interface IStatus extends TextProps {
id: string;
size: number;
status: TUserStatus;
}
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;
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;
}
......
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';
......@@ -18,7 +18,7 @@ const Hashtag = React.memo(({ hashtag, channels, navToRoomInfo, style = [] }: IH
const handlePress = () => {
const index = channels?.findIndex(channel => channel.name === hashtag);
if (index && navToRoomInfo) {
if (typeof index !== 'undefined' && navToRoomInfo) {
const navParam = {
t: 'c',
rid: channels?.[index]._id
......@@ -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;
......
import React from 'react';
interface IList {
children: JSX.Element;
children: React.ReactElement[] | null;
ordered: boolean;
start: number;