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

Merge 4.26.0 into master (#3978)



* test

* Remove console.log

* test

* [FIX] StoryShots not working for async rendered components (#3677)

* remove console.log

* Add missing DiscussionsView snapshot

* fix build and useless done and async generator

* update snapshot

* Chore: fix build and useless done and async generator (#3678)

* fix build and useless done and async generator

* update snapshot

* Chore: Migrate Database to Typescript (#3580)

Co-authored-by: default avatarGleidson Daniel Silva <gleidson10daniel@hotmail.com>
Co-authored-by: default avatarDiego Mello <diegolmello@gmail.com>

* Chore: Migrate redux module permissions to typescript (#3630)

* Chore: Migrate redux module share to typescript (#3612)

* chore: migrate redux module share to typescript

* chore: fix types

* chore: update types

* chore: migrate redux module share to typescript

* remove double import

* chore: fix import

* Chore: Migrate redux module createChannel to typescript (#3602)

* chore: migrate ...
parent bd4d4852
......@@ -64,5 +64,6 @@ artifacts
.vscode/
e2e/docker/rc_test_env/docker-compose.yml
e2e/docker/data/db
e2e/e2e_account.js
*.p8
\ No newline at end of file
......@@ -20,6 +20,26 @@ jest.mock('react-native-file-viewer', () => ({
jest.mock('../app/lib/database', () => jest.fn(() => null));
global.Date.now = jest.fn(() => new Date('2019-10-10').getTime());
jest.mock('react-native-mmkv-storage', () => {
return {
Loader: jest.fn().mockImplementation(() => {
return {
setProcessingMode: jest.fn().mockImplementation(() => {
return {
withEncryption: jest.fn().mockImplementation(() => {
return {
initialize: jest.fn()
};
})
};
})
};
}),
create: jest.fn(),
MODES: { MULTI_PROCESS: '' }
};
});
const converter = new Stories2SnapsConverter();
initStoryshots({
......
......@@ -144,7 +144,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode VERSIONCODE as Integer
versionName "4.25.0"
versionName "4.26.0"
vectorDrawables.useSupportLibrary = true
if (!isFoss) {
manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String]
......
......@@ -12,7 +12,6 @@ import com.facebook.soloader.SoLoader;
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;
......@@ -54,7 +53,7 @@ public class MainApplication extends Application implements ReactApplication {
@Override
protected JSIModulePackage getJSIModulePackage() {
return new ReanimatedJSIModulePackage(); // <- add
return new ReanimatedJSIModulePackage();
}
@Override
......
......@@ -53,16 +53,8 @@ public class Ejson {
String alias = Utils.toHex("com.MMKV.default");
// Retrieve container password
secureKeystore.getSecureKey(alias, new RNCallback() {
@Override
public void invoke(Object... args) {
String error = (String) args[0];
if (error == null) {
String password = (String) args[1];
mmkv = MMKV.mmkvWithID("default", MMKV.SINGLE_PROCESS_MODE, password);
}
}
});
String password = secureKeystore.getSecureKey(alias);
mmkv = MMKV.mmkvWithID("default", MMKV.SINGLE_PROCESS_MODE, password);
}
public String getAvatarUri() {
......
......@@ -11,7 +11,7 @@ function createRequestTypes(base = {}, types = defaultTypes): Record<string, str
// Login events
export const LOGIN = createRequestTypes('LOGIN', [...defaultTypes, 'SET_SERVICES', 'SET_PREFERENCE', 'SET_LOCAL_AUTHENTICATED']);
export const SHARE = createRequestTypes('SHARE', ['SELECT_SERVER', 'SET_USER', 'SET_SETTINGS', 'SET_SERVER_INFO']);
export const USER = createRequestTypes('USER', ['SET']);
export const USER = createRequestTypes('USER', ['SET', 'CLEAR']);
export const ROOMS = createRequestTypes('ROOMS', [
...defaultTypes,
'REFRESH',
......
......@@ -93,6 +93,12 @@ export function setUser(user: Partial<IUser>): ISetUser {
};
}
export function clearUser(): Action {
return {
type: types.USER.CLEAR
};
}
export function setLoginServices(data: Record<string, any>): ISetServices {
return {
type: types.LOGIN.SET_SERVICES,
......
......@@ -4,7 +4,7 @@ import { ERoomType } from '../definitions/ERoomType';
import { ROOM } from './actionsTypes';
// TYPE RETURN RELATED
type ISelected = Record<string, string>;
type ISelected = string[];
export interface ITransferData {
roomId: string;
......
import { Action } from 'redux';
import { ISettings, TSettings } from '../reducers/settings';
import { TSettingsState, TSupportedSettings, TSettingsValues } from '../reducers/settings';
import { SETTINGS } from './actionsTypes';
interface IAddSettings extends Action {
payload: ISettings;
payload: TSettingsState;
}
interface IUpdateSettings extends Action {
payload: { id: string; value: TSettings };
payload: { id: TSupportedSettings; value: TSettingsValues };
}
export type IActionSettings = IAddSettings & IUpdateSettings;
export function addSettings(settings: ISettings): IAddSettings {
export function addSettings(settings: TSettingsState): IAddSettings {
return {
type: SETTINGS.ADD,
payload: settings
};
}
export function updateSettings(id: string, value: TSettings): IUpdateSettings {
export function updateSettings(id: TSupportedSettings, value: TSettingsValues): IUpdateSettings {
return {
type: SETTINGS.UPDATE,
payload: { id, value }
......
......@@ -143,8 +143,8 @@ export const deleteKeyCommands = (): void => KeyCommands.deleteKeyCommands(keyCo
export const KEY_COMMAND = 'KEY_COMMAND';
interface IKeyCommandEvent extends NativeSyntheticEvent<typeof KeyCommand> {
input: string;
export interface IKeyCommandEvent extends NativeSyntheticEvent<typeof KeyCommand> {
input: number & string;
modifierFlags: string | number;
}
......
......@@ -37,6 +37,7 @@ export const themes: any = {
infoText: '#6d6d72',
tintColor: '#1d74f5',
tintActive: '#549df9',
tintDisabled: '#88B4F5',
auxiliaryTintColor: '#6C727A',
actionTintColor: '#1d74f5',
separatorColor: '#cbcbcc',
......@@ -66,6 +67,8 @@ export const themes: any = {
previewTintColor: '#ffffff',
backdropOpacity: 0.3,
attachmentLoadingOpacity: 0.7,
collapsibleQuoteBorder: '#CBCED1',
collapsibleChevron: '#6C727A',
...mentions
},
dark: {
......@@ -85,6 +88,7 @@ export const themes: any = {
infoText: '#6D6D72',
tintColor: '#1d74f5',
tintActive: '#549df9',
tintDisabled: '#88B4F5',
auxiliaryTintColor: '#f9f9f9',
actionTintColor: '#1d74f5',
separatorColor: '#2b2b2d',
......@@ -114,6 +118,8 @@ export const themes: any = {
previewTintColor: '#ffffff',
backdropOpacity: 0.9,
attachmentLoadingOpacity: 0.3,
collapsibleQuoteBorder: '#CBCED1',
collapsibleChevron: '#6C727A',
...mentions
},
black: {
......@@ -133,6 +139,7 @@ export const themes: any = {
infoText: '#6d6d72',
tintColor: '#1e9bfe',
tintActive: '#76b7fc',
tintDisabled: '#88B4F5', // TODO: Evaluate this with design team
auxiliaryTintColor: '#f9f9f9',
actionTintColor: '#1e9bfe',
separatorColor: '#272728',
......@@ -162,6 +169,8 @@ export const themes: any = {
previewTintColor: '#ffffff',
backdropOpacity: 0.9,
attachmentLoadingOpacity: 0.3,
collapsibleQuoteBorder: '#CBCED1',
collapsibleChevron: '#6C727A',
...mentions
}
};
export const MESSAGE_TYPE_LOAD_MORE = 'load_more';
export const MESSAGE_TYPE_LOAD_PREVIOUS_CHUNK = 'load_previous_chunk';
export const MESSAGE_TYPE_LOAD_NEXT_CHUNK = 'load_next_chunk';
export enum MessageTypeLoad {
MORE = 'load_more',
PREVIOUS_CHUNK = 'load_previous_chunk',
NEXT_CHUNK = 'load_next_chunk'
}
export const MESSAGE_TYPE_ANY_LOAD = [MESSAGE_TYPE_LOAD_MORE, MESSAGE_TYPE_LOAD_PREVIOUS_CHUNK, MESSAGE_TYPE_LOAD_NEXT_CHUNK];
export const MESSAGE_TYPE_ANY_LOAD = [MessageTypeLoad.MORE, MessageTypeLoad.PREVIOUS_CHUNK, MessageTypeLoad.NEXT_CHUNK];
......@@ -206,4 +206,4 @@ export default {
Canned_Responses_Enable: {
type: 'valueAsBoolean'
}
};
} as const;
import { useBackHandler } from '@react-native-community/hooks';
import * as Haptics from 'expo-haptics';
import React, { forwardRef, isValidElement, useEffect, useImperativeHandle, useRef, useState } from 'react';
import { Keyboard, Text } from 'react-native';
import { HandlerStateChangeEventPayload, State, TapGestureHandler } from 'react-native-gesture-handler';
import Animated, { Easing, Extrapolate, interpolateNode, Value } from 'react-native-reanimated';
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, interpolateNode } from 'react-native-reanimated';
import * as Haptics from 'expo-haptics';
import { useBackHandler } from '@react-native-community/hooks';
import { Item } from './Item';
import { Handle } from './Handle';
import { Button } from './Button';
import { themes } from '../../constants/colors';
import styles, { ITEM_HEIGHT } from './styles';
import { useDimensions, useOrientation } from '../../dimensions';
import I18n from '../../i18n';
import { useTheme } from '../../theme';
import { isIOS, isTablet } from '../../utils/deviceInfo';
import * as List from '../List';
import I18n from '../../i18n';
import { IDimensionsContextProps, useDimensions, useOrientation } from '../../dimensions';
interface IActionSheetData {
options: any;
headerHeight?: number;
hasCancel?: boolean;
customHeader: any;
}
import { Button } from './Button';
import { Handle } from './Handle';
import { IActionSheetItem, Item } from './Item';
import { TActionSheetOptions, TActionSheetOptionsItem } from './Provider';
import styles, { ITEM_HEIGHT } from './styles';
const getItemLayout = (data: any, index: number) => ({ length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index });
const getItemLayout = (data: TActionSheetOptionsItem[] | null | undefined, index: number) => ({
length: ITEM_HEIGHT,
offset: ITEM_HEIGHT * index,
index
});
const HANDLE_HEIGHT = isIOS ? 40 : 56;
const MAX_SNAP_HEIGHT = 16;
......@@ -39,16 +38,17 @@ const ANIMATION_CONFIG = {
};
const ActionSheet = React.memo(
forwardRef(({ children, theme }: { children: JSX.Element; theme: string }, ref) => {
const bottomSheetRef: any = useRef();
const [data, setData] = useState<IActionSheetData>({} as IActionSheetData);
forwardRef(({ children }: { children: React.ReactElement }, ref) => {
const { theme } = useTheme();
const bottomSheetRef = useRef<ScrollBottomSheet<TActionSheetOptionsItem>>(null);
const [data, setData] = useState<TActionSheetOptions>({} as TActionSheetOptions);
const [isVisible, setVisible] = useState(false);
const { height }: Partial<IDimensionsContextProps> = useDimensions();
const { height } = useDimensions();
const { isLandscape } = useOrientation();
const insets = useSafeAreaInsets();
const maxSnap = Math.max(
height! -
height -
// Items height
ITEM_HEIGHT * (data?.options?.length || 0) -
// Handle height
......@@ -69,7 +69,7 @@ const ActionSheet = React.memo(
* we'll provide more one snap
* that point 50% of the whole screen
*/
const snaps: any = height! - maxSnap > height! * 0.6 && !isLandscape ? [maxSnap, height! * 0.5, height] : [maxSnap, height];
const snaps = height - maxSnap > height * 0.6 && !isLandscape ? [maxSnap, height * 0.5, height] : [maxSnap, height];
const openedSnapIndex = snaps.length > 2 ? 1 : 0;
const closedSnapIndex = snaps.length - 1;
......@@ -79,12 +79,12 @@ const ActionSheet = React.memo(
bottomSheetRef.current?.snapTo(closedSnapIndex);
};
const show = (options: any) => {
const show = (options: TActionSheetOptions) => {
setData(options);
toggleVisible();
};
const onBackdropPressed = ({ nativeEvent }: any) => {
const onBackdropPressed = ({ nativeEvent }: { nativeEvent: HandlerStateChangeEventPayload }) => {
if (nativeEvent.oldState === State.ACTIVE) {
hide();
}
......@@ -117,7 +117,7 @@ const ActionSheet = React.memo(
const renderHandle = () => (
<>
<Handle theme={theme} />
<Handle />
{isValidElement(data?.customHeader) ? data.customHeader : null}
</>
);
......@@ -127,21 +127,23 @@ const ActionSheet = React.memo(
<Button
onPress={hide}
style={[styles.button, { backgroundColor: themes[theme].auxiliaryBackground }]}
// TODO: Remove when migrate Touch
theme={theme}
accessibilityLabel={I18n.t('Cancel')}>
<Text style={[styles.text, { color: themes[theme].bodyText }]}>{I18n.t('Cancel')}</Text>
</Button>
) : null;
const renderItem = ({ item }: any) => <Item item={item} hide={hide} theme={theme} />;
const renderItem = ({ item }: { item: IActionSheetItem['item'] }) => <Item item={item} hide={hide} />;
const animatedPosition = React.useRef(new Value(0));
// 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;
}) as any; // The function's return differs from the expected type of opacity, however this problem is something related to lib, maybe when updating the types will be fixed.
const bottomSheet = isLandscape || isTablet ? styles.bottomSheet : {};
return (
<>
......@@ -160,7 +162,7 @@ const ActionSheet = React.memo(
]}
/>
</TapGestureHandler>
<ScrollBottomSheet
<ScrollBottomSheet<TActionSheetOptionsItem>
testID='action-sheet'
ref={bottomSheetRef}
componentType='FlatList'
......@@ -169,18 +171,11 @@ const ActionSheet = React.memo(
renderHandle={renderHandle}
onSettle={index => index === closedSnapIndex && toggleVisible()}
animatedPosition={animatedPosition.current}
containerStyle={
[
styles.container,
{ backgroundColor: themes[theme].focusedBackground },
(isLandscape || isTablet) && styles.bottomSheet
] as any
}
containerStyle={{ ...styles.container, ...bottomSheet, backgroundColor: themes[theme].focusedBackground }}
animationConfig={ANIMATION_CONFIG}
// FlatList props
data={data?.options}
data={data.options}
renderItem={renderItem}
keyExtractor={(item: any) => item.title}
keyExtractor={item => item.title}
style={{ backgroundColor: themes[theme].focusedBackground }}
contentContainerStyle={styles.content}
ItemSeparatorComponent={List.Separator}
......
......@@ -3,9 +3,13 @@ import { View } from 'react-native';
import styles from './styles';
import { themes } from '../../constants/colors';
import { useTheme } from '../../theme';
export const Handle = React.memo(({ theme }: { theme: string }) => (
<View style={[styles.handle, { backgroundColor: themes[theme].focusedBackground }]} testID='action-sheet-handle'>
<View style={[styles.handleIndicator, { backgroundColor: themes[theme].auxiliaryText }]} />
</View>
));
export const Handle = React.memo(() => {
const { theme } = useTheme();
return (
<View style={[styles.handle, { backgroundColor: themes[theme].focusedBackground }]} testID='action-sheet-handle'>
<View style={[styles.handleIndicator, { backgroundColor: themes[theme].auxiliaryText }]} />
</View>
);
});
......@@ -3,23 +3,24 @@ import { Text, View } from 'react-native';
import { themes } from '../../constants/colors';
import { CustomIcon } from '../../lib/Icons';
import { useTheme } from '../../theme';
import { Button } from './Button';
import styles from './styles';
interface IActionSheetItem {
export interface IActionSheetItem {
item: {
title: string;
icon: string;
danger: boolean;
testID: string;
onPress(): void;
right: Function;
danger?: boolean;
testID?: string;
onPress: () => void;
right?: Function;
};
theme: string;
hide(): void;
}
export const Item = React.memo(({ item, hide, theme }: IActionSheetItem) => {
export const Item = React.memo(({ item, hide }: IActionSheetItem) => {
const { theme } = useTheme();
const onPress = () => {
hide();
item?.onPress();
......
import React, { ForwardedRef, forwardRef, useContext, useRef } from 'react';
import ActionSheet from './ActionSheet';
import { useTheme } from '../../theme';
export type TActionSheetOptionsItem = { title: string; icon: string; onPress: () => void };
export type TActionSheetOptions = {
options: TActionSheetOptionsItem[];
headerHeight: number;
customHeader: React.ReactElement | null;
hasCancel?: boolean;
};
interface IActionSheetProvider {
Provider: any;
Consumer: any;
showActionSheet: (item: TActionSheetOptions) => void;
hideActionSheet: () => void;
}
const context: IActionSheetProvider = React.createContext({
const context = React.createContext<IActionSheetProvider>({
showActionSheet: () => {},
hideActionSheet: () => {}
});
......@@ -17,17 +24,16 @@ export const useActionSheet = () => useContext(context);
const { Provider, Consumer } = context;
export const withActionSheet = (Component: any): any =>
forwardRef((props: any, ref: ForwardedRef<any>) => (
<Consumer>{(contexts: any) => <Component {...props} {...contexts} ref={ref} />}</Consumer>
export const withActionSheet = (Component: React.ComponentType<any>): typeof Component =>
forwardRef((props: typeof React.Component, ref: ForwardedRef<IActionSheetProvider>) => (
<Consumer>{(contexts: IActionSheetProvider) => <Component {...props} {...contexts} ref={ref} />}</Consumer>
));
export const ActionSheetProvider = React.memo(({ children }: { children: JSX.Element | JSX.Element[] }) => {
const ref: ForwardedRef<any> = useRef();
const { theme }: any = useTheme();
export const ActionSheetProvider = React.memo(({ children }: { children: React.ReactElement | React.ReactElement[] }) => {
const ref: ForwardedRef<IActionSheetProvider> = useRef(null);
const getContext = () => ({
showActionSheet: (options: any) => {
showActionSheet: (options: TActionSheetOptions) => {
ref.current?.showActionSheet(options);
},
hideActionSheet: () => {
......@@ -37,7 +43,7 @@ export const ActionSheetProvider = React.memo(({ children }: { children: JSX.Ele
return (
<Provider value={getContext()}>
<ActionSheet ref={ref} theme={theme}>
<ActionSheet ref={ref}>
<>{children}</>
</ActionSheet>
</Provider>
......
import React from 'react';
import { ActivityIndicator, ActivityIndicatorProps, StyleSheet } from 'react-native';
import { useTheme } from '../theme';
import { themes } from '../constants/colors';
type TTheme = 'light' | 'dark' | 'black' | string;
interface IActivityIndicator extends ActivityIndicatorProps {
theme?: TTheme;
absolute?: boolean;
props?: object;
}
const styles = StyleSheet.create({
......@@ -27,8 +24,11 @@ const styles = StyleSheet.create({
}
});
const RCActivityIndicator = ({ theme = 'light', absolute, ...props }: IActivityIndicator) => (
<ActivityIndicator style={[styles.indicator, absolute && styles.absolute]} color={themes[theme].auxiliaryText} {...props} />
);
const RCActivityIndicator = ({ absolute, ...props }: IActivityIndicator): React.ReactElement => {
const { theme } = useTheme();
return (
<ActivityIndicator style={[styles.indicator, absolute && styles.absolute]} color={themes[theme].auxiliaryText} {...props} />
);
};
export default RCActivityIndicator;
......@@ -37,6 +37,21 @@ class AvatarContainer extends React.Component<IAvatar, any> {
}
}
shouldComponentUpdate(nextProps: IAvatar, nextState: { avatarETag: string }) {
const { avatarETag } = this.state;
const { text, type } = this.props;
if (nextState.avatarETag !== avatarETag) {
return true;
}
if (nextProps.text !== text) {
return true;
}
if (nextProps.type !== type) {
return true;
}
return false;
}
componentWillUnmount() {
if (this.subscription?.unsubscribe) {
this.subscription.unsubscribe();
......
import React from 'react';
import { TGetCustomEmoji } from '../../definitions/IEmoji';
export interface IAvatar {
......@@ -9,7 +11,7 @@ export interface IAvatar {
size?: number;
borderRadius?: number;
type?: string;
children?: JSX.Element;
children?: React.ReactElement | null;
user?: {
id?: string;
token?: string;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment