Chore: add eslint-plugin-react-hooks lib (#4021)

Co-authored-by: Diego Mello <diegolmello@gmail.com>
This commit is contained in:
Alex Junior 2022-04-11 15:01:43 -03:00 committed by GitHub
parent 53eb251476
commit b0d408ebc8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 113 additions and 72 deletions

View File

@ -17,7 +17,7 @@ module.exports = {
legacyDecorators: true legacyDecorators: true
} }
}, },
plugins: ['react', 'jsx-a11y', 'import', 'react-native', '@babel', 'jest'], plugins: ['react', 'jsx-a11y', 'import', 'react-native', '@babel', 'jest', 'react-hooks'],
env: { env: {
browser: true, browser: true,
commonjs: true, commonjs: true,
@ -148,7 +148,9 @@ module.exports = {
'no-async-promise-executor': [0], 'no-async-promise-executor': [0],
'max-classes-per-file': [0], 'max-classes-per-file': [0],
'no-multiple-empty-lines': [0], 'no-multiple-empty-lines': [0],
'no-sequences': 'off' 'no-sequences': 'off',
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn'
}, },
globals: { globals: {
__DEV__: true __DEV__: true
@ -237,7 +239,9 @@ module.exports = {
} }
], ],
'new-cap': 'off', 'new-cap': 'off',
'lines-between-class-members': 'off' 'lines-between-class-members': 'off',
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn'
}, },
globals: { globals: {
JSX: true JSX: true

View File

@ -1,4 +1,4 @@
import React from 'react'; import React, { useContext, memo, useEffect } from 'react';
import { NavigationContainer } from '@react-navigation/native'; import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack'; import { createStackNavigator } from '@react-navigation/stack';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
@ -27,21 +27,23 @@ const SetUsernameStack = () => (
// App // App
const Stack = createStackNavigator<StackParamList>(); const Stack = createStackNavigator<StackParamList>();
const App = React.memo(({ root, isMasterDetail }: { root: string; isMasterDetail: boolean }) => { const App = memo(({ root, isMasterDetail }: { root: string; isMasterDetail: boolean }) => {
const { theme } = useContext(ThemeContext);
useEffect(() => {
if (root) {
const state = Navigation.navigationRef.current?.getRootState();
const currentRouteName = getActiveRouteName(state);
Navigation.routeNameRef.current = currentRouteName;
setCurrentScreen(currentRouteName);
}
}, [root]);
if (!root) { if (!root) {
return null; return null;
} }
const { theme } = React.useContext(ThemeContext);
const navTheme = navigationTheme(theme); const navTheme = navigationTheme(theme);
React.useEffect(() => {
const state = Navigation.navigationRef.current?.getRootState();
const currentRouteName = getActiveRouteName(state);
Navigation.routeNameRef.current = currentRouteName;
setCurrentScreen(currentRouteName);
}, []);
return ( return (
<NavigationContainer <NavigationContainer
theme={navTheme} theme={navTheme}

View File

@ -30,12 +30,12 @@ const Avatar = React.memo(
borderRadius = 4, borderRadius = 4,
type = SubscriptionType.DIRECT type = SubscriptionType.DIRECT
}: IAvatar) => { }: IAvatar) => {
const { theme } = useTheme();
if ((!text && !avatar && !emoji && !rid) || !server) { if ((!text && !avatar && !emoji && !rid) || !server) {
return null; return null;
} }
const { theme } = useTheme();
const avatarStyle = { const avatarStyle = {
width: size, width: size,
height: size, height: size,

View File

@ -15,10 +15,12 @@ interface IMessageBoxCommandsPreview {
const CommandsPreview = React.memo( const CommandsPreview = React.memo(
({ commandPreview, showCommandPreview }: IMessageBoxCommandsPreview) => { ({ commandPreview, showCommandPreview }: IMessageBoxCommandsPreview) => {
const { theme } = useTheme();
if (!showCommandPreview) { if (!showCommandPreview) {
return null; return null;
} }
const { theme } = useTheme();
return ( return (
<FlatList <FlatList
testID='commandbox-container' testID='commandbox-container'

View File

@ -16,10 +16,12 @@ interface IMessageBoxMentions {
const Mentions = React.memo( const Mentions = React.memo(
({ mentions, trackingType, loading }: IMessageBoxMentions) => { ({ mentions, trackingType, loading }: IMessageBoxMentions) => {
const { theme } = useTheme();
if (!trackingType) { if (!trackingType) {
return null; return null;
} }
const { theme } = useTheme();
return ( return (
<View testID='messagebox-container'> <View testID='messagebox-container'>
<FlatList <FlatList

View File

@ -56,10 +56,12 @@ interface IMessageBoxReplyPreview {
const ReplyPreview = React.memo( const ReplyPreview = React.memo(
({ message, Message_TimeFormat, replying, close, useRealName }: IMessageBoxReplyPreview) => { ({ message, Message_TimeFormat, replying, close, useRealName }: IMessageBoxReplyPreview) => {
const { theme } = useTheme();
if (!replying) { if (!replying) {
return null; return null;
} }
const { theme } = useTheme();
const time = moment(message.ts).format(Message_TimeFormat); const time = moment(message.ts).format(Message_TimeFormat);
return ( return (
<View style={[styles.container, { backgroundColor: themes[theme].messageboxBackground }]}> <View style={[styles.container, { backgroundColor: themes[theme].messageboxBackground }]}>

View File

@ -1,4 +1,4 @@
/* eslint-disable class-methods-use-this */ /* eslint-disable react-hooks/rules-of-hooks */
import React, { useContext } from 'react'; import React, { useContext } from 'react';
import { StyleSheet, Text } from 'react-native'; import { StyleSheet, Text } from 'react-native';
import { BLOCK_CONTEXT, UiKitParserMessage, UiKitParserModal, uiKitMessage, uiKitModal } from '@rocket.chat/ui-kit'; import { BLOCK_CONTEXT, UiKitParserMessage, UiKitParserModal, uiKitMessage, uiKitModal } from '@rocket.chat/ui-kit';

View File

@ -17,12 +17,12 @@ interface IMarkdownPreview {
} }
const MarkdownPreview = ({ msg, numberOfLines = 1, testID, style = [] }: IMarkdownPreview) => { const MarkdownPreview = ({ msg, numberOfLines = 1, testID, style = [] }: IMarkdownPreview) => {
const { theme } = useTheme();
if (!msg) { if (!msg) {
return null; return null;
} }
const { theme } = useTheme();
let m = formatText(msg); let m = formatText(msg);
m = formatHyperlink(m); m = formatHyperlink(m);
m = shortnameToUnicode(m); m = shortnameToUnicode(m);

View File

@ -24,11 +24,12 @@ export type TElement = {
}; };
const AttachedActions = ({ attachment }: { attachment: IAttachment }) => { const AttachedActions = ({ attachment }: { attachment: IAttachment }) => {
const { onAnswerButtonPress } = useContext(MessageContext);
const { theme } = useTheme();
if (!attachment.actions) { if (!attachment.actions) {
return null; return null;
} }
const { onAnswerButtonPress } = useContext(MessageContext);
const { theme } = useTheme();
const attachedButtons = attachment.actions.map((element: TElement) => { const attachedButtons = attachment.actions.map((element: TElement) => {
const onPress = () => { const onPress = () => {
@ -57,12 +58,12 @@ const AttachedActions = ({ attachment }: { attachment: IAttachment }) => {
const Attachments: React.FC<IMessageAttachments> = React.memo( const Attachments: React.FC<IMessageAttachments> = React.memo(
({ attachments, timeFormat, showAttachment, style, getCustomEmoji, isReply }: IMessageAttachments) => { ({ attachments, timeFormat, showAttachment, style, getCustomEmoji, isReply }: IMessageAttachments) => {
const { theme } = useTheme();
if (!attachments || attachments.length === 0) { if (!attachments || attachments.length === 0) {
return null; return null;
} }
const { theme } = useTheme();
const attachmentsElements = attachments.map((file: IAttachment, index: number) => { const attachmentsElements = attachments.map((file: IAttachment, index: number) => {
if (file && file.image_url) { if (file && file.image_url) {
return ( return (

View File

@ -81,11 +81,13 @@ interface IMessageReply {
const Fields = React.memo( const Fields = React.memo(
({ attachment, getCustomEmoji }: IMessageFields) => { ({ attachment, getCustomEmoji }: IMessageFields) => {
const { theme } = useTheme();
const { baseUrl, user } = useContext(MessageContext);
if (!attachment.fields) { if (!attachment.fields) {
return null; return null;
} }
const { baseUrl, user } = useContext(MessageContext);
const { theme } = useTheme();
return ( return (
<> <>
{attachment.fields.map(field => ( {attachment.fields.map(field => (
@ -111,11 +113,12 @@ const Fields = React.memo(
const CollapsibleQuote = React.memo( const CollapsibleQuote = React.memo(
({ attachment, index, getCustomEmoji }: IMessageReply) => { ({ attachment, index, getCustomEmoji }: IMessageReply) => {
const { theme } = useTheme();
const [collapsed, setCollapsed] = useState(attachment?.collapsed);
if (!attachment) { if (!attachment) {
return null; return null;
} }
const [collapsed, setCollapsed] = useState(attachment.collapsed);
const { theme } = useTheme();
const onPress = () => { const onPress = () => {
setCollapsed(!collapsed); setCollapsed(!collapsed);

View File

@ -16,6 +16,8 @@ import { E2E_MESSAGE_TYPE, themes } from '../../lib/constants';
const Content = React.memo( const Content = React.memo(
(props: IMessageContent) => { (props: IMessageContent) => {
const { theme } = useTheme(); const { theme } = useTheme();
const { baseUrl, user, onLinkPress } = useContext(MessageContext);
if (props.isInfo) { if (props.isInfo) {
// @ts-ignore // @ts-ignore
const infoMessage = getInfoMessage({ ...props }); const infoMessage = getInfoMessage({ ...props });
@ -49,7 +51,6 @@ const Content = React.memo(
} else if (isPreview) { } else if (isPreview) {
content = <MarkdownPreview msg={props.msg} />; content = <MarkdownPreview msg={props.msg} />;
} else { } else {
const { baseUrl, user, onLinkPress } = useContext(MessageContext);
content = ( content = (
<Markdown <Markdown
msg={props.msg} msg={props.msg}

View File

@ -10,11 +10,12 @@ import { E2E_MESSAGE_TYPE, themes } from '../../lib/constants';
const Encrypted = React.memo(({ type }: { type: string }) => { const Encrypted = React.memo(({ type }: { type: string }) => {
const { theme } = useTheme(); const { theme } = useTheme();
const { onEncryptedPress } = useContext(MessageContext);
if (type !== E2E_MESSAGE_TYPE) { if (type !== E2E_MESSAGE_TYPE) {
return null; return null;
} }
const { onEncryptedPress } = useContext(MessageContext);
return ( return (
<Touchable onPress={onEncryptedPress} style={styles.encrypted} hitSlop={BUTTON_HIT_SLOP}> <Touchable onPress={onEncryptedPress} style={styles.encrypted} hitSlop={BUTTON_HIT_SLOP}>
<CustomIcon name='encrypted' size={16} color={themes[theme].auxiliaryText} /> <CustomIcon name='encrypted' size={16} color={themes[theme].auxiliaryText} />

View File

@ -110,6 +110,9 @@ const Message = React.memo((props: IMessage) => {
Message.displayName = 'Message'; Message.displayName = 'Message';
const MessageTouchable = React.memo((props: IMessageTouchable & IMessage) => { const MessageTouchable = React.memo((props: IMessageTouchable & IMessage) => {
const { onPress, onLongPress } = useContext(MessageContext);
const { theme } = useTheme();
if (props.hasError) { if (props.hasError) {
return ( return (
<View> <View>
@ -117,8 +120,6 @@ const MessageTouchable = React.memo((props: IMessageTouchable & IMessage) => {
</View> </View>
); );
} }
const { onPress, onLongPress } = useContext(MessageContext);
const { theme } = useTheme();
return ( return (
<Touchable <Touchable

View File

@ -11,11 +11,12 @@ import { useTheme } from '../../theme';
const MessageError = React.memo( const MessageError = React.memo(
({ hasError }: { hasError: boolean }) => { ({ hasError }: { hasError: boolean }) => {
const { theme } = useTheme(); const { theme } = useTheme();
const { onErrorPress } = useContext(MessageContext);
if (!hasError) { if (!hasError) {
return null; return null;
} }
const { onErrorPress } = useContext(MessageContext);
return ( return (
<Touchable onPress={onErrorPress} style={styles.errorButton} hitSlop={BUTTON_HIT_SLOP}> <Touchable onPress={onErrorPress} style={styles.errorButton} hitSlop={BUTTON_HIT_SLOP}>
<CustomIcon name='warning' color={themes[theme].dangerColor} size={18} /> <CustomIcon name='warning' color={themes[theme].dangerColor} size={18} />

View File

@ -11,16 +11,7 @@ import { useTheme } from '../../theme';
const RepliedThread = memo(({ tmid, tmsg, isHeader, fetchThreadName, id, isEncrypted }: IMessageRepliedThread) => { const RepliedThread = memo(({ tmid, tmsg, isHeader, fetchThreadName, id, isEncrypted }: IMessageRepliedThread) => {
const { theme } = useTheme(); const { theme } = useTheme();
if (!tmid || !isHeader) {
return null;
}
const [msg, setMsg] = useState(isEncrypted ? I18n.t('Encrypted_message') : tmsg); const [msg, setMsg] = useState(isEncrypted ? I18n.t('Encrypted_message') : tmsg);
const fetch = async () => {
const threadName = fetchThreadName ? await fetchThreadName(tmid, id) : '';
setMsg(threadName);
};
useEffect(() => { useEffect(() => {
if (!msg) { if (!msg) {
@ -28,6 +19,15 @@ const RepliedThread = memo(({ tmid, tmsg, isHeader, fetchThreadName, id, isEncry
} }
}, []); }, []);
if (!tmid || !isHeader) {
return null;
}
const fetch = async () => {
const threadName = fetchThreadName ? await fetchThreadName(tmid, id) : '';
setMsg(threadName);
};
if (!msg) { if (!msg) {
return null; return null;
} }

View File

@ -113,11 +113,13 @@ const Title = React.memo(({ attachment, timeFormat, theme }: { attachment: IAtta
const Description = React.memo( const Description = React.memo(
({ attachment, getCustomEmoji, theme }: { attachment: IAttachment; getCustomEmoji: TGetCustomEmoji; theme: string }) => { ({ attachment, getCustomEmoji, theme }: { attachment: IAttachment; getCustomEmoji: TGetCustomEmoji; theme: string }) => {
const { baseUrl, user } = useContext(MessageContext);
const text = attachment.text || attachment.title; const text = attachment.text || attachment.title;
if (!text) { if (!text) {
return null; return null;
} }
const { baseUrl, user } = useContext(MessageContext);
return ( return (
<Markdown <Markdown
msg={text} msg={text}
@ -145,10 +147,12 @@ const Description = React.memo(
const UrlImage = React.memo( const UrlImage = React.memo(
({ image }: { image?: string }) => { ({ image }: { image?: string }) => {
const { baseUrl, user } = useContext(MessageContext);
if (!image) { if (!image) {
return null; return null;
} }
const { baseUrl, user } = useContext(MessageContext);
image = image.includes('http') ? image : `${baseUrl}/${image}?rc_uid=${user.id}&rc_token=${user.token}`; image = image.includes('http') ? image : `${baseUrl}/${image}?rc_uid=${user.id}&rc_token=${user.token}`;
return <FastImage source={{ uri: image }} style={styles.image} resizeMode={FastImage.resizeMode.cover} />; return <FastImage source={{ uri: image }} style={styles.image} resizeMode={FastImage.resizeMode.cover} />;
}, },
@ -157,11 +161,12 @@ const UrlImage = React.memo(
const Fields = React.memo( const Fields = React.memo(
({ attachment, theme, getCustomEmoji }: { attachment: IAttachment; theme: string; getCustomEmoji: TGetCustomEmoji }) => { ({ attachment, theme, getCustomEmoji }: { attachment: IAttachment; theme: string; getCustomEmoji: TGetCustomEmoji }) => {
const { baseUrl, user } = useContext(MessageContext);
if (!attachment.fields) { if (!attachment.fields) {
return null; return null;
} }
const { baseUrl, user } = useContext(MessageContext);
return ( return (
<View style={styles.fieldsContainer}> <View style={styles.fieldsContainer}>
{attachment.fields.map(field => ( {attachment.fields.map(field => (
@ -187,13 +192,12 @@ const Reply = React.memo(
({ attachment, timeFormat, index, getCustomEmoji }: IMessageReply) => { ({ attachment, timeFormat, index, getCustomEmoji }: IMessageReply) => {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const { theme } = useTheme(); const { theme } = useTheme();
const { baseUrl, user, jumpToMessage } = useContext(MessageContext);
if (!attachment) { if (!attachment) {
return null; return null;
} }
const { baseUrl, user, jumpToMessage } = useContext(MessageContext);
const onPress = async () => { const onPress = async () => {
let url = attachment.title_link || attachment.author_link; let url = attachment.title_link || attachment.author_link;
if (attachment.message_link) { if (attachment.message_link) {

View File

@ -12,12 +12,12 @@ import { useTheme } from '../../theme';
const Thread = React.memo( const Thread = React.memo(
({ msg, tcount, tlm, isThreadRoom, id }: IMessageThread) => { ({ msg, tcount, tlm, isThreadRoom, id }: IMessageThread) => {
const { theme } = useTheme(); const { theme } = useTheme();
const { threadBadgeColor, toggleFollowThread, user, replies } = useContext(MessageContext);
if (!tlm || isThreadRoom || tcount === 0) { if (!tlm || isThreadRoom || tcount === 0) {
return null; return null;
} }
const { threadBadgeColor, toggleFollowThread, user, replies } = useContext(MessageContext);
return ( return (
<View style={styles.buttonContainer}> <View style={styles.buttonContainer}>
<View style={[styles.button, { backgroundColor: themes[theme].tintColor }]} testID={`message-thread-button-${msg}`}> <View style={[styles.button, { backgroundColor: themes[theme].tintColor }]} testID={`message-thread-button-${msg}`}>

View File

@ -53,10 +53,12 @@ const styles = StyleSheet.create({
const UrlImage = React.memo( const UrlImage = React.memo(
({ image }: { image: string }) => { ({ image }: { image: string }) => {
const { baseUrl, user } = useContext(MessageContext);
if (!image) { if (!image) {
return null; return null;
} }
const { baseUrl, user } = useContext(MessageContext);
image = image.includes('http') ? image : `${baseUrl}/${image}?rc_uid=${user.id}&rc_token=${user.token}`; image = image.includes('http') ? image : `${baseUrl}/${image}?rc_uid=${user.id}&rc_token=${user.token}`;
return <FastImage source={{ uri: image }} style={styles.image} resizeMode={FastImage.resizeMode.cover} />; return <FastImage source={{ uri: image }} style={styles.image} resizeMode={FastImage.resizeMode.cover} />;
}, },

View File

@ -57,9 +57,10 @@ interface IMessageUser {
const User = React.memo( const User = React.memo(
({ isHeader, useRealName, author, alias, ts, timeFormat, hasError, navToRoomInfo, type, ...props }: IMessageUser) => { ({ isHeader, useRealName, author, alias, ts, timeFormat, hasError, navToRoomInfo, type, ...props }: IMessageUser) => {
const { user } = useContext(MessageContext);
const { theme } = useTheme();
if (isHeader || hasError) { if (isHeader || hasError) {
const { user } = useContext(MessageContext);
const { theme } = useTheme();
const username = (useRealName && author?.name) || author?.username; const username = (useRealName && author?.name) || author?.username;
const aliasUsername = alias ? ( const aliasUsername = alias ? (
<Text style={[styles.alias, { color: themes[theme].auxiliaryText }]}> @{username}</Text> <Text style={[styles.alias, { color: themes[theme].auxiliaryText }]}> @{username}</Text>

View File

@ -6,7 +6,7 @@ import Message from './Message';
import MessageContext from './Context'; import MessageContext from './Context';
import debounce from '../../utils/debounce'; import debounce from '../../utils/debounce';
import { SYSTEM_MESSAGES, getMessageTranslation } from './utils'; import { SYSTEM_MESSAGES, getMessageTranslation } from './utils';
import { useTheme, withTheme } from '../../theme'; import { withTheme } from '../../theme';
import openLink from '../../utils/openLink'; import openLink from '../../utils/openLink';
import { TGetCustomEmoji } from '../../definitions/IEmoji'; import { TGetCustomEmoji } from '../../definitions/IEmoji';
import { IAttachment, TAnyMessageModel } from '../../definitions'; import { IAttachment, TAnyMessageModel } from '../../definitions';
@ -57,6 +57,7 @@ interface IMessageContainerProps {
toggleFollowThread?: (isFollowingThread: boolean, tmid?: string) => Promise<void>; toggleFollowThread?: (isFollowingThread: boolean, tmid?: string) => Promise<void>;
jumpToMessage?: (link: string) => void; jumpToMessage?: (link: string) => void;
onPress?: () => void; onPress?: () => void;
theme: string;
} }
interface IMessageContainerState { interface IMessageContainerState {
@ -292,8 +293,7 @@ class MessageContainer extends React.Component<IMessageContainerProps, IMessageC
}; };
onLinkPress = (link: string): void => { onLinkPress = (link: string): void => {
const { theme } = useTheme(); const { item, jumpToMessage, theme } = this.props;
const { item, jumpToMessage } = this.props;
const isMessageLink = item?.attachments?.findIndex((att: IAttachment) => att?.message_link === link) !== -1; const isMessageLink = item?.attachments?.findIndex((att: IAttachment) => att?.message_link === link) !== -1;
if (isMessageLink && jumpToMessage) { if (isMessageLink && jumpToMessage) {
return jumpToMessage(link); return jumpToMessage(link);

View File

@ -20,16 +20,20 @@ interface IOmnichannelStatus {
} }
const OmnichannelStatus = memo(({ searching, goQueue, queueSize, inquiryEnabled, user }: IOmnichannelStatus) => { const OmnichannelStatus = memo(({ searching, goQueue, queueSize, inquiryEnabled, user }: IOmnichannelStatus) => {
if (searching || !(RocketChat.isOmnichannelModuleAvailable() && user?.roles?.includes('livechat-agent'))) {
return null;
}
const { theme } = useTheme(); const { theme } = useTheme();
const [status, setStatus] = useState(isOmnichannelStatusAvailable(user)); const [status, setStatus] = useState<boolean>(false);
const canUseOmnichannel = RocketChat.isOmnichannelModuleAvailable() && user?.roles?.includes('livechat-agent');
useEffect(() => { useEffect(() => {
setStatus(isOmnichannelStatusAvailable(user)); if (canUseOmnichannel) {
setStatus(isOmnichannelStatusAvailable(user));
}
}, [user.statusLivechat]); }, [user.statusLivechat]);
if (searching || !canUseOmnichannel) {
return null;
}
const toggleLivechat = async () => { const toggleLivechat = async () => {
try { try {
setStatus(v => !v); setStatus(v => !v);

View File

@ -6,7 +6,7 @@ import { getDeviceToken } from '../../notifications/push';
import { extractHostname } from '../../utils/server'; import { extractHostname } from '../../utils/server';
import { BASIC_AUTH_KEY } from '../../utils/fetch'; import { BASIC_AUTH_KEY } from '../../utils/fetch';
import database, { getDatabase } from '../database'; import database, { getDatabase } from '../database';
import { useSsl } from '../../utils/url'; import { isSsl } from '../../utils/url';
import log from '../../utils/log'; import log from '../../utils/log';
import { ICertificate, IRocketChat } from '../../definitions'; import { ICertificate, IRocketChat } from '../../definitions';
import sdk from '../services/sdk'; import sdk from '../services/sdk';
@ -80,7 +80,7 @@ export async function removeServer({ server }: { server: string }): Promise<void
if (userId) { if (userId) {
const resume = UserPreferences.getString(`${RocketChat.TOKEN_KEY}-${userId}`); const resume = UserPreferences.getString(`${RocketChat.TOKEN_KEY}-${userId}`);
const sdk = new RocketchatClient({ host: server, protocol: 'ddp', useSsl: useSsl(server) }); const sdk = new RocketchatClient({ host: server, protocol: 'ddp', useSsl: isSsl(server) });
await sdk.login({ resume }); await sdk.login({ resume });
const token = getDeviceToken(); const token = getDeviceToken();

View File

@ -760,7 +760,7 @@ export const validateInviteToken = (token: string): any =>
// @ts-ignore // @ts-ignore
sdk.post('validateInviteToken', { token }); sdk.post('validateInviteToken', { token });
export const useInviteToken = (token: string): any => export const inviteToken = (token: string): any =>
// RC 2.4.0 // RC 2.4.0
// TODO: missing definitions from server // TODO: missing definitions from server
// @ts-ignore // @ts-ignore

View File

@ -3,7 +3,7 @@ import EJSON from 'ejson';
import isEmpty from 'lodash/isEmpty'; import isEmpty from 'lodash/isEmpty';
import { twoFactor } from '../../utils/twoFactor'; import { twoFactor } from '../../utils/twoFactor';
import { useSsl } from '../../utils/url'; import { isSsl } from '../../utils/url';
import { store as reduxStore } from '../store/auxStore'; import { store as reduxStore } from '../store/auxStore';
import { Serialized, MatchPathPattern, OperationParams, PathFor, ResultFor } from '../../definitions/rest/helpers'; import { Serialized, MatchPathPattern, OperationParams, PathFor, ResultFor } from '../../definitions/rest/helpers';
@ -14,7 +14,7 @@ class Sdk {
private initializeSdk(server: string): typeof Rocketchat { private initializeSdk(server: string): typeof Rocketchat {
// The app can't reconnect if reopen interval is 5s while in development // The app can't reconnect if reopen interval is 5s while in development
return new Rocketchat({ host: server, protocol: 'ddp', useSsl: useSsl(server), reopen: __DEV__ ? 20000 : 5000 }); return new Rocketchat({ host: server, protocol: 'ddp', useSsl: isSsl(server), reopen: __DEV__ ? 20000 : 5000 });
} }
// TODO: We need to stop returning the SDK after all methods are dehydrated // TODO: We need to stop returning the SDK after all methods are dehydrated

View File

@ -16,7 +16,7 @@ const handleRequest = function* handleRequest({ token }) {
return; return;
} }
const result = yield RocketChat.useInviteToken(token); const result = yield RocketChat.inviteToken(token);
if (!result.success) { if (!result.success) {
yield put(inviteLinksFailure()); yield put(inviteLinksFailure());
return; return;

View File

@ -11,5 +11,5 @@ export const isValidURL = (url: string): boolean => {
return !!pattern.test(url); return !!pattern.test(url);
}; };
// Use useSsl: false only if server url starts with http:// // Use checkUseSsl: false only if server url starts with http://
export const useSsl = (url: string): boolean => !/http:\/\//.test(url); export const isSsl = (url: string): boolean => !/http:\/\//.test(url);

View File

@ -201,6 +201,7 @@ class MessagesView extends React.Component<IMessagesViewProps, any> {
renderItem: (item: any) => ( renderItem: (item: any) => (
<Message <Message
{...renderItemCommonProps(item)} {...renderItemCommonProps(item)}
theme={theme}
item={{ item={{
...item, ...item,
u: item.user, u: item.user,

View File

@ -40,6 +40,8 @@ const LeftButtons = ({
goRoomActionsView, goRoomActionsView,
isMasterDetail isMasterDetail
}: ILeftButtonsProps): React.ReactElement | null => { }: ILeftButtonsProps): React.ReactElement | null => {
const onPress = useCallback(() => goRoomActionsView(), []);
if (!isMasterDetail || tmid) { if (!isMasterDetail || tmid) {
const onPress = () => navigation.goBack(); const onPress = () => navigation.goBack();
let label = ' '; let label = ' ';
@ -61,7 +63,6 @@ const LeftButtons = ({
/> />
); );
} }
const onPress = useCallback(() => goRoomActionsView(), []);
if (baseUrl && userId && token) { if (baseUrl && userId && token) {
return <Avatar text={title} size={30} type={t} style={styles.avatar} onPress={onPress} />; return <Avatar text={title} size={30} type={t} style={styles.avatar} onPress={onPress} />;

View File

@ -1144,7 +1144,8 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
renderItem = (item: TAnyMessageModel, previousItem: TAnyMessageModel, highlightedMessage?: string) => { renderItem = (item: TAnyMessageModel, previousItem: TAnyMessageModel, highlightedMessage?: string) => {
const { room, lastOpen, canAutoTranslate } = this.state; const { room, lastOpen, canAutoTranslate } = this.state;
const { user, Message_GroupingPeriod, Message_TimeFormat, useRealName, baseUrl, Message_Read_Receipt_Enabled } = this.props; const { user, Message_GroupingPeriod, Message_TimeFormat, useRealName, baseUrl, Message_Read_Receipt_Enabled, theme } =
this.props;
let dateSeparator = null; let dateSeparator = null;
let showUnreadSeparator = false; let showUnreadSeparator = false;
@ -1207,6 +1208,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
toggleFollowThread={this.toggleFollowThread} toggleFollowThread={this.toggleFollowThread}
jumpToMessage={this.jumpToMessageByUrl} jumpToMessage={this.jumpToMessageByUrl}
highlighted={highlightedMessage === item.id} highlighted={highlightedMessage === item.id}
theme={theme}
/> />
); );
} }

View File

@ -20,12 +20,12 @@ interface IRoomListHeader {
const ListHeader = React.memo( const ListHeader = React.memo(
({ searching, goEncryption, goQueue, queueSize, inquiryEnabled, encryptionBanner, user }: IRoomListHeader) => { ({ searching, goEncryption, goQueue, queueSize, inquiryEnabled, encryptionBanner, user }: IRoomListHeader) => {
const { theme } = useTheme();
if (searching) { if (searching) {
return null; return null;
} }
const { theme } = useTheme();
return ( return (
<> <>
{encryptionBanner ? ( {encryptionBanner ? (

View File

@ -174,6 +174,7 @@
"eslint-plugin-jest": "24.7.0", "eslint-plugin-jest": "24.7.0",
"eslint-plugin-jsx-a11y": "6.3.1", "eslint-plugin-jsx-a11y": "6.3.1",
"eslint-plugin-react": "7.20.3", "eslint-plugin-react": "7.20.3",
"eslint-plugin-react-hooks": "^4.4.0",
"eslint-plugin-react-native": "3.8.1", "eslint-plugin-react-native": "3.8.1",
"husky": "^6.0.0", "husky": "^6.0.0",
"identity-obj-proxy": "^3.0.0", "identity-obj-proxy": "^3.0.0",

View File

@ -8570,6 +8570,11 @@ eslint-plugin-jsx-a11y@6.3.1:
jsx-ast-utils "^2.4.1" jsx-ast-utils "^2.4.1"
language-tags "^1.0.5" language-tags "^1.0.5"
eslint-plugin-react-hooks@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.4.0.tgz#71c39e528764c848d8253e1aa2c7024ed505f6c4"
integrity sha512-U3RVIfdzJaeKDQKEJbz5p3NW8/L80PCATJAfuojwbaEL+gBjfGdhUcGde+WGUW46Q5sr/NgxevsIiDtNXrvZaQ==
eslint-plugin-react-native-globals@^0.1.1: eslint-plugin-react-native-globals@^0.1.1:
version "0.1.2" version "0.1.2"
resolved "https://registry.yarnpkg.com/eslint-plugin-react-native-globals/-/eslint-plugin-react-native-globals-0.1.2.tgz#ee1348bc2ceb912303ce6bdbd22e2f045ea86ea2" resolved "https://registry.yarnpkg.com/eslint-plugin-react-native-globals/-/eslint-plugin-react-native-globals-0.1.2.tgz#ee1348bc2ceb912303ce6bdbd22e2f045ea86ea2"