Unverified Commit 5cd4d824 authored by Diego Mello's avatar Diego Mello Committed by GitHub
Browse files

Merge 4.21.0 into master (#3467)

parent 4db5db7f
......@@ -23,3 +23,4 @@ SECURITY.md
npm-debug.log
yarn-error.log
app/i18n/locales/
......@@ -144,7 +144,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode VERSIONCODE as Integer
versionName "4.20.0"
versionName "4.21.0"
vectorDrawables.useSupportLibrary = true
if (!isFoss) {
manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String]
......@@ -260,7 +260,6 @@ android {
dependencies {
addUnimodulesDependencies()
implementation project(':watermelondb')
implementation project(':@react-native-community_viewpager')
playImplementation project(':reactnativenotifications')
playImplementation project(':@react-native-firebase_app')
......@@ -293,6 +292,8 @@ dependencies {
implementation "com.github.bumptech.glide:glide:4.9.0"
annotationProcessor "com.github.bumptech.glide:compiler:4.9.0"
implementation "com.tencent:mmkv-static:1.2.1"
implementation 'com.squareup.okhttp3:okhttp:4.9.0'
implementation "com.squareup.okhttp3:okhttp-urlconnection:4.9.0"
}
// Run this once to be able to run the application with BUCK
......
......@@ -3,7 +3,6 @@
package="chat.rocket.reactnative">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
......
......@@ -9,8 +9,9 @@ import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.soloader.SoLoader;
import com.nozbe.watermelondb.WatermelonDBPackage;
import com.reactnativecommunity.viewpager.RNCViewPagerPackage;
import com.facebook.react.bridge.JSIModulePackage;
import com.swmansion.reanimated.ReanimatedJSIModulePackage;
import org.unimodules.adapters.react.ModuleRegistryAdapter;
import org.unimodules.adapters.react.ReactModuleRegistryProvider;
......@@ -35,7 +36,6 @@ public class MainApplication extends Application implements ReactApplication {
protected List<ReactPackage> getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
packages.add(new WatermelonDBPackage());
packages.add(new RNCViewPagerPackage());
packages.add(new SSLPinningPackage());
List<ReactPackage> unimodules = Arrays.<ReactPackage>asList(
......@@ -52,6 +52,11 @@ public class MainApplication extends Application implements ReactApplication {
return "index";
}
@Override
protected JSIModulePackage getJSIModulePackage() {
return new ReanimatedJSIModulePackage(); // <- add
}
@Override
protected @Nullable String getBundleAssetName() {
return "app.bundle";
......
......@@ -14,7 +14,6 @@ public class BasePackageList {
new expo.modules.imageloader.ImageLoaderPackage(),
new expo.modules.keepawake.KeepAwakePackage(),
new expo.modules.localauthentication.LocalAuthenticationPackage(),
new expo.modules.permissions.PermissionsPackage(),
new expo.modules.videothumbnails.VideoThumbnailsPackage(),
new expo.modules.webbrowser.WebBrowserPackage()
);
......
......@@ -10,8 +10,8 @@ buildscript {
ext {
buildToolsVersion = "29.0.3"
minSdkVersion = 23
compileSdkVersion = 29
targetSdkVersion = 29
compileSdkVersion = 30
targetSdkVersion = 30
ndkVersion = "20.1.5948944"
glideVersion = "4.11.0"
kotlin_version = "1.3.50"
......@@ -68,10 +68,10 @@ subprojects { subproject ->
afterEvaluate {
if ((subproject.plugins.hasPlugin('android') || subproject.plugins.hasPlugin('android-library'))) {
android {
compileSdkVersion 29
compileSdkVersion 30
buildToolsVersion "29.0.3"
defaultConfig {
targetSdkVersion 29
targetSdkVersion 30
}
variantFilter { variant ->
def names = variant.flavors*.name
......
......@@ -2,8 +2,6 @@ apply from: '../node_modules/react-native-unimodules/gradle.groovy'
includeUnimodulesProjects()
rootProject.name = 'RocketChatRN'
include ':watermelondb'
project(':watermelondb').projectDir = new File(rootProject.projectDir, '../node_modules/@nozbe/watermelondb/native/android')
include ':reactnativenotifications'
project(':reactnativenotifications').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-notifications/android/app')
include ':@react-native-community_viewpager'
......
......@@ -18,8 +18,6 @@ export const ROOMS = createRequestTypes('ROOMS', [
'SET_SEARCH',
'CLOSE_SERVER_DROPDOWN',
'TOGGLE_SERVER_DROPDOWN',
'CLOSE_SORT_DROPDOWN',
'TOGGLE_SORT_DROPDOWN',
'OPEN_SEARCH_HEADER',
'CLOSE_SEARCH_HEADER'
]);
......
......@@ -45,18 +45,6 @@ export function toggleServerDropdown() {
};
}
export function closeSortDropdown() {
return {
type: types.ROOMS.CLOSE_SORT_DROPDOWN
};
}
export function toggleSortDropdown() {
return {
type: types.ROOMS.TOGGLE_SORT_DROPDOWN
};
}
export function openSearchHeader() {
return {
type: types.ROOMS.OPEN_SEARCH_HEADER
......
export const DISPLAY_MODE_CONDENSED = 'condensed';
export const DISPLAY_MODE_EXPANDED = 'expanded';
......@@ -3,7 +3,7 @@ import { Keyboard, Text } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { State, TapGestureHandler } from 'react-native-gesture-handler';
import ScrollBottomSheet from 'react-native-scroll-bottom-sheet';
import Animated, { Easing, Extrapolate, Value, interpolate } from 'react-native-reanimated';
import Animated, { Easing, Extrapolate, Value, interpolateNode } from 'react-native-reanimated';
import * as Haptics from 'expo-haptics';
import { useBackHandler } from '@react-native-community/hooks';
......@@ -132,11 +132,12 @@ const ActionSheet = React.memo(
const renderItem = ({ item }: any) => <Item item={item} hide={hide} theme={theme} />;
const animatedPosition = React.useRef(new Value(0));
const opacity = interpolate(animatedPosition.current, {
// TODO: Similar to https://github.com/wcandillon/react-native-redash/issues/307#issuecomment-827442320
const opacity = interpolateNode(animatedPosition.current, {
inputRange: [0, 1],
outputRange: [0, themes[theme].backdropOpacity],
extrapolate: Extrapolate.CLAMP
});
}) as any;
return (
<>
......
......@@ -85,7 +85,7 @@ const Avatar = React.memo(
}
return (
<View style={[avatarStyle, style]}>
<View style={[avatarStyle, style]} testID='avatar'>
{image}
{children}
</View>
......
import React from 'react';
import { ScrollView, StyleSheet, View } from 'react-native';
import { ScrollView, ScrollViewProps, StyleSheet, View } from 'react-native';
import { themes } from '../constants/colors';
import sharedStyles from '../views/Styles';
......@@ -10,10 +10,10 @@ import AppVersion from './AppVersion';
import { isTablet } from '../utils/deviceInfo';
import SafeAreaView from './SafeAreaView';
interface IFormContainer {
interface IFormContainer extends ScrollViewProps {
theme: string;
testID: string;
children: JSX.Element;
children: React.ReactNode;
}
const styles = StyleSheet.create({
......@@ -22,11 +22,11 @@ const styles = StyleSheet.create({
}
});
export const FormContainerInner = ({ children }: { children: JSX.Element }) => (
export const FormContainerInner = ({ children }: { children: React.ReactNode }): JSX.Element => (
<View style={[sharedStyles.container, isTablet && sharedStyles.tabletScreenContent]}>{children}</View>
);
const FormContainer = ({ children, theme, testID, ...props }: IFormContainer) => (
const FormContainer = ({ children, theme, testID, ...props }: IFormContainer): JSX.Element => (
// @ts-ignore
<KeyboardView
style={{ backgroundColor: themes[theme].backgroundColor }}
......
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { BorderlessButton } from 'react-native-gesture-handler';
import { StyleProp, StyleSheet, Text, TextInputProps, TextStyle, View, ViewStyle } from 'react-native';
import Touchable from 'react-native-platform-touchable';
import sharedStyles from '../views/Styles';
import TextInput from '../presentation/TextInput';
......@@ -50,23 +50,21 @@ const styles = StyleSheet.create({
}
});
interface IRCTextInputProps {
label: string;
error: {
interface IRCTextInputProps extends TextInputProps {
label?: string;
error?: {
error: any;
reason: any;
};
loading: boolean;
secureTextEntry: boolean;
containerStyle: any;
inputStyle: object;
inputRef: any;
testID: string;
iconLeft: string;
iconRight: string;
placeholder: string;
left: JSX.Element;
onIconRightPress(): void;
loading?: boolean;
containerStyle?: StyleProp<ViewStyle>;
inputStyle?: TextStyle;
inputRef?: React.Ref<unknown>;
testID?: string;
iconLeft?: string;
iconRight?: string;
left?: JSX.Element;
onIconRightPress?(): void;
theme: string;
}
......@@ -95,9 +93,9 @@ export default class RCTextInput extends React.PureComponent<IRCTextInputProps,
get iconRight() {
const { iconRight, onIconRightPress, theme } = this.props;
return (
<BorderlessButton onPress={onIconRightPress} style={[styles.iconContainer, styles.iconRight]}>
<Touchable onPress={onIconRightPress} style={[styles.iconContainer, styles.iconRight]}>
<CustomIcon name={iconRight} style={{ color: themes[theme].bodyText }} size={20} />
</BorderlessButton>
</Touchable>
);
}
......@@ -105,14 +103,14 @@ export default class RCTextInput extends React.PureComponent<IRCTextInputProps,
const { showPassword } = this.state;
const { testID, theme } = this.props;
return (
<BorderlessButton onPress={this.tooglePassword} style={[styles.iconContainer, styles.iconRight]}>
<Touchable onPress={this.tooglePassword} style={[styles.iconContainer, styles.iconRight]}>
<CustomIcon
name={showPassword ? 'unread-on-top' : 'unread-on-top-disabled'}
testID={testID ? `${testID}-icon-right` : null}
style={{ color: themes[theme].auxiliaryText }}
size={20}
/>
</BorderlessButton>
</Touchable>
);
}
......@@ -148,17 +146,10 @@ export default class RCTextInput extends React.PureComponent<IRCTextInputProps,
return (
<View style={[styles.inputContainer, containerStyle]}>
{label ? (
<Text
contentDescription={null}
// @ts-ignore
accessibilityLabel={null}
style={[styles.label, { color: themes[theme].titleText }, error.error && { color: dangerColor }]}>
{label}
</Text>
<Text style={[styles.label, { color: themes[theme].titleText }, error?.error && { color: dangerColor }]}>{label}</Text>
) : null}
<View style={styles.wrap}>
<TextInput
/* @ts-ignore*/
style={[
styles.input,
iconLeft && styles.inputIconLeft,
......@@ -168,14 +159,13 @@ export default class RCTextInput extends React.PureComponent<IRCTextInputProps,
borderColor: themes[theme].separatorColor,
color: themes[theme].titleText
},
error.error && {
error?.error && {
color: dangerColor,
borderColor: dangerColor
},
inputStyle
]}
ref={inputRef}
/* @ts-ignore*/
autoCorrect={false}
autoCapitalize='none'
underlineColorAndroid='transparent'
......@@ -183,8 +173,6 @@ export default class RCTextInput extends React.PureComponent<IRCTextInputProps,
testID={testID}
accessibilityLabel={placeholder}
placeholder={placeholder}
/* @ts-ignore*/
contentDescription={placeholder}
theme={theme}
{...inputProps}
/>
......
import React from 'react';
import { Text } from 'react-native';
import { useTheme } from '../../theme';
import { themes } from '../../constants/colors';
import styles from './styles';
import { events, logEvent } from '../../utils/log';
......@@ -9,20 +10,20 @@ interface IAtMention {
mention: string;
username: string;
navToRoomInfo: Function;
style: any;
style?: any;
useRealName: boolean;
theme: string;
mentions: any;
}
const AtMention = React.memo(({ mention, mentions, username, navToRoomInfo, style = [], useRealName, theme }: IAtMention) => {
const AtMention = React.memo(({ mention, mentions, username, navToRoomInfo, style = [], useRealName }: IAtMention) => {
const { theme } = useTheme();
if (mention === 'all' || mention === 'here') {
return (
<Text
style={[
styles.mention,
{
color: themes[theme].mentionGroupColor
color: themes[theme!].mentionGroupColor
},
...style
]}>
......@@ -34,11 +35,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
};
}
......@@ -61,7 +62,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;
import React from 'react';
import { Text } from 'react-native';
import { Text, TextStyle } from 'react-native';
import { themes } from '../../constants/colors';
import { useTheme } from '../../theme';
import styles from './styles';
interface IHashtag {
hashtag: string;
navToRoomInfo: Function;
style: [];
theme: string;
style?: TextStyle[];
channels: {
[index: number]: string | number;
name: string;
_id: number;
}[];
}
const Hashtag = React.memo(({ hashtag, channels, navToRoomInfo, style = [], theme }: IHashtag) => {
const Hashtag = React.memo(({ hashtag, channels, navToRoomInfo, style = [] }: IHashtag) => {
const { theme } = useTheme();
const handlePress = () => {
const index = channels.findIndex(channel => channel.name === hashtag);
const index = channels?.findIndex(channel => channel.name === hashtag);
const navParam = {
t: 'c',
rid: channels[index]._id
......@@ -31,7 +34,7 @@ const Hashtag = React.memo(({ hashtag, channels, navToRoomInfo, style = [], them
style={[
styles.mention,
{
color: themes[theme].mentionOtherColor
color: themes[theme!].mentionOtherColor
},
...style
]}
......@@ -40,7 +43,7 @@ const Hashtag = React.memo(({ hashtag, channels, navToRoomInfo, style = [], them
</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;
......@@ -3,6 +3,7 @@ import { Image, Text } from 'react-native';
import { Node, Parser } from 'commonmark';
import Renderer from 'commonmark-react-renderer';
import removeMarkdown from 'remove-markdown';
import { MarkdownAST } from '@rocket.chat/message-parser';
import shortnameToUnicode from '../../utils/shortnameToUnicode';
import I18n from '../../i18n';
......@@ -20,9 +21,20 @@ import MarkdownTableCell from './TableCell';
import mergeTextNodes from './mergeTextNodes';
import styles from './styles';
import { isValidURL } from '../../utils/url';
import NewMarkdown from './new';
interface IUser {
_id: string;
username: string;
name: string;
}
type UserMention = Pick<IUser, '_id' | 'username' | 'name'>;
interface IMarkdownProps {
msg: string;
md: MarkdownAST;
mentions: UserMention[];
getCustomEmoji: Function;
baseUrl: string;
username: string;
......@@ -35,7 +47,7 @@ interface IMarkdownProps {
name: string;
_id: number;
}[];
mentions: object[];
enableMessageParser: boolean;
navToRoomInfo: Function;
preview: boolean;
theme: string;
......@@ -97,7 +109,9 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
constructor(props: IMarkdownProps) {
super(props);
this.renderer = this.createRenderer();
if (!this.isNewMarkdown) {
this.renderer = this.createRenderer();
}
}
createRenderer = () =>
......@@ -139,6 +153,11 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
renderParagraphsInLists: true
});
get isNewMarkdown(): boolean {
const { md, enableMessageParser } = this.props;
return enableMessageParser && !!md;
}
editedMessage = (ast: any) => {
const { isEdited } = this.props;
if (isEdited) {
......@@ -227,12 +246,12 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
};
renderHashtag = ({ hashtag }: { hashtag: string }) => {
const { channels, navToRoomInfo, style, theme } = this.props;
return <MarkdownHashtag hashtag={hashtag} channels={channels} navToRoomInfo={navToRoomInfo} theme={theme} style={style} />;
const { channels, navToRoomInfo, style } = this.props;
return <MarkdownHashtag hashtag={hashtag} channels={channels} navToRoomInfo={navToRoomInfo} style={style} />;
};
renderAtMention = ({ mentionName }: { mentionName: string }) => {
const { username, mentions, navToRoomInfo, useRealName, style, theme } = this.props;
const { username, mentions, navToRoomInfo, useRealName, style } = this.props;
return (
<MarkdownAtMention
mentions={mentions}
......@@ -240,7 +259,6 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
useRealName={useRealName}
username={username}
navToRoomInfo={navToRoomInfo}
theme={theme}
style={style}
/>
);
......@@ -329,12 +347,44 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
};
render() {
const { msg, numberOfLines, preview = false, theme, style = [], testID } = this.props;
const {
msg,
md,
numberOfLines,
preview = false,
theme,
style = [],
testID,
mentions,
channels,
navToRoomInfo,
useRealName,
username,
getCustomEmoji,
baseUrl,
onLinkPress
} = this.props;
if (!msg) {
return null;
}
if (this.isNewMarkdown && !preview) {
return (
<NewMarkdown
username={username}
baseUrl={baseUrl}
getCustomEmoji={getCustomEmoji}
useRealName={useRealName}
tokens={md}
mentions={mentions}
channels={channels}
navToRoomInfo={navToRoomInfo}
onLinkPress={onLinkPress}
/>
);
}
let m = formatText(msg);
// Ex: '[ ](https://open.rocket.chat/group/test?msg=abcdef) Test'
......
import React from 'react';
import { StyleSheet, View } from 'react-native';
import { BigEmoji as BigEmojiProps } from '@rocket.chat/message-parser';
import Emoji from './Emoji';
interface IBigEmojiProps {
value: BigEmojiProps['value'];
}
const styles = StyleSheet.create({
container: {
flexDirection: 'row'
}
});
const BigEmoji = ({ value }: IBigEmojiProps): JSX.Element => (
<View style={styles.container}>
{value.map(block => (
<Emoji value={block.value} isBigEmoji />
))}
</View>
);
export default BigEmoji;
import React from 'react';
import { StyleSheet, Text } from 'react-native';
import { Bold as BoldProps } from '@rocket.chat/message-parser';
import sharedStyles from '../../../views/Styles';
import Strike from './Strike';
import Italic from './Italic';
import Plain from './Plain';
import Link from './Link';
interface IBoldProps {