Merge develop into beta 4.26.0-rc.1

This commit is contained in:
Diego Mello 2022-03-23 13:40:57 -03:00 committed by GitHub
commit 062dace1f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
49 changed files with 422 additions and 361 deletions

View File

@ -1,30 +1,29 @@
import { useBackHandler } from '@react-native-community/hooks';
import * as Haptics from 'expo-haptics';
import React, { forwardRef, isValidElement, useEffect, useImperativeHandle, useRef, useState } from 'react'; import React, { forwardRef, isValidElement, useEffect, useImperativeHandle, useRef, useState } from 'react';
import { Keyboard, Text } from 'react-native'; 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 { useSafeAreaInsets } from 'react-native-safe-area-context';
import { State, TapGestureHandler } from 'react-native-gesture-handler';
import ScrollBottomSheet from 'react-native-scroll-bottom-sheet'; 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 { 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 { isIOS, isTablet } from '../../utils/deviceInfo';
import * as List from '../List'; import * as List from '../List';
import I18n from '../../i18n'; import { Button } from './Button';
import { IDimensionsContextProps, useDimensions, useOrientation } from '../../dimensions'; import { Handle } from './Handle';
import { IActionSheetItem, Item } from './Item';
import { TActionSheetOptions, TActionSheetOptionsItem } from './Provider';
import styles, { ITEM_HEIGHT } from './styles';
interface IActionSheetData { const getItemLayout = (data: TActionSheetOptionsItem[] | null | undefined, index: number) => ({
options: any; length: ITEM_HEIGHT,
headerHeight?: number; offset: ITEM_HEIGHT * index,
hasCancel?: boolean; index
customHeader: any; });
}
const getItemLayout = (data: any, index: number) => ({ length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index });
const HANDLE_HEIGHT = isIOS ? 40 : 56; const HANDLE_HEIGHT = isIOS ? 40 : 56;
const MAX_SNAP_HEIGHT = 16; const MAX_SNAP_HEIGHT = 16;
@ -39,16 +38,17 @@ const ANIMATION_CONFIG = {
}; };
const ActionSheet = React.memo( const ActionSheet = React.memo(
forwardRef(({ children, theme }: { children: JSX.Element; theme: string }, ref) => { forwardRef(({ children }: { children: React.ReactElement }, ref) => {
const bottomSheetRef: any = useRef(); const { theme } = useTheme();
const [data, setData] = useState<IActionSheetData>({} as IActionSheetData); const bottomSheetRef = useRef<ScrollBottomSheet<TActionSheetOptionsItem>>(null);
const [data, setData] = useState<TActionSheetOptions>({} as TActionSheetOptions);
const [isVisible, setVisible] = useState(false); const [isVisible, setVisible] = useState(false);
const { height }: Partial<IDimensionsContextProps> = useDimensions(); const { height } = useDimensions();
const { isLandscape } = useOrientation(); const { isLandscape } = useOrientation();
const insets = useSafeAreaInsets(); const insets = useSafeAreaInsets();
const maxSnap = Math.max( const maxSnap = Math.max(
height! - height -
// Items height // Items height
ITEM_HEIGHT * (data?.options?.length || 0) - ITEM_HEIGHT * (data?.options?.length || 0) -
// Handle height // Handle height
@ -69,7 +69,7 @@ const ActionSheet = React.memo(
* we'll provide more one snap * we'll provide more one snap
* that point 50% of the whole screen * 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 openedSnapIndex = snaps.length > 2 ? 1 : 0;
const closedSnapIndex = snaps.length - 1; const closedSnapIndex = snaps.length - 1;
@ -79,12 +79,12 @@ const ActionSheet = React.memo(
bottomSheetRef.current?.snapTo(closedSnapIndex); bottomSheetRef.current?.snapTo(closedSnapIndex);
}; };
const show = (options: any) => { const show = (options: TActionSheetOptions) => {
setData(options); setData(options);
toggleVisible(); toggleVisible();
}; };
const onBackdropPressed = ({ nativeEvent }: any) => { const onBackdropPressed = ({ nativeEvent }: { nativeEvent: HandlerStateChangeEventPayload }) => {
if (nativeEvent.oldState === State.ACTIVE) { if (nativeEvent.oldState === State.ACTIVE) {
hide(); hide();
} }
@ -117,7 +117,7 @@ const ActionSheet = React.memo(
const renderHandle = () => ( const renderHandle = () => (
<> <>
<Handle theme={theme} /> <Handle />
{isValidElement(data?.customHeader) ? data.customHeader : null} {isValidElement(data?.customHeader) ? data.customHeader : null}
</> </>
); );
@ -127,21 +127,23 @@ const ActionSheet = React.memo(
<Button <Button
onPress={hide} onPress={hide}
style={[styles.button, { backgroundColor: themes[theme].auxiliaryBackground }]} style={[styles.button, { backgroundColor: themes[theme].auxiliaryBackground }]}
// TODO: Remove when migrate Touch
theme={theme} theme={theme}
accessibilityLabel={I18n.t('Cancel')}> accessibilityLabel={I18n.t('Cancel')}>
<Text style={[styles.text, { color: themes[theme].bodyText }]}>{I18n.t('Cancel')}</Text> <Text style={[styles.text, { color: themes[theme].bodyText }]}>{I18n.t('Cancel')}</Text>
</Button> </Button>
) : null; ) : 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)); 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, { const opacity = interpolateNode(animatedPosition.current, {
inputRange: [0, 1], inputRange: [0, 1],
outputRange: [0, themes[theme].backdropOpacity], outputRange: [0, themes[theme].backdropOpacity],
extrapolate: Extrapolate.CLAMP 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 ( return (
<> <>
@ -160,7 +162,7 @@ const ActionSheet = React.memo(
]} ]}
/> />
</TapGestureHandler> </TapGestureHandler>
<ScrollBottomSheet <ScrollBottomSheet<TActionSheetOptionsItem>
testID='action-sheet' testID='action-sheet'
ref={bottomSheetRef} ref={bottomSheetRef}
componentType='FlatList' componentType='FlatList'
@ -169,18 +171,11 @@ const ActionSheet = React.memo(
renderHandle={renderHandle} renderHandle={renderHandle}
onSettle={index => index === closedSnapIndex && toggleVisible()} onSettle={index => index === closedSnapIndex && toggleVisible()}
animatedPosition={animatedPosition.current} animatedPosition={animatedPosition.current}
containerStyle={ containerStyle={{ ...styles.container, ...bottomSheet, backgroundColor: themes[theme].focusedBackground }}
[
styles.container,
{ backgroundColor: themes[theme].focusedBackground },
(isLandscape || isTablet) && styles.bottomSheet
] as any
}
animationConfig={ANIMATION_CONFIG} animationConfig={ANIMATION_CONFIG}
// FlatList props data={data.options}
data={data?.options}
renderItem={renderItem} renderItem={renderItem}
keyExtractor={(item: any) => item.title} keyExtractor={item => item.title}
style={{ backgroundColor: themes[theme].focusedBackground }} style={{ backgroundColor: themes[theme].focusedBackground }}
contentContainerStyle={styles.content} contentContainerStyle={styles.content}
ItemSeparatorComponent={List.Separator} ItemSeparatorComponent={List.Separator}

View File

@ -3,9 +3,13 @@ import { View } from 'react-native';
import styles from './styles'; import styles from './styles';
import { themes } from '../../constants/colors'; import { themes } from '../../constants/colors';
import { useTheme } from '../../theme';
export const Handle = React.memo(({ theme }: { theme: string }) => ( export const Handle = React.memo(() => {
<View style={[styles.handle, { backgroundColor: themes[theme].focusedBackground }]} testID='action-sheet-handle'> const { theme } = useTheme();
<View style={[styles.handleIndicator, { backgroundColor: themes[theme].auxiliaryText }]} /> return (
</View> <View style={[styles.handle, { backgroundColor: themes[theme].focusedBackground }]} testID='action-sheet-handle'>
)); <View style={[styles.handleIndicator, { backgroundColor: themes[theme].auxiliaryText }]} />
</View>
);
});

View File

@ -3,23 +3,24 @@ import { Text, View } from 'react-native';
import { themes } from '../../constants/colors'; import { themes } from '../../constants/colors';
import { CustomIcon } from '../../lib/Icons'; import { CustomIcon } from '../../lib/Icons';
import { useTheme } from '../../theme';
import { Button } from './Button'; import { Button } from './Button';
import styles from './styles'; import styles from './styles';
interface IActionSheetItem { export interface IActionSheetItem {
item: { item: {
title: string; title: string;
icon: string; icon: string;
danger: boolean; danger?: boolean;
testID: string; testID?: string;
onPress(): void; onPress: () => void;
right: Function; right?: Function;
}; };
theme: string;
hide(): void; hide(): void;
} }
export const Item = React.memo(({ item, hide, theme }: IActionSheetItem) => { export const Item = React.memo(({ item, hide }: IActionSheetItem) => {
const { theme } = useTheme();
const onPress = () => { const onPress = () => {
hide(); hide();
item?.onPress(); item?.onPress();

View File

@ -1,14 +1,21 @@
import React, { ForwardedRef, forwardRef, useContext, useRef } from 'react'; import React, { ForwardedRef, forwardRef, useContext, useRef } from 'react';
import ActionSheet from './ActionSheet'; 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 { interface IActionSheetProvider {
Provider: any; showActionSheet: (item: TActionSheetOptions) => void;
Consumer: any; hideActionSheet: () => void;
} }
const context: IActionSheetProvider = React.createContext({ const context = React.createContext<IActionSheetProvider>({
showActionSheet: () => {}, showActionSheet: () => {},
hideActionSheet: () => {} hideActionSheet: () => {}
}); });
@ -17,17 +24,16 @@ export const useActionSheet = () => useContext(context);
const { Provider, Consumer } = context; const { Provider, Consumer } = context;
export const withActionSheet = (Component: any): any => export const withActionSheet = (Component: React.ComponentType<any>): typeof Component =>
forwardRef((props: any, ref: ForwardedRef<any>) => ( forwardRef((props: typeof React.Component, ref: ForwardedRef<IActionSheetProvider>) => (
<Consumer>{(contexts: any) => <Component {...props} {...contexts} ref={ref} />}</Consumer> <Consumer>{(contexts: IActionSheetProvider) => <Component {...props} {...contexts} ref={ref} />}</Consumer>
)); ));
export const ActionSheetProvider = React.memo(({ children }: { children: JSX.Element | JSX.Element[] }) => { export const ActionSheetProvider = React.memo(({ children }: { children: React.ReactElement | React.ReactElement[] }) => {
const ref: ForwardedRef<any> = useRef(); const ref: ForwardedRef<IActionSheetProvider> = useRef(null);
const { theme }: any = useTheme();
const getContext = () => ({ const getContext = () => ({
showActionSheet: (options: any) => { showActionSheet: (options: TActionSheetOptions) => {
ref.current?.showActionSheet(options); ref.current?.showActionSheet(options);
}, },
hideActionSheet: () => { hideActionSheet: () => {
@ -37,7 +43,7 @@ export const ActionSheetProvider = React.memo(({ children }: { children: JSX.Ele
return ( return (
<Provider value={getContext()}> <Provider value={getContext()}>
<ActionSheet ref={ref} theme={theme}> <ActionSheet ref={ref}>
<>{children}</> <>{children}</>
</ActionSheet> </ActionSheet>
</Provider> </Provider>

View File

@ -3,11 +3,8 @@ import { StyleSheet } from 'react-native';
import { CustomIcon } from '../lib/Icons'; import { CustomIcon } from '../lib/Icons';
import { themes } from '../constants/colors'; import { themes } from '../constants/colors';
import { useTheme } from '../theme';
interface ICheck {
style?: object;
theme: string;
}
const styles = StyleSheet.create({ const styles = StyleSheet.create({
icon: { icon: {
width: 22, width: 22,
@ -16,8 +13,9 @@ const styles = StyleSheet.create({
} }
}); });
const Check = React.memo(({ theme, style }: ICheck) => ( const Check = React.memo(() => {
<CustomIcon style={[styles.icon, style]} color={themes[theme].tintColor} size={22} name='check' /> const { theme } = useTheme();
)); return <CustomIcon style={styles.icon} color={themes[theme].tintColor} size={22} name='check' />;
});
export default Check; export default Check;

View File

@ -14,9 +14,18 @@ import { ROW_HEIGHT } from '../../presentation/RoomItem';
import { goRoom } from '../../utils/goRoom'; import { goRoom } from '../../utils/goRoom';
import Navigation from '../../lib/Navigation'; import Navigation from '../../lib/Navigation';
import { useOrientation } from '../../dimensions'; import { useOrientation } from '../../dimensions';
import { IApplicationState, ISubscription, SubscriptionType } from '../../definitions';
interface INotifierComponent { export interface INotifierComponent {
notification: object; notification: {
text: string;
payload: {
sender: { username: string };
type: SubscriptionType;
} & Pick<ISubscription, '_id' | 'name' | 'rid' | 'prid'>;
title: string;
avatar: string;
};
isMasterDetail: boolean; isMasterDetail: boolean;
} }
@ -67,15 +76,15 @@ const styles = StyleSheet.create({
const hideNotification = () => Notifier.hideNotification(); const hideNotification = () => Notifier.hideNotification();
const NotifierComponent = React.memo(({ notification, isMasterDetail }: INotifierComponent) => { const NotifierComponent = React.memo(({ notification, isMasterDetail }: INotifierComponent) => {
const { theme }: any = useTheme(); const { theme } = useTheme();
const insets = useSafeAreaInsets(); const insets = useSafeAreaInsets();
const { isLandscape } = useOrientation(); const { isLandscape } = useOrientation();
const { text, payload }: any = notification; const { text, payload } = notification;
const { type, rid } = payload; const { type, rid } = payload;
const name = type === 'd' ? payload.sender.username : payload.name; const name = type === 'd' ? payload.sender.username : payload.name;
// if sub is not on local database, title and avatar will be null, so we use payload from notification // if sub is not on local database, title and avatar will be null, so we use payload from notification
const { title = name, avatar = name }: any = notification; const { title = name, avatar = name } = notification;
const onPress = () => { const onPress = () => {
const { prid, _id } = payload; const { prid, _id } = payload;
@ -133,7 +142,7 @@ const NotifierComponent = React.memo(({ notification, isMasterDetail }: INotifie
); );
}); });
const mapStateToProps = (state: any) => ({ const mapStateToProps = (state: IApplicationState) => ({
isMasterDetail: state.app.isMasterDetail isMasterDetail: state.app.isMasterDetail
}); });

View File

@ -3,16 +3,18 @@ import { Easing, Notifier, NotifierRoot } from 'react-native-notifier';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { dequal } from 'dequal'; import { dequal } from 'dequal';
import NotifierComponent from './NotifierComponent'; import NotifierComponent, { INotifierComponent } from './NotifierComponent';
import EventEmitter from '../../utils/events'; import EventEmitter from '../../utils/events';
import Navigation from '../../lib/Navigation'; import Navigation from '../../lib/Navigation';
import { getActiveRoute } from '../../utils/navigation'; import { getActiveRoute } from '../../utils/navigation';
import { IApplicationState } from '../../definitions';
import { IRoom } from '../../reducers/room';
export const INAPP_NOTIFICATION_EMITTER = 'NotificationInApp'; export const INAPP_NOTIFICATION_EMITTER = 'NotificationInApp';
const InAppNotification = memo( const InAppNotification = memo(
({ rooms, appState }: { rooms: any; appState: string }) => { ({ rooms, appState }: { rooms: IRoom['rooms']; appState: string }) => {
const show = (notification: any) => { const show = (notification: INotifierComponent['notification']) => {
if (appState !== 'foreground') { if (appState !== 'foreground') {
return; return;
} }
@ -46,7 +48,7 @@ const InAppNotification = memo(
(prevProps, nextProps) => dequal(prevProps.rooms, nextProps.rooms) (prevProps, nextProps) => dequal(prevProps.rooms, nextProps.rooms)
); );
const mapStateToProps = (state: any) => ({ const mapStateToProps = (state: IApplicationState) => ({
rooms: state.room.rooms, rooms: state.room.rooms,
appState: state.app.ready && state.app.foreground ? 'foreground' : 'background' appState: state.app.ready && state.app.foreground ? 'foreground' : 'background'
}); });

View File

@ -3,6 +3,7 @@ import { Animated, Easing, Linking, StyleSheet, Text, View } from 'react-native'
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Base64 } from 'js-base64'; import { Base64 } from 'js-base64';
import * as AppleAuthentication from 'expo-apple-authentication'; import * as AppleAuthentication from 'expo-apple-authentication';
import { StackNavigationProp } from '@react-navigation/stack';
import { withTheme } from '../theme'; import { withTheme } from '../theme';
import sharedStyles from '../views/Styles'; import sharedStyles from '../views/Styles';
@ -15,6 +16,9 @@ import random from '../utils/random';
import { events, logEvent } from '../utils/log'; import { events, logEvent } from '../utils/log';
import RocketChat from '../lib/rocketchat'; import RocketChat from '../lib/rocketchat';
import { CustomIcon } from '../lib/Icons'; import { CustomIcon } from '../lib/Icons';
import { IServices } from '../selectors/login';
import { OutsideParamList } from '../stacks/types';
import { IApplicationState } from '../definitions';
const BUTTON_HEIGHT = 48; const BUTTON_HEIGHT = 48;
const SERVICE_HEIGHT = 58; const SERVICE_HEIGHT = 58;
@ -58,31 +62,40 @@ const styles = StyleSheet.create({
}); });
interface IOpenOAuth { interface IOpenOAuth {
url?: string; url: string;
ssoToken?: string; ssoToken?: string;
authType?: string; authType?: string;
} }
interface IService { interface IItemService {
name: string; name: string;
service: string; service: string;
authType: string; authType: string;
buttonColor: string; buttonColor: string;
buttonLabelColor: string; buttonLabelColor: string;
clientConfig: { provider: string };
serverURL: string;
authorizePath: string;
clientId: string;
scope: string;
}
interface IOauthProvider {
[key: string]: () => void;
facebook: () => void;
github: () => void;
gitlab: () => void;
google: () => void;
linkedin: () => void;
'meteor-developer': () => void;
twitter: () => void;
wordpress: () => void;
} }
interface ILoginServicesProps { interface ILoginServicesProps {
navigation: any; navigation: StackNavigationProp<OutsideParamList>;
server: string; server: string;
services: { services: IServices;
facebook: { clientId: string };
github: { clientId: string };
gitlab: { clientId: string };
google: { clientId: string };
linkedin: { clientId: string };
'meteor-developer': { clientId: string };
wordpress: { clientId: string; serverURL: string };
};
Gitlab_URL: string; Gitlab_URL: string;
CAS_enabled: boolean; CAS_enabled: boolean;
CAS_login_url: string; CAS_login_url: string;
@ -90,12 +103,13 @@ interface ILoginServicesProps {
theme: string; theme: string;
} }
class LoginServices extends React.PureComponent<ILoginServicesProps, any> { interface ILoginServicesState {
private _animation: any; collapsed: boolean;
servicesHeight: Animated.Value;
}
static defaultProps = { class LoginServices extends React.PureComponent<ILoginServicesProps, ILoginServicesState> {
separator: true private _animation?: Animated.CompositeAnimation | void;
};
state = { state = {
collapsed: true, collapsed: true,
@ -194,7 +208,7 @@ class LoginServices extends React.PureComponent<ILoginServicesProps, any> {
this.openOAuth({ url: `${endpoint}${params}` }); this.openOAuth({ url: `${endpoint}${params}` });
}; };
onPressCustomOAuth = (loginService: any) => { onPressCustomOAuth = (loginService: IItemService) => {
logEvent(events.ENTER_WITH_CUSTOM_OAUTH); logEvent(events.ENTER_WITH_CUSTOM_OAUTH);
const { server } = this.props; const { server } = this.props;
const { serverURL, authorizePath, clientId, scope, service } = loginService; const { serverURL, authorizePath, clientId, scope, service } = loginService;
@ -207,7 +221,7 @@ class LoginServices extends React.PureComponent<ILoginServicesProps, any> {
this.openOAuth({ url }); this.openOAuth({ url });
}; };
onPressSaml = (loginService: any) => { onPressSaml = (loginService: IItemService) => {
logEvent(events.ENTER_WITH_SAML); logEvent(events.ENTER_WITH_SAML);
const { server } = this.props; const { server } = this.props;
const { clientConfig } = loginService; const { clientConfig } = loginService;
@ -234,7 +248,6 @@ class LoginServices extends React.PureComponent<ILoginServicesProps, any> {
AppleAuthentication.AppleAuthenticationScope.EMAIL AppleAuthentication.AppleAuthenticationScope.EMAIL
] ]
}); });
// @ts-ignore
await RocketChat.loginOAuthOrSso({ fullName, email, identityToken }); await RocketChat.loginOAuthOrSso({ fullName, email, identityToken });
} catch { } catch {
logEvent(events.ENTER_WITH_APPLE_F); logEvent(events.ENTER_WITH_APPLE_F);
@ -243,7 +256,12 @@ class LoginServices extends React.PureComponent<ILoginServicesProps, any> {
getOAuthState = (loginStyle = LOGIN_STYPE_POPUP) => { getOAuthState = (loginStyle = LOGIN_STYPE_POPUP) => {
const credentialToken = random(43); const credentialToken = random(43);
let obj: any = { loginStyle, credentialToken, isCordova: true }; let obj: {
loginStyle: string;
credentialToken: string;
isCordova: boolean;
redirectUrl?: string;
} = { loginStyle, credentialToken, isCordova: true };
if (loginStyle === LOGIN_STYPE_REDIRECT) { if (loginStyle === LOGIN_STYPE_REDIRECT) {
obj = { obj = {
...obj, ...obj,
@ -263,12 +281,11 @@ class LoginServices extends React.PureComponent<ILoginServicesProps, any> {
if (this._animation) { if (this._animation) {
this._animation.stop(); this._animation.stop();
} }
// @ts-ignore
this._animation = Animated.timing(servicesHeight, { this._animation = Animated.timing(servicesHeight, {
toValue: height, toValue: height,
duration: 300, duration: 300,
// @ts-ignore easing: Easing.inOut(Easing.quad),
easing: Easing.easeOutCubic useNativeDriver: true
}).start(); }).start();
}; };
@ -281,11 +298,11 @@ class LoginServices extends React.PureComponent<ILoginServicesProps, any> {
} else { } else {
this.transitionServicesTo(SERVICES_COLLAPSED_HEIGHT); this.transitionServicesTo(SERVICES_COLLAPSED_HEIGHT);
} }
this.setState((prevState: any) => ({ collapsed: !prevState.collapsed })); this.setState((prevState: ILoginServicesState) => ({ collapsed: !prevState.collapsed }));
}; };
getSocialOauthProvider = (name: string) => { getSocialOauthProvider = (name: string) => {
const oauthProviders: any = { const oauthProviders: IOauthProvider = {
facebook: this.onPressFacebook, facebook: this.onPressFacebook,
github: this.onPressGithub, github: this.onPressGithub,
gitlab: this.onPressGitlab, gitlab: this.onPressGitlab,
@ -324,7 +341,7 @@ class LoginServices extends React.PureComponent<ILoginServicesProps, any> {
return null; return null;
}; };
renderItem = (service: IService) => { renderItem = (service: IItemService) => {
const { CAS_enabled, theme } = this.props; const { CAS_enabled, theme } = this.props;
let { name } = service; let { name } = service;
name = name === 'meteor-developer' ? 'meteor' : name; name = name === 'meteor-developer' ? 'meteor' : name;
@ -401,26 +418,28 @@ class LoginServices extends React.PureComponent<ILoginServicesProps, any> {
if (length > 3 && separator) { if (length > 3 && separator) {
return ( return (
<> <>
<Animated.View style={style}>{Object.values(services).map((service: any) => this.renderItem(service))}</Animated.View> <Animated.View style={style}>
{Object.values(services).map((service: IItemService) => this.renderItem(service))}
</Animated.View>
{this.renderServicesSeparator()} {this.renderServicesSeparator()}
</> </>
); );
} }
return ( return (
<> <>
{Object.values(services).map((service: any) => this.renderItem(service))} {Object.values(services).map((service: IItemService) => this.renderItem(service))}
{this.renderServicesSeparator()} {this.renderServicesSeparator()}
</> </>
); );
} }
} }
const mapStateToProps = (state: any) => ({ const mapStateToProps = (state: IApplicationState) => ({
server: state.server.server, server: state.server.server,
Gitlab_URL: state.settings.API_Gitlab_URL, Gitlab_URL: state.settings.API_Gitlab_URL as string,
CAS_enabled: state.settings.CAS_enabled, CAS_enabled: state.settings.CAS_enabled as boolean,
CAS_login_url: state.settings.CAS_login_url, CAS_login_url: state.settings.CAS_login_url as string,
services: state.login.services services: state.login.services as IServices
}); });
export default connect(mapStateToProps)(withTheme(LoginServices)) as any; export default connect(mapStateToProps)(withTheme(LoginServices));

View File

@ -9,7 +9,7 @@ import { Q } from '@nozbe/watermelondb';
import { TouchableWithoutFeedback } from 'react-native-gesture-handler'; import { TouchableWithoutFeedback } from 'react-native-gesture-handler';
import { generateTriggerId } from '../../lib/methods/actions'; import { generateTriggerId } from '../../lib/methods/actions';
import TextInput from '../../presentation/TextInput'; import TextInput, { IThemedTextInput } from '../../presentation/TextInput';
import { userTyping as userTypingAction } from '../../actions/room'; import { userTyping as userTypingAction } from '../../actions/room';
import RocketChat from '../../lib/rocketchat'; import RocketChat from '../../lib/rocketchat';
import styles from './styles'; import styles from './styles';
@ -1038,7 +1038,7 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
tmid tmid
} = this.props; } = this.props;
const isAndroidTablet = const isAndroidTablet: Partial<IThemedTextInput> =
isTablet && isAndroid isTablet && isAndroid
? { ? {
multiline: false, multiline: false,
@ -1090,7 +1090,6 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
<TextInput <TextInput
ref={component => (this.component = component)} ref={component => (this.component = component)}
style={[styles.textBoxInput, { color: themes[theme].bodyText }]} style={[styles.textBoxInput, { color: themes[theme].bodyText }]}
// @ts-ignore
returnKeyType='default' returnKeyType='default'
keyboardType='twitter' keyboardType='twitter'
blurOnSubmit={false} blurOnSubmit={false}

View File

@ -1,4 +1,5 @@
import { forwardRef, useImperativeHandle } from 'react'; import { forwardRef, useImperativeHandle } from 'react';
import Model from '@nozbe/watermelondb/Model';
import RocketChat from '../lib/rocketchat'; import RocketChat from '../lib/rocketchat';
import database from '../lib/database'; import database from '../lib/database';
@ -6,18 +7,20 @@ import protectedFunction from '../lib/methods/helpers/protectedFunction';
import { useActionSheet } from './ActionSheet'; import { useActionSheet } from './ActionSheet';
import I18n from '../i18n'; import I18n from '../i18n';
import log from '../utils/log'; import log from '../utils/log';
import { TMessageModel } from '../definitions';
const MessageErrorActions = forwardRef(({ tmid }: any, ref): any => { const MessageErrorActions = forwardRef(({ tmid }: { tmid: string }, ref) => {
// TODO - remove this any after merge ActionSheet evaluate
const { showActionSheet }: any = useActionSheet(); const { showActionSheet }: any = useActionSheet();
const handleResend = protectedFunction(async (message: any) => { const handleResend = protectedFunction(async (message: TMessageModel) => {
await RocketChat.resendMessage(message, tmid); await RocketChat.resendMessage(message, tmid);
}); });
const handleDelete = async (message: any) => { const handleDelete = async (message: TMessageModel) => {
try { try {
const db = database.active; const db = database.active;
const deleteBatch: any = []; const deleteBatch: Model[] = [];
const msgCollection = db.get('messages'); const msgCollection = db.get('messages');
const threadCollection = db.get('threads'); const threadCollection = db.get('threads');
@ -38,7 +41,7 @@ const MessageErrorActions = forwardRef(({ tmid }: any, ref): any => {
const msg = await msgCollection.find(tmid); const msg = await msgCollection.find(tmid);
if (msg?.tcount && msg.tcount <= 1) { if (msg?.tcount && msg.tcount <= 1) {
deleteBatch.push( deleteBatch.push(
msg.prepareUpdate((m: any) => { msg.prepareUpdate(m => {
m.tcount = null; m.tcount = null;
m.tlm = null; m.tlm = null;
}) })
@ -53,8 +56,10 @@ const MessageErrorActions = forwardRef(({ tmid }: any, ref): any => {
} }
} else { } else {
deleteBatch.push( deleteBatch.push(
msg.prepareUpdate((m: any) => { msg.prepareUpdate(m => {
m.tcount -= 1; if (m.tcount) {
m.tcount -= 1;
}
}) })
); );
} }
@ -70,7 +75,7 @@ const MessageErrorActions = forwardRef(({ tmid }: any, ref): any => {
} }
}; };
const showMessageErrorActions = (message: any) => { const showMessageErrorActions = (message: TMessageModel) => {
showActionSheet({ showActionSheet({
options: [ options: [
{ {

View File

@ -5,16 +5,18 @@ import styles from './styles';
import { themes } from '../../../constants/colors'; import { themes } from '../../../constants/colors';
import Touch from '../../../utils/touch'; import Touch from '../../../utils/touch';
import { CustomIcon } from '../../../lib/Icons'; import { CustomIcon } from '../../../lib/Icons';
import { useTheme } from '../../../theme';
interface IPasscodeButton { interface IPasscodeButton {
text?: string; text?: string;
icon?: string; icon?: string;
theme: string;
disabled?: boolean; disabled?: boolean;
onPress?: Function; onPress?: Function;
} }
const Button = React.memo(({ text, disabled, theme, onPress, icon }: IPasscodeButton) => { const Button = React.memo(({ text, disabled, onPress, icon }: IPasscodeButton) => {
const { theme } = useTheme();
const press = () => onPress && onPress(text); const press = () => onPress && onPress(text);
return ( return (

View File

@ -4,47 +4,51 @@ import range from 'lodash/range';
import styles from './styles'; import styles from './styles';
import { themes } from '../../../constants/colors'; import { themes } from '../../../constants/colors';
import { useTheme } from '../../../theme';
const SIZE_EMPTY = 12; const SIZE_EMPTY = 12;
const SIZE_FULL = 16; const SIZE_FULL = 16;
interface IPasscodeDots { interface IPasscodeDots {
passcode: string; passcode: string;
theme: string;
length: number; length: number;
} }
const Dots = React.memo(({ passcode, theme, length }: IPasscodeDots) => ( const Dots = React.memo(({ passcode, length }: IPasscodeDots) => {
<View style={styles.dotsContainer}> const { theme } = useTheme();
{range(length).map(val => {
const lengthSup = passcode.length >= val + 1; return (
const height = lengthSup ? SIZE_FULL : SIZE_EMPTY; <View style={styles.dotsContainer}>
const width = lengthSup ? SIZE_FULL : SIZE_EMPTY; {range(length).map(val => {
let backgroundColor = ''; const lengthSup = passcode.length >= val + 1;
if (lengthSup && passcode.length > 0) { const height = lengthSup ? SIZE_FULL : SIZE_EMPTY;
backgroundColor = themes[theme].passcodeDotFull; const width = lengthSup ? SIZE_FULL : SIZE_EMPTY;
} else { let backgroundColor = '';
backgroundColor = themes[theme].passcodeDotEmpty; if (lengthSup && passcode.length > 0) {
} backgroundColor = themes[theme].passcodeDotFull;
const borderRadius = lengthSup ? SIZE_FULL / 2 : SIZE_EMPTY / 2; } else {
const marginRight = lengthSup ? 10 - (SIZE_FULL - SIZE_EMPTY) / 2 : 10; backgroundColor = themes[theme].passcodeDotEmpty;
const marginLeft = lengthSup ? 10 - (SIZE_FULL - SIZE_EMPTY) / 2 : 10; }
return ( const borderRadius = lengthSup ? SIZE_FULL / 2 : SIZE_EMPTY / 2;
<View style={styles.dotsView}> const marginRight = lengthSup ? 10 - (SIZE_FULL - SIZE_EMPTY) / 2 : 10;
<View const marginLeft = lengthSup ? 10 - (SIZE_FULL - SIZE_EMPTY) / 2 : 10;
style={{ return (
height, <View style={styles.dotsView}>
width, <View
borderRadius, style={{
backgroundColor, height,
marginRight, width,
marginLeft borderRadius,
}} backgroundColor,
/> marginRight,
</View> marginLeft
); }}
})} />
</View> </View>
)); );
})}
</View>
);
});
export default Dots; export default Dots;

View File

@ -5,13 +5,18 @@ import { Row } from 'react-native-easy-grid';
import styles from './styles'; import styles from './styles';
import { themes } from '../../../constants/colors'; import { themes } from '../../../constants/colors';
import { CustomIcon } from '../../../lib/Icons'; import { CustomIcon } from '../../../lib/Icons';
import { useTheme } from '../../../theme';
const LockIcon = React.memo(({ theme }: { theme: string }) => ( const LockIcon = React.memo(() => {
<Row style={styles.row}> const { theme } = useTheme();
<View style={styles.iconView}>
<CustomIcon name='auth' size={40} color={themes[theme].passcodeLockIcon} /> return (
</View> <Row style={styles.row}>
</Row> <View style={styles.iconView}>
)); <CustomIcon name='auth' size={40} color={themes[theme].passcodeLockIcon} />
</View>
</Row>
);
});
export default LockIcon; export default LockIcon;

View File

@ -6,36 +6,35 @@ import { resetAttempts } from '../../../utils/localAuthentication';
import { TYPE } from '../constants'; import { TYPE } from '../constants';
import { getDiff, getLockedUntil } from '../utils'; import { getDiff, getLockedUntil } from '../utils';
import I18n from '../../../i18n'; import I18n from '../../../i18n';
import { useTheme } from '../../../theme';
import styles from './styles'; import styles from './styles';
import Title from './Title'; import Title from './Title';
import Subtitle from './Subtitle'; import Subtitle from './Subtitle';
import LockIcon from './LockIcon'; import LockIcon from './LockIcon';
interface IPasscodeTimer { interface IPasscodeTimer {
time: string; time: Date | null;
theme: string;
setStatus: Function; setStatus: Function;
} }
interface IPasscodeLocked { interface IPasscodeLocked {
theme: string;
setStatus: Function; setStatus: Function;
} }
const Timer = React.memo(({ time, theme, setStatus }: IPasscodeTimer) => { const Timer = React.memo(({ time, setStatus }: IPasscodeTimer) => {
const calcTimeLeft = () => { const calcTimeLeft = () => {
const diff = getDiff(time); const diff = getDiff(time || 0);
if (diff > 0) { if (diff > 0) {
return Math.floor((diff / 1000) % 60); return Math.floor((diff / 1000) % 60);
} }
}; };
const [timeLeft, setTimeLeft] = useState<any>(calcTimeLeft()); const [timeLeft, setTimeLeft] = useState(calcTimeLeft());
useEffect(() => { useEffect(() => {
setTimeout(() => { setTimeout(() => {
setTimeLeft(calcTimeLeft()); setTimeLeft(calcTimeLeft());
if (timeLeft <= 1) { if (timeLeft && timeLeft <= 1) {
resetAttempts(); resetAttempts();
setStatus(TYPE.ENTER); setStatus(TYPE.ENTER);
} }
@ -46,11 +45,12 @@ const Timer = React.memo(({ time, theme, setStatus }: IPasscodeTimer) => {
return null; return null;
} }
return <Subtitle text={I18n.t('Passcode_app_locked_subtitle', { timeLeft })} theme={theme} />; return <Subtitle text={I18n.t('Passcode_app_locked_subtitle', { timeLeft })} />;
}); });
const Locked = React.memo(({ theme, setStatus }: IPasscodeLocked) => { const Locked = React.memo(({ setStatus }: IPasscodeLocked) => {
const [lockedUntil, setLockedUntil] = useState<any>(null); const [lockedUntil, setLockedUntil] = useState<Date | null>(null);
const { theme } = useTheme();
const readItemFromStorage = async () => { const readItemFromStorage = async () => {
const l = await getLockedUntil(); const l = await getLockedUntil();
@ -63,9 +63,9 @@ const Locked = React.memo(({ theme, setStatus }: IPasscodeLocked) => {
return ( return (
<Grid style={[styles.grid, { backgroundColor: themes[theme].passcodeBackground }]}> <Grid style={[styles.grid, { backgroundColor: themes[theme].passcodeBackground }]}>
<LockIcon theme={theme} /> <LockIcon />
<Title text={I18n.t('Passcode_app_locked_title')} theme={theme} /> <Title text={I18n.t('Passcode_app_locked_title')} />
<Timer theme={theme} time={lockedUntil} setStatus={setStatus} /> <Timer time={lockedUntil} setStatus={setStatus} />
</Grid> </Grid>
); );
}); });

View File

@ -4,18 +4,22 @@ import { Row } from 'react-native-easy-grid';
import styles from './styles'; import styles from './styles';
import { themes } from '../../../constants/colors'; import { themes } from '../../../constants/colors';
import { useTheme } from '../../../theme';
interface IPasscodeSubtitle { interface IPasscodeSubtitle {
text: string; text: string;
theme: string;
} }
const Subtitle = React.memo(({ text, theme }: IPasscodeSubtitle) => ( const Subtitle = React.memo(({ text }: IPasscodeSubtitle) => {
<Row style={styles.row}> const { theme } = useTheme();
<View style={styles.subtitleView}>
<Text style={[styles.textSubtitle, { color: themes[theme].passcodeSecondary }]}>{text}</Text> return (
</View> <Row style={styles.row}>
</Row> <View style={styles.subtitleView}>
)); <Text style={[styles.textSubtitle, { color: themes[theme].passcodeSecondary }]}>{text}</Text>
</View>
</Row>
);
});
export default Subtitle; export default Subtitle;

View File

@ -4,18 +4,22 @@ import { Row } from 'react-native-easy-grid';
import styles from './styles'; import styles from './styles';
import { themes } from '../../../constants/colors'; import { themes } from '../../../constants/colors';
import { useTheme } from '../../../theme';
interface IPasscodeTitle { interface IPasscodeTitle {
text: string; text: string;
theme: string;
} }
const Title = React.memo(({ text, theme }: IPasscodeTitle) => ( const Title = React.memo(({ text }: IPasscodeTitle) => {
<Row style={styles.row}> const { theme } = useTheme();
<View style={styles.titleView}>
<Text style={[styles.textTitle, { color: themes[theme].passcodePrimary }]}>{text}</Text> return (
</View> <Row style={styles.row}>
</Row> <View style={styles.titleView}>
)); <Text style={[styles.textTitle, { color: themes[theme].passcodePrimary }]}>{text}</Text>
</View>
</Row>
);
});
export default Title; export default Title;

View File

@ -1,6 +1,7 @@
import React, { forwardRef, useImperativeHandle, useRef, useState } from 'react'; import React, { forwardRef, useImperativeHandle, useRef, useState } from 'react';
import { Col, Grid, Row } from 'react-native-easy-grid'; import { Col, Grid, Row } from 'react-native-easy-grid';
import range from 'lodash/range'; import range from 'lodash/range';
import { View } from 'react-native';
import * as Animatable from 'react-native-animatable'; import * as Animatable from 'react-native-animatable';
import * as Haptics from 'expo-haptics'; import * as Haptics from 'expo-haptics';
@ -10,12 +11,12 @@ import Dots from './Dots';
import { TYPE } from '../constants'; import { TYPE } from '../constants';
import { themes } from '../../../constants/colors'; import { themes } from '../../../constants/colors';
import { PASSCODE_LENGTH } from '../../../constants/localAuthentication'; import { PASSCODE_LENGTH } from '../../../constants/localAuthentication';
import { useTheme } from '../../../theme';
import LockIcon from './LockIcon'; import LockIcon from './LockIcon';
import Title from './Title'; import Title from './Title';
import Subtitle from './Subtitle'; import Subtitle from './Subtitle';
interface IPasscodeBase { interface IPasscodeBase {
theme: string;
type: string; type: string;
previousPasscode?: string; previousPasscode?: string;
title: string; title: string;
@ -26,25 +27,30 @@ interface IPasscodeBase {
onBiometryPress?(): void; onBiometryPress?(): void;
} }
const Base = forwardRef( export interface IBase {
( clearPasscode: () => void;
{ theme, type, onEndProcess, previousPasscode, title, subtitle, onError, showBiometry, onBiometryPress }: IPasscodeBase, wrongPasscode: () => void;
ref animate: (animation: Animatable.Animation, duration?: number) => void;
) => { }
const rootRef = useRef<any>();
const dotsRef = useRef<any>(); const Base = forwardRef<IBase, IPasscodeBase>(
({ type, onEndProcess, previousPasscode, title, subtitle, onError, showBiometry, onBiometryPress }, ref) => {
const { theme } = useTheme();
const rootRef = useRef<Animatable.View & View>(null);
const dotsRef = useRef<Animatable.View & View>(null);
const [passcode, setPasscode] = useState(''); const [passcode, setPasscode] = useState('');
const clearPasscode = () => setPasscode(''); const clearPasscode = () => setPasscode('');
const wrongPasscode = () => { const wrongPasscode = () => {
clearPasscode(); clearPasscode();
dotsRef?.current?.shake(500); dotsRef?.current?.shake?.(500);
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error); Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
}; };
const animate = (animation: string, duration = 500) => { const animate = (animation: Animatable.Animation, duration = 500) => {
rootRef?.current?.[animation](duration); rootRef?.current?.[animation]?.(duration);
}; };
const onPressNumber = (text: string) => const onPressNumber = (text: string) =>
@ -90,48 +96,48 @@ const Base = forwardRef(
return ( return (
<Animatable.View ref={rootRef} style={styles.container}> <Animatable.View ref={rootRef} style={styles.container}>
<Grid style={[styles.grid, { backgroundColor: themes[theme].passcodeBackground }]}> <Grid style={[styles.grid, { backgroundColor: themes[theme].passcodeBackground }]}>
<LockIcon theme={theme} /> <LockIcon />
<Title text={title} theme={theme} /> <Title text={title} />
<Subtitle text={subtitle!} theme={theme} /> {subtitle ? <Subtitle text={subtitle} /> : null}
<Row style={styles.row}> <Row style={styles.row}>
<Animatable.View ref={dotsRef}> <Animatable.View ref={dotsRef}>
<Dots passcode={passcode} theme={theme} length={PASSCODE_LENGTH} /> <Dots passcode={passcode} length={PASSCODE_LENGTH} />
</Animatable.View> </Animatable.View>
</Row> </Row>
<Row style={[styles.row, styles.buttonRow]}> <Row style={[styles.row, styles.buttonRow]}>
{range(1, 4).map((i: any) => ( {range(1, 4).map(i => (
<Col key={i} style={styles.colButton}> <Col key={i} style={styles.colButton}>
<Button text={i} theme={theme} onPress={onPressNumber} /> <Button text={i.toString()} onPress={onPressNumber} />
</Col> </Col>
))} ))}
</Row> </Row>
<Row style={[styles.row, styles.buttonRow]}> <Row style={[styles.row, styles.buttonRow]}>
{range(4, 7).map((i: any) => ( {range(4, 7).map(i => (
<Col key={i} style={styles.colButton}> <Col key={i} style={styles.colButton}>
<Button text={i} theme={theme} onPress={onPressNumber} /> <Button text={i.toString()} onPress={onPressNumber} />
</Col> </Col>
))} ))}
</Row> </Row>
<Row style={[styles.row, styles.buttonRow]}> <Row style={[styles.row, styles.buttonRow]}>
{range(7, 10).map((i: any) => ( {range(7, 10).map(i => (
<Col key={i} style={styles.colButton}> <Col key={i} style={styles.colButton}>
<Button text={i} theme={theme} onPress={onPressNumber} /> <Button text={i.toString()} onPress={onPressNumber} />
</Col> </Col>
))} ))}
</Row> </Row>
<Row style={[styles.row, styles.buttonRow]}> <Row style={[styles.row, styles.buttonRow]}>
{showBiometry ? ( {showBiometry ? (
<Col style={styles.colButton}> <Col style={styles.colButton}>
<Button icon='fingerprint' theme={theme} onPress={onBiometryPress} /> <Button icon='fingerprint' onPress={onBiometryPress} />
</Col> </Col>
) : ( ) : (
<Col style={styles.colButton} /> <Col style={styles.colButton} />
)} )}
<Col style={styles.colButton}> <Col style={styles.colButton}>
<Button text='0' theme={theme} onPress={onPressNumber} /> <Button text='0' onPress={onPressNumber} />
</Col> </Col>
<Col style={styles.colButton}> <Col style={styles.colButton}>
<Button icon='backspace' theme={theme} onPress={onPressDelete} /> <Button icon='backspace' onPress={onPressDelete} />
</Col> </Col>
</Row> </Row>
</Grid> </Grid>

View File

@ -2,24 +2,23 @@ import React, { useRef, useState } from 'react';
import * as Haptics from 'expo-haptics'; import * as Haptics from 'expo-haptics';
import { gestureHandlerRootHOC } from 'react-native-gesture-handler'; import { gestureHandlerRootHOC } from 'react-native-gesture-handler';
import Base from './Base'; import Base, { IBase } from './Base';
import { TYPE } from './constants'; import { TYPE } from './constants';
import I18n from '../../i18n'; import I18n from '../../i18n';
interface IPasscodeChoose { interface IPasscodeChoose {
theme: string;
force?: boolean; force?: boolean;
finishProcess: Function; finishProcess: Function;
} }
const PasscodeChoose = ({ theme, finishProcess, force = false }: IPasscodeChoose) => { const PasscodeChoose = ({ finishProcess, force = false }: IPasscodeChoose) => {
const chooseRef = useRef<any>(null); const chooseRef = useRef<IBase>(null);
const confirmRef = useRef<any>(null); const confirmRef = useRef<IBase>(null);
const [subtitle, setSubtitle] = useState(null); const [subtitle, setSubtitle] = useState(null);
const [status, setStatus] = useState(TYPE.CHOOSE); const [status, setStatus] = useState(TYPE.CHOOSE);
const [previousPasscode, setPreviouPasscode] = useState<any>(null); const [previousPasscode, setPreviouPasscode] = useState('');
const firstStep = (p: any) => { const firstStep = (p: string) => {
setTimeout(() => { setTimeout(() => {
setStatus(TYPE.CONFIRM); setStatus(TYPE.CONFIRM);
setPreviouPasscode(p); setPreviouPasscode(p);
@ -43,7 +42,6 @@ const PasscodeChoose = ({ theme, finishProcess, force = false }: IPasscodeChoose
return ( return (
<Base <Base
ref={confirmRef} ref={confirmRef}
theme={theme}
type={TYPE.CONFIRM} type={TYPE.CONFIRM}
onEndProcess={changePasscode} onEndProcess={changePasscode}
previousPasscode={previousPasscode} previousPasscode={previousPasscode}
@ -56,7 +54,6 @@ const PasscodeChoose = ({ theme, finishProcess, force = false }: IPasscodeChoose
return ( return (
<Base <Base
ref={chooseRef} ref={chooseRef}
theme={theme}
type={TYPE.CHOOSE} type={TYPE.CHOOSE}
onEndProcess={firstStep} onEndProcess={firstStep}
title={I18n.t('Passcode_choose_title')} title={I18n.t('Passcode_choose_title')}

View File

@ -4,7 +4,7 @@ import { gestureHandlerRootHOC } from 'react-native-gesture-handler';
import * as Haptics from 'expo-haptics'; import * as Haptics from 'expo-haptics';
import { sha256 } from 'js-sha256'; import { sha256 } from 'js-sha256';
import Base from './Base'; import Base, { IBase } from './Base';
import Locked from './Base/Locked'; import Locked from './Base/Locked';
import { TYPE } from './constants'; import { TYPE } from './constants';
import { ATTEMPTS_KEY, LOCKED_OUT_TIMER_KEY, MAX_ATTEMPTS, PASSCODE_KEY } from '../../constants/localAuthentication'; import { ATTEMPTS_KEY, LOCKED_OUT_TIMER_KEY, MAX_ATTEMPTS, PASSCODE_KEY } from '../../constants/localAuthentication';
@ -14,18 +14,17 @@ import { useUserPreferences } from '../../lib/userPreferences';
import I18n from '../../i18n'; import I18n from '../../i18n';
interface IPasscodePasscodeEnter { interface IPasscodePasscodeEnter {
theme: string;
hasBiometry: boolean; hasBiometry: boolean;
finishProcess: Function; finishProcess: Function;
} }
const PasscodeEnter = ({ theme, hasBiometry, finishProcess }: IPasscodePasscodeEnter) => { const PasscodeEnter = ({ hasBiometry, finishProcess }: IPasscodePasscodeEnter) => {
const ref = useRef(null); const ref = useRef<IBase>(null);
let attempts: any = 0; let attempts = 0;
let lockedUntil: any = false; let lockedUntil: any = false;
const [passcode] = useUserPreferences(PASSCODE_KEY); const [passcode] = useUserPreferences(PASSCODE_KEY);
const [status, setStatus] = useState(null); const [status, setStatus] = useState<TYPE | null>(null);
const { getItem: getAttempts, setItem: setAttempts } = useAsyncStorage(ATTEMPTS_KEY); const { setItem: setAttempts } = useAsyncStorage(ATTEMPTS_KEY);
const { setItem: setLockedUntil } = useAsyncStorage(LOCKED_OUT_TIMER_KEY); const { setItem: setLockedUntil } = useAsyncStorage(LOCKED_OUT_TIMER_KEY);
const biometry = async () => { const biometry = async () => {
@ -45,7 +44,6 @@ const PasscodeEnter = ({ theme, hasBiometry, finishProcess }: IPasscodePasscodeE
await resetAttempts(); await resetAttempts();
setStatus(TYPE.ENTER); setStatus(TYPE.ENTER);
} else { } else {
attempts = await getAttempts();
setStatus(TYPE.LOCKED); setStatus(TYPE.LOCKED);
} }
} else { } else {
@ -58,7 +56,7 @@ const PasscodeEnter = ({ theme, hasBiometry, finishProcess }: IPasscodePasscodeE
readStorage(); readStorage();
}, [status]); }, [status]);
const onEndProcess = (p: any) => { const onEndProcess = (p: string) => {
setTimeout(() => { setTimeout(() => {
if (sha256(p) === passcode) { if (sha256(p) === passcode) {
finishProcess(); finishProcess();
@ -69,8 +67,7 @@ const PasscodeEnter = ({ theme, hasBiometry, finishProcess }: IPasscodePasscodeE
setLockedUntil(new Date().toISOString()); setLockedUntil(new Date().toISOString());
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error); Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
} else { } else {
// @ts-ignore ref?.current?.wrongPasscode();
ref.current.wrongPasscode();
setAttempts(attempts?.toString()); setAttempts(attempts?.toString());
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Warning); Haptics.notificationAsync(Haptics.NotificationFeedbackType.Warning);
} }
@ -79,13 +76,12 @@ const PasscodeEnter = ({ theme, hasBiometry, finishProcess }: IPasscodePasscodeE
}; };
if (status === TYPE.LOCKED) { if (status === TYPE.LOCKED) {
return <Locked theme={theme} setStatus={setStatus} />; return <Locked setStatus={setStatus} />;
} }
return ( return (
<Base <Base
ref={ref} ref={ref}
theme={theme}
type={TYPE.ENTER} type={TYPE.ENTER}
title={I18n.t('Passcode_enter_title')} title={I18n.t('Passcode_enter_title')}
showBiometry={hasBiometry} showBiometry={hasBiometry}

View File

@ -1,6 +1,6 @@
export const TYPE: any = { export enum TYPE {
CHOOSE: 'choose', CHOOSE = 'choose',
CONFIRM: 'confirm', CONFIRM = 'confirm',
ENTER: 'enter', ENTER = 'enter',
LOCKED: 'locked' LOCKED = 'locked'
}; }

View File

@ -4,11 +4,11 @@ import moment from 'moment';
import { LOCKED_OUT_TIMER_KEY, TIME_TO_LOCK } from '../../constants/localAuthentication'; import { LOCKED_OUT_TIMER_KEY, TIME_TO_LOCK } from '../../constants/localAuthentication';
export const getLockedUntil = async () => { export const getLockedUntil = async () => {
const t: any = await AsyncStorage.getItem(LOCKED_OUT_TIMER_KEY); const t = await AsyncStorage.getItem(LOCKED_OUT_TIMER_KEY);
if (t) { if (t) {
return moment(t).add(TIME_TO_LOCK); return moment(t).add(TIME_TO_LOCK).toDate();
} }
return null; return null;
}; };
// @ts-ignore
export const getDiff = t => new Date(t) - new Date(); export const getDiff = (t: string | number | Date) => new Date(t).getTime() - new Date().getTime();

View File

@ -52,10 +52,7 @@ const styles = StyleSheet.create({
export interface IRCTextInputProps extends TextInputProps { export interface IRCTextInputProps extends TextInputProps {
label?: string; label?: string;
error?: { error?: any;
error: any;
reason: any;
};
loading?: boolean; loading?: boolean;
containerStyle?: StyleProp<ViewStyle>; containerStyle?: StyleProp<ViewStyle>;
inputStyle?: StyleProp<TextStyle>; inputStyle?: StyleProp<TextStyle>;
@ -68,7 +65,11 @@ export interface IRCTextInputProps extends TextInputProps {
theme: string; theme: string;
} }
export default class RCTextInput extends React.PureComponent<IRCTextInputProps, any> { interface IRCTextInputState {
showPassword: boolean;
}
export default class RCTextInput extends React.PureComponent<IRCTextInputProps, IRCTextInputState> {
static defaultProps = { static defaultProps = {
error: {}, error: {},
theme: 'light' theme: 'light'
@ -120,7 +121,7 @@ export default class RCTextInput extends React.PureComponent<IRCTextInputProps,
} }
tooglePassword = () => { tooglePassword = () => {
this.setState((prevState: any) => ({ showPassword: !prevState.showPassword })); this.setState(prevState => ({ showPassword: !prevState.showPassword }));
}; };
render() { render() {

View File

@ -41,7 +41,7 @@ const styles = StyleSheet.create({
}); });
interface IThreadDetails { interface IThreadDetails {
item: Partial<TThreadModel>; item: Pick<TThreadModel, 'tcount' | 'replies' | 'id'>;
user: { user: {
id: string; id: string;
}; };
@ -52,7 +52,7 @@ interface IThreadDetails {
const ThreadDetails = ({ item, user, badgeColor, toggleFollowThread, style }: IThreadDetails): JSX.Element => { const ThreadDetails = ({ item, user, badgeColor, toggleFollowThread, style }: IThreadDetails): JSX.Element => {
const { theme } = useTheme(); const { theme } = useTheme();
let count: string | number | undefined = item.tcount; let count: string | number | undefined | null = item.tcount;
if (count && count >= 1000) { if (count && count >= 1000) {
count = '+999'; count = '+999';
} }
@ -62,21 +62,21 @@ const ThreadDetails = ({ item, user, badgeColor, toggleFollowThread, style }: IT
replies = '+999'; replies = '+999';
} }
const isFollowing = item.replies?.find((u: any) => u === user?.id); const isFollowing = item.replies?.find((u: string) => u === user?.id);
return ( return (
<View style={[styles.container, style]}> <View style={[styles.container, style]}>
<View style={styles.detailsContainer}> <View style={styles.detailsContainer}>
<View style={styles.detailContainer}> <View style={styles.detailContainer}>
<CustomIcon name='threads' size={24} color={themes[theme!].auxiliaryText} /> <CustomIcon name='threads' size={24} color={themes[theme].auxiliaryText} />
<Text style={[styles.detailText, { color: themes[theme!].auxiliaryText }]} numberOfLines={1}> <Text style={[styles.detailText, { color: themes[theme].auxiliaryText }]} numberOfLines={1}>
{count} {count}
</Text> </Text>
</View> </View>
<View style={styles.detailContainer}> <View style={styles.detailContainer}>
<CustomIcon name='user' size={24} color={themes[theme!].auxiliaryText} /> <CustomIcon name='user' size={24} color={themes[theme].auxiliaryText} />
<Text style={[styles.detailText, { color: themes[theme!].auxiliaryText }]} numberOfLines={1}> <Text style={[styles.detailText, { color: themes[theme].auxiliaryText }]} numberOfLines={1}>
{replies} {replies}
</Text> </Text>
</View> </View>
@ -87,7 +87,7 @@ const ThreadDetails = ({ item, user, badgeColor, toggleFollowThread, style }: IT
<CustomIcon <CustomIcon
size={24} size={24}
name={isFollowing ? 'notification' : 'notification-disabled'} name={isFollowing ? 'notification' : 'notification-disabled'}
color={themes[theme!].auxiliaryTintColor} color={themes[theme].auxiliaryTintColor}
/> />
</Touchable> </Touchable>
</View> </View>

View File

@ -9,21 +9,36 @@ import { connect } from 'react-redux';
import TextInput from '../TextInput'; import TextInput from '../TextInput';
import I18n from '../../i18n'; import I18n from '../../i18n';
import EventEmitter from '../../utils/events'; import EventEmitter from '../../utils/events';
import { withTheme } from '../../theme'; import { useTheme } from '../../theme';
import { themes } from '../../constants/colors'; import { themes } from '../../constants/colors';
import Button from '../Button'; import Button from '../Button';
import sharedStyles from '../../views/Styles'; import sharedStyles from '../../views/Styles';
import RocketChat from '../../lib/rocketchat'; import RocketChat from '../../lib/rocketchat';
import styles from './styles'; import styles from './styles';
import { IApplicationState } from '../../definitions';
export const TWO_FACTOR = 'TWO_FACTOR'; export const TWO_FACTOR = 'TWO_FACTOR';
interface ITwoFactor { interface IMethodsProp {
theme?: string; text: string;
isMasterDetail: boolean; keyboardType: 'numeric' | 'default';
title?: string;
secureTextEntry?: boolean;
}
interface IMethods {
totp: IMethodsProp;
email: IMethodsProp;
password: IMethodsProp;
} }
const methods: any = { interface EventListenerMethod {
method?: keyof IMethods;
submit?: (param: string) => void;
cancel?: () => void;
invalid?: boolean;
}
const methods: IMethods = {
totp: { totp: {
text: 'Open_your_authentication_app_and_enter_the_code', text: 'Open_your_authentication_app_and_enter_the_code',
keyboardType: 'numeric' keyboardType: 'numeric'
@ -40,14 +55,14 @@ const methods: any = {
} }
}; };
const TwoFactor = React.memo(({ theme, isMasterDetail }: ITwoFactor) => { const TwoFactor = React.memo(({ isMasterDetail }: { isMasterDetail: boolean }) => {
const { theme } = useTheme();
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const [data, setData] = useState<any>({}); const [data, setData] = useState<EventListenerMethod>({});
const [code, setCode] = useState<any>(''); const [code, setCode] = useState<string>('');
const method = methods[data.method]; const method = data.method ? methods[data.method] : null;
const isEmail = data.method === 'email'; const isEmail = data.method === 'email';
const sendEmail = () => RocketChat.sendEmailCode(); const sendEmail = () => RocketChat.sendEmailCode();
useDeepCompareEffect(() => { useDeepCompareEffect(() => {
@ -59,7 +74,7 @@ const TwoFactor = React.memo(({ theme, isMasterDetail }: ITwoFactor) => {
} }
}, [data]); }, [data]);
const showTwoFactor = (args: any) => setData(args); const showTwoFactor = (args: EventListenerMethod) => setData(args);
useEffect(() => { useEffect(() => {
const listener = EventEmitter.addEventListener(TWO_FACTOR, showTwoFactor); const listener = EventEmitter.addEventListener(TWO_FACTOR, showTwoFactor);
@ -87,26 +102,19 @@ const TwoFactor = React.memo(({ theme, isMasterDetail }: ITwoFactor) => {
setData({}); setData({});
}; };
const color = themes[theme!].titleText; const color = themes[theme].titleText;
return ( return (
<Modal <Modal avoidKeyboard useNativeDriver isVisible={visible} hideModalContentWhileAnimating>
// @ts-ignore
transparent
avoidKeyboard
useNativeDriver
isVisible={visible}
hideModalContentWhileAnimating>
<View style={styles.container} testID='two-factor'> <View style={styles.container} testID='two-factor'>
<View <View
style={[ style={[
styles.content, styles.content,
isMasterDetail && [sharedStyles.modalFormSheet, styles.tablet], isMasterDetail && [sharedStyles.modalFormSheet, styles.tablet],
{ backgroundColor: themes[theme!].backgroundColor } { backgroundColor: themes[theme].backgroundColor }
]}> ]}>
<Text style={[styles.title, { color }]}>{I18n.t(method?.title || 'Two_Factor_Authentication')}</Text> <Text style={[styles.title, { color }]}>{I18n.t(method?.title || 'Two_Factor_Authentication')}</Text>
{method?.text ? <Text style={[styles.subtitle, { color }]}>{I18n.t(method.text)}</Text> : null} {method?.text ? <Text style={[styles.subtitle, { color }]}>{I18n.t(method.text)}</Text> : null}
<TextInput <TextInput
/* @ts-ignore*/
value={code} value={code}
theme={theme} theme={theme}
inputRef={(e: any) => InteractionManager.runAfterInteractions(() => e?.getNativeRef()?.focus())} inputRef={(e: any) => InteractionManager.runAfterInteractions(() => e?.getNativeRef()?.focus())}
@ -116,19 +124,19 @@ const TwoFactor = React.memo(({ theme, isMasterDetail }: ITwoFactor) => {
onSubmitEditing={onSubmit} onSubmitEditing={onSubmit}
keyboardType={method?.keyboardType} keyboardType={method?.keyboardType}
secureTextEntry={method?.secureTextEntry} secureTextEntry={method?.secureTextEntry}
error={data.invalid && { error: 'totp-invalid', reason: I18n.t('Code_or_password_invalid') }} error={data.invalid ? { error: 'totp-invalid', reason: I18n.t('Code_or_password_invalid') } : undefined}
testID='two-factor-input' testID='two-factor-input'
/> />
{isEmail && ( {isEmail ? (
<Text style={[styles.sendEmail, { color }]} onPress={sendEmail}> <Text style={[styles.sendEmail, { color }]} onPress={sendEmail}>
{I18n.t('Send_me_the_code_again')} {I18n.t('Send_me_the_code_again')}
</Text> </Text>
)} ) : null}
<View style={styles.buttonContainer}> <View style={styles.buttonContainer}>
<Button <Button
title={I18n.t('Cancel')} title={I18n.t('Cancel')}
type='secondary' type='secondary'
backgroundColor={themes[theme!].chatComponentBackground} backgroundColor={themes[theme].chatComponentBackground}
style={styles.button} style={styles.button}
onPress={onCancel} onPress={onCancel}
theme={theme} theme={theme}
@ -148,8 +156,8 @@ const TwoFactor = React.memo(({ theme, isMasterDetail }: ITwoFactor) => {
); );
}); });
const mapStateToProps = (state: any) => ({ const mapStateToProps = (state: IApplicationState) => ({
isMasterDetail: state.app.isMasterDetail isMasterDetail: state.app.isMasterDetail
}); });
export default connect(mapStateToProps)(withTheme(TwoFactor)); export default connect(mapStateToProps)(TwoFactor);

View File

@ -41,7 +41,7 @@ const Item = ({ item, selected, onSelect, theme }: IItem) => {
<> <>
{item.imageUrl ? <FastImage style={styles.itemImage} source={{ uri: item.imageUrl }} /> : null} {item.imageUrl ? <FastImage style={styles.itemImage} source={{ uri: item.imageUrl }} /> : null}
<Text style={{ color: themes[theme].titleText }}>{textParser([item.text])}</Text> <Text style={{ color: themes[theme].titleText }}>{textParser([item.text])}</Text>
{selected ? <Check theme={theme} /> : null} {selected ? <Check /> : null}
</> </>
</Touchable> </Touchable>
); );

View File

@ -126,7 +126,6 @@ export const MultiSelect = React.memo(
<View style={[styles.content, { backgroundColor: themes[theme].backgroundColor }]}> <View style={[styles.content, { backgroundColor: themes[theme].backgroundColor }]}>
<TextInput <TextInput
testID='multi-select-search' testID='multi-select-search'
/* @ts-ignore*/
onChangeText={onSearch || onSearchChange} onChangeText={onSearch || onSearchChange}
placeholder={I18n.t('Search')} placeholder={I18n.t('Search')}
theme={theme} theme={theme}

View File

@ -187,16 +187,14 @@ class ModalParser extends UiKitParserModal {
return ( return (
// @ts-ignore // @ts-ignore
<TextInput <TextInput
id={actionId} key={actionId}
placeholder={plainText(placeholder)} placeholder={plainText(placeholder)}
onInput={action}
multiline={multiline} multiline={multiline}
loading={loading} loading={loading}
onChangeText={(text: any) => action({ value: text })} onChangeText={text => action({ value: text })}
inputStyle={multiline && styles.multiline} inputStyle={multiline && styles.multiline}
containerStyle={styles.input} containerStyle={styles.input}
value={value} value={value}
// @ts-ignore
error={{ error }} error={{ error }}
theme={theme} theme={theme}
/> />

View File

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { Easing, StyleProp, StyleSheet, Text, TextStyle, View } from 'react-native'; import { StyleProp, StyleSheet, Text, TextStyle, View } from 'react-native';
import { Audio } from 'expo-av'; import { Audio } from 'expo-av';
import Slider from '@react-native-community/slider'; import Slider from '@react-native-community/slider';
import moment from 'moment'; import moment from 'moment';
@ -84,12 +84,6 @@ const formatTime = (seconds: number) => moment.utc(seconds * 1000).format('mm:ss
const BUTTON_HIT_SLOP = { top: 12, right: 12, bottom: 12, left: 12 }; const BUTTON_HIT_SLOP = { top: 12, right: 12, bottom: 12, left: 12 };
const sliderAnimationConfig = {
duration: 250,
easing: Easing.linear,
delay: 0
};
const Button = React.memo(({ loading, paused, onPress, disabled, theme }: IButton) => ( const Button = React.memo(({ loading, paused, onPress, disabled, theme }: IButton) => (
<Touchable <Touchable
style={styles.playPauseButton} style={styles.playPauseButton}
@ -285,10 +279,7 @@ class MessageAudio extends React.Component<IMessageAudioProps, IMessageAudioStat
value={currentTime} value={currentTime}
maximumValue={duration} maximumValue={duration}
minimumValue={0} minimumValue={0}
// @ts-ignore thumbTintColor={isReply && isAndroid ? themes[theme].tintDisabled : isAndroid && themes[theme].tintColor}
animateTransitions
animationConfig={sliderAnimationConfig}
thumbTintColor={isReply ? themes[theme].tintDisabled : isAndroid && themes[theme].tintColor}
minimumTrackTintColor={themes[theme].tintColor} minimumTrackTintColor={themes[theme].tintColor}
maximumTrackTintColor={themes[theme].auxiliaryText} maximumTrackTintColor={themes[theme].auxiliaryText}
onValueChange={this.onValueChange} onValueChange={this.onValueChange}

View File

@ -39,9 +39,7 @@ const Content = React.memo(
const isPreview: any = props.tmid && !props.isThreadRoom; const isPreview: any = props.tmid && !props.isThreadRoom;
let content = null; let content = null;
if (props.tmid && !props.msg) { if (props.isEncrypted) {
content = <Text style={[styles.text, { color: themes[props.theme].bodyText }]}>{I18n.t('Sent_an_attachment')}</Text>;
} else if (props.isEncrypted) {
content = ( content = (
<Text <Text
style={[styles.textInfo, { color: themes[props.theme].auxiliaryText }]} style={[styles.textInfo, { color: themes[props.theme].auxiliaryText }]}

View File

@ -98,7 +98,7 @@ export interface IMessageEmoji {
export interface IMessageThread { export interface IMessageThread {
msg?: string; msg?: string;
tcount?: number; tcount?: number | null;
theme: string; theme: string;
tlm?: Date; tlm?: Date;
isThreadRoom: boolean; isThreadRoom: boolean;

View File

@ -1,3 +1,5 @@
import { AppleAuthenticationFullName } from 'expo-apple-authentication';
export interface ICredentials { export interface ICredentials {
resume?: string; resume?: string;
user?: string; user?: string;
@ -13,4 +15,7 @@ export interface ICredentials {
login: ICredentials; login: ICredentials;
code: string; code: string;
}; };
fullName?: AppleAuthenticationFullName | null;
email?: string | null;
identityToken?: string | null;
} }

View File

@ -130,8 +130,8 @@ export interface IMessage extends IMessageFromServer {
dcount?: number; dcount?: number;
dlm?: string | Date; dlm?: string | Date;
tmid?: string; tmid?: string;
tcount?: number; tcount?: number | null;
tlm?: string | Date; tlm?: string | Date | null;
replies?: string[]; replies?: string[];
unread?: boolean; unread?: boolean;
autoTranslate?: boolean; autoTranslate?: boolean;

View File

@ -6,10 +6,10 @@ import { TNavigationOptions } from './definitions/navigationTypes';
export interface IDimensionsContextProps { export interface IDimensionsContextProps {
width: number; width: number;
height?: number; height: number;
scale: number; scale?: number;
fontScale: number; fontScale?: number;
setDimensions: ({ setDimensions?: ({
width, width,
height, height,
scale, scale,
@ -22,7 +22,7 @@ export interface IDimensionsContextProps {
}) => void; }) => void;
} }
export const DimensionsContext = React.createContext<Partial<IDimensionsContextProps>>(Dimensions.get('window')); export const DimensionsContext = React.createContext<IDimensionsContextProps>(Dimensions.get('window'));
export function withDimensions<T extends object>(Component: React.ComponentType<T> & TNavigationOptions): typeof Component { export function withDimensions<T extends object>(Component: React.ComponentType<T> & TNavigationOptions): typeof Component {
const DimensionsComponent = (props: T) => ( const DimensionsComponent = (props: T) => (
@ -37,7 +37,7 @@ export const useDimensions = () => React.useContext(DimensionsContext);
export const useOrientation = () => { export const useOrientation = () => {
const { width, height } = React.useContext(DimensionsContext); const { width, height } = React.useContext(DimensionsContext);
const isPortrait = height! > width!; const isPortrait = height > width;
return { return {
isPortrait, isPortrait,
isLandscape: !isPortrait isLandscape: !isPortrait

View File

@ -59,7 +59,7 @@ const ServerItem = React.memo(({ item, onPress, onLongPress, hasCheck, theme }:
{item.id} {item.id}
</Text> </Text>
</View> </View>
{hasCheck ? <Check theme={theme!} /> : null} {hasCheck ? <Check /> : null}
</View> </View>
</Pressable> </Pressable>
)); ));

View File

@ -10,7 +10,7 @@ const styles = StyleSheet.create({
} }
}); });
interface IThemedTextInput extends IRCTextInputProps { export interface IThemedTextInput extends IRCTextInputProps {
style: StyleProp<TextStyle>; style: StyleProp<TextStyle>;
theme: string; theme: string;
} }

View File

@ -3,7 +3,7 @@ import isEmpty from 'lodash/isEmpty';
import { IApplicationState, IUser } from '../definitions'; import { IApplicationState, IUser } from '../definitions';
interface IServices { export interface IServices {
facebook: { clientId: string }; facebook: { clientId: string };
github: { clientId: string }; github: { clientId: string };
gitlab: { clientId: string }; gitlab: { clientId: string };

View File

@ -272,8 +272,14 @@ export type OutsideParamList = {
}; };
RegisterView: { RegisterView: {
title: string; title: string;
username?: string;
}; };
LegalView: undefined; LegalView: undefined;
AuthenticationWebView: {
authType: string;
url: string;
ssoToken?: string;
};
}; };
export type OutsideModalParamList = { export type OutsideModalParamList = {

View File

@ -80,7 +80,7 @@ const ChangePasscodeView = React.memo(() => {
return ( return (
<Modal useNativeDriver isVisible={visible} hideModalContentWhileAnimating style={styles.modal}> <Modal useNativeDriver isVisible={visible} hideModalContentWhileAnimating style={styles.modal}>
<PasscodeChoose theme={theme} finishProcess={onSubmit} force={data?.force} /> <PasscodeChoose finishProcess={onSubmit} force={data?.force} />
{!data?.force ? ( {!data?.force ? (
<Touchable onPress={onCancel} style={styles.close}> <Touchable onPress={onCancel} style={styles.close}>
<CustomIcon name='close' color={themes[theme].passcodePrimary} size={30} /> <CustomIcon name='close' color={themes[theme].passcodePrimary} size={30} />

View File

@ -175,7 +175,6 @@ class CreateChannelView extends React.Component<ICreateChannelViewProps, any> {
testID='multi-select-discussion-name' testID='multi-select-discussion-name'
placeholder={I18n.t('A_meaningful_name_for_the_discussion_room')} placeholder={I18n.t('A_meaningful_name_for_the_discussion_room')}
containerStyle={styles.inputStyle} containerStyle={styles.inputStyle}
/* @ts-ignore*/
defaultValue={name} defaultValue={name}
onChangeText={(text: string) => this.setState({ name: text })} onChangeText={(text: string) => this.setState({ name: text })}
theme={theme} theme={theme}

View File

@ -71,7 +71,7 @@ export default class DirectoryOptions extends PureComponent<IDirectoryOptionsPro
<View style={styles.dropdownItemContainer}> <View style={styles.dropdownItemContainer}>
<CustomIcon style={[styles.dropdownItemIcon, { color: themes[theme].bodyText }]} size={22} name={icon} /> <CustomIcon style={[styles.dropdownItemIcon, { color: themes[theme].bodyText }]} size={22} name={icon} />
<Text style={[styles.dropdownItemText, { color: themes[theme].bodyText }]}>{I18n.t(text)}</Text> <Text style={[styles.dropdownItemText, { color: themes[theme].bodyText }]}>{I18n.t(text)}</Text>
{propType === itemType ? <Check theme={theme} /> : null} {propType === itemType ? <Check /> : null}
</View> </View>
</Touch> </Touch>
); );

View File

@ -231,7 +231,7 @@ class LoginView extends React.Component<ILoginViewProps, any> {
return ( return (
<FormContainer theme={theme} testID='login-view'> <FormContainer theme={theme} testID='login-view'>
<FormContainerInner> <FormContainerInner>
<LoginServices separator={Accounts_ShowFormLogin} navigation={navigation} /> <LoginServices separator={Accounts_ShowFormLogin} navigation={navigation} theme={theme} />
{this.renderUserForm()} {this.renderUserForm()}
</FormContainerInner> </FormContainerInner>
</FormContainer> </FormContainer>

View File

@ -186,7 +186,7 @@ class RegisterView extends React.Component<IProps, any> {
}} }}
value={customFields[key]}> value={customFields[key]}>
<TextInput <TextInput
inputRef={(e: any) => { inputRef={e => {
// @ts-ignore // @ts-ignore
this[key] = e; this[key] = e;
}} }}
@ -237,7 +237,7 @@ class RegisterView extends React.Component<IProps, any> {
return ( return (
<FormContainer theme={theme} testID='register-view'> <FormContainer theme={theme} testID='register-view'>
<FormContainerInner> <FormContainerInner>
<LoginServices navigation={navigation} /> <LoginServices navigation={navigation} theme={theme} separator />
<Text style={[styles.title, sharedStyles.textBold, { color: themes[theme].titleText }]}>{I18n.t('Sign_Up')}</Text> <Text style={[styles.title, sharedStyles.textBold, { color: themes[theme].titleText }]}>{I18n.t('Sign_Up')}</Text>
<TextInput <TextInput
label={I18n.t('Name')} label={I18n.t('Name')}

View File

@ -666,4 +666,4 @@ const mapStateToProps = (state: IApplicationState) => ({
viewAllTeamsPermission: state.permissions['view-all-teams'] viewAllTeamsPermission: state.permissions['view-all-teams']
}); });
export default connect(mapStateToProps)(withActionSheet(withTheme(RoomMembersView))); export default connect(mapStateToProps)(withTheme(withActionSheet(RoomMembersView)));

View File

@ -72,8 +72,7 @@ const JoinCode = React.memo(
useImperativeHandle(ref, () => ({ show })); useImperativeHandle(ref, () => ({ show }));
return ( return (
// @ts-ignore TODO: `transparent` seems to exist, but types are incorrect on the lib <Modal avoidKeyboard useNativeDriver isVisible={visible} hideModalContentWhileAnimating>
<Modal transparent={true} avoidKeyboard useNativeDriver isVisible={visible} hideModalContentWhileAnimating>
<View style={styles.container} testID='join-code'> <View style={styles.container} testID='join-code'>
<View <View
style={[ style={[

View File

@ -4,7 +4,6 @@ import useDeepCompareEffect from 'use-deep-compare-effect';
import isEmpty from 'lodash/isEmpty'; import isEmpty from 'lodash/isEmpty';
import Orientation from 'react-native-orientation-locker'; import Orientation from 'react-native-orientation-locker';
import { useTheme } from '../theme';
import EventEmitter from '../utils/events'; import EventEmitter from '../utils/events';
import { LOCAL_AUTHENTICATE_EMITTER } from '../constants/localAuthentication'; import { LOCAL_AUTHENTICATE_EMITTER } from '../constants/localAuthentication';
import { isTablet } from '../utils/deviceInfo'; import { isTablet } from '../utils/deviceInfo';
@ -19,8 +18,6 @@ const ScreenLockedView = (): JSX.Element => {
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const [data, setData] = useState<IData>({}); const [data, setData] = useState<IData>({});
const { theme } = useTheme();
useDeepCompareEffect(() => { useDeepCompareEffect(() => {
if (!isEmpty(data)) { if (!isEmpty(data)) {
setVisible(true); setVisible(true);
@ -62,7 +59,7 @@ const ScreenLockedView = (): JSX.Element => {
style={{ margin: 0 }} style={{ margin: 0 }}
animationIn='fadeIn' animationIn='fadeIn'
animationOut='fadeOut'> animationOut='fadeOut'>
<PasscodeEnter theme={theme} hasBiometry={!!data?.hasBiometry} finishProcess={onSubmit} /> <PasscodeEnter hasBiometry={!!data?.hasBiometry} finishProcess={onSubmit} />
</Modal> </Modal>
); );
}; };

View File

@ -70,7 +70,6 @@ const Item = ({ item, useRealName, user, badgeColor, onPress, toggleFollowThread
const username = (useRealName && item?.u?.name) || item?.u?.username; const username = (useRealName && item?.u?.name) || item?.u?.username;
let time; let time;
if (item?.ts) { if (item?.ts) {
// @ts-ignore TODO: to be fixed after we unify our types
time = formatDateThreads(item.ts); time = formatDateThreads(item.ts);
} }
@ -78,15 +77,15 @@ const Item = ({ item, useRealName, user, badgeColor, onPress, toggleFollowThread
<Touchable <Touchable
onPress={() => onPress(item)} onPress={() => onPress(item)}
testID={`thread-messages-view-${item.msg}`} testID={`thread-messages-view-${item.msg}`}
style={{ backgroundColor: themes[theme!].backgroundColor }}> style={{ backgroundColor: themes[theme].backgroundColor }}>
<View style={styles.container}> <View style={styles.container}>
<Avatar style={styles.avatar} text={item?.u?.username} size={36} borderRadius={4} theme={theme} /> <Avatar style={styles.avatar} text={item?.u?.username} size={36} borderRadius={4} theme={theme} />
<View style={styles.contentContainer}> <View style={styles.contentContainer}>
<View style={styles.titleContainer}> <View style={styles.titleContainer}>
<Text style={[styles.title, { color: themes[theme!].titleText }]} numberOfLines={1}> <Text style={[styles.title, { color: themes[theme].titleText }]} numberOfLines={1}>
{username} {username}
</Text> </Text>
<Text style={[styles.time, { color: themes[theme!].auxiliaryText }]}>{time}</Text> <Text style={[styles.time, { color: themes[theme].auxiliaryText }]}>{time}</Text>
</View> </View>
<View style={styles.messageContainer}> <View style={styles.messageContainer}>
<MarkdownPreview msg={makeThreadName(item)} numberOfLines={2} style={[styles.markdown]} /> <MarkdownPreview msg={makeThreadName(item)} numberOfLines={2} style={[styles.markdown]} />

View File

@ -19,7 +19,18 @@ const item = {
iconURL: 'https://open.rocket.chat/images/logo/android-chrome-512x512.png' iconURL: 'https://open.rocket.chat/images/logo/android-chrome-512x512.png'
}; };
const ServerItem = props => <ServerItemComponent item={item} hasCheck={false} {...props} />; const ServerItem = ({ theme = themes.light, ...props }) => (
<ThemeContext.Provider
value={{
theme,
themePreferences: {
currentTheme: theme,
darkLevel: theme
}
}}>
<ServerItemComponent item={item} hasCheck={false} {...props} />
</ThemeContext.Provider>
);
stories.add('content', () => ( stories.add('content', () => (
<> <>
@ -47,16 +58,10 @@ stories.add('touchable', () => (
</> </>
)); ));
const ThemeStory = ({ theme }) => (
<ThemeContext.Provider value={theme}>
<ServerItem theme={theme} hasCheck />
</ThemeContext.Provider>
);
stories.add('themes', () => ( stories.add('themes', () => (
<> <>
<ThemeStory theme={themes.light} /> <ServerItem theme={themes.light} />
<ThemeStory theme={themes.dark} /> <ServerItem theme={themes.dark} />
<ThemeStory theme={themes.black} /> <ServerItem theme={themes.black} />
</> </>
)); ));

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long