From 9bf57701a3756ecd2293ba228124857fbecd04fe Mon Sep 17 00:00:00 2001 From: Gabriel Henriques Date: Wed, 17 Jun 2020 16:58:49 -0300 Subject: [PATCH 1/8] Fixed hooks lint rules --- .storybook/hooks.js | 2 +- app/api/server/v1/invites.js | 1 + client/admin/cloud/CloudPage.js | 4 +- .../cloud/ManualWorkspaceRegistrationModal.js | 4 +- client/admin/cloud/WorkspaceLoginSection.js | 2 +- client/admin/customEmoji/AddCustomEmoji.js | 2 +- client/admin/customEmoji/CustomEmoji.js | 4 +- client/admin/customEmoji/CustomEmojiRoute.js | 13 +- client/admin/customEmoji/EditCustomEmoji.js | 9 +- client/admin/customSounds/AddCustomSound.js | 12 +- client/admin/customSounds/AdminSoundsRoute.js | 29 +-- client/admin/customSounds/EditCustomSound.js | 18 +- client/admin/customSounds/EditSound.js | 202 ------------------ client/admin/customSounds/NewSound.js | 101 --------- .../customUserStatus/AddCustomUserStatus.js | 2 +- .../customUserStatus/CustomUserStatus.js | 4 +- .../customUserStatus/CustomUserStatusRoute.js | 25 +-- .../customUserStatus/EditCustomUserStatus.js | 9 +- client/admin/import/ImportHistoryPage.js | 4 +- client/admin/import/ImportProgressPage.js | 6 +- client/admin/import/NewImportPage.js | 2 +- client/admin/import/PrepareImportPage.js | 12 +- client/admin/info/InformationRoute.js | 2 +- .../admin/integrations/IncomingWebhookForm.js | 46 ++-- client/admin/integrations/IntegrationsPage.js | 12 +- .../admin/integrations/IntegrationsTable.js | 28 +-- .../integrations/OutgoiongWebhookForm.js | 69 +++--- .../integrations/edit/EditIncomingWebhook.js | 13 +- .../integrations/edit/EditIntegrationsPage.js | 4 +- .../integrations/edit/EditOutgoingWebhook.js | 13 +- .../edit/OutgoingWebhookHistoryPage.js | 47 ++-- .../admin/integrations/exampleIncomingData.js | 34 +-- .../integrations/new/NewIncomingWebhook.js | 9 +- .../integrations/new/NewIntegrationsPage.js | 13 +- .../integrations/new/NewOutgoingWebhook.js | 3 +- client/admin/invites/InvitesPage.js | 2 +- client/admin/oauthApps/OAuthAddApp.js | 2 +- client/admin/oauthApps/OAuthAppsTable.js | 9 +- client/admin/oauthApps/OAuthEditApp.js | 9 +- client/admin/rooms/EditRoom.js | 9 +- client/admin/rooms/RoomsTable.js | 28 +-- client/admin/settings/Setting.js | 4 +- .../admin/settings/inputs/CodeSettingInput.js | 2 +- .../settings/inputs/ColorSettingInput.js | 4 +- .../settings/inputs/RoomPickSettingInput.js | 2 +- client/admin/users/AddUser.js | 11 +- client/admin/users/CustomFieldsForm.js | 21 +- client/admin/users/EditUser.js | 16 +- client/admin/users/UserForm.js | 22 +- client/admin/users/UserInfo.js | 1 + client/admin/users/UserInfoActions.js | 30 ++- client/admin/users/UsersTable.js | 30 +-- client/admin/viewLogs/ViewLogs.js | 9 +- client/components/GenericTable.js | 8 +- client/components/SortList.js | 26 +-- client/components/basic/Modal.js | 2 +- client/contexts/RouterContext.js | 2 +- client/contexts/ServerContext.ts | 4 +- client/hooks/useEndpointAction.js | 2 +- client/hooks/useEndpointData.js | 2 +- client/hooks/useEndpointDataExperimental.js | 2 +- client/hooks/useEndpointUpload.js | 2 +- client/hooks/useForm.js | 1 + client/hooks/useFormatTime.js | 2 +- client/hooks/useHighlightedCode.js | 6 + client/hooks/useHilightCode.js | 6 - client/hooks/useObservableValue.js | 2 +- client/views/blocks/ModalBlock.js | 6 +- client/views/directory/ChannelsTab.js | 23 +- client/views/directory/DirectoryPage.js | 6 +- client/views/directory/UserTab.js | 31 ++- client/views/directory/hooks.js | 12 +- client/views/setupWizard/SetupWizardRoute.js | 2 +- client/views/setupWizard/SetupWizardState.js | 9 +- .../UsersTab/UsersByTimeOfTheDaySection.js | 2 +- 75 files changed, 434 insertions(+), 695 deletions(-) delete mode 100644 client/admin/customSounds/EditSound.js delete mode 100644 client/admin/customSounds/NewSound.js create mode 100644 client/hooks/useHighlightedCode.js delete mode 100644 client/hooks/useHilightCode.js diff --git a/.storybook/hooks.js b/.storybook/hooks.js index d5e2dcd1c06d1..4f9fc7ef96738 100644 --- a/.storybook/hooks.js +++ b/.storybook/hooks.js @@ -9,7 +9,7 @@ export const useAutoToggle = (initialValue = false, ms = 1000) => { return () => { clearInterval(timer); }; - }, []); + }, [ms]); return value; }; diff --git a/app/api/server/v1/invites.js b/app/api/server/v1/invites.js index 513f99f7808e0..9409458e3093a 100644 --- a/app/api/server/v1/invites.js +++ b/app/api/server/v1/invites.js @@ -35,6 +35,7 @@ API.v1.addRoute('removeInvite/:_id', { authRequired: true }, { API.v1.addRoute('useInviteToken', { authRequired: true }, { post() { const { token } = this.bodyParams; + // eslint-disable-next-line react-hooks/rules-of-hooks const result = useInviteToken(this.userId, token); return API.v1.success(result); diff --git a/client/admin/cloud/CloudPage.js b/client/admin/cloud/CloudPage.js index e4e285da741a6..3223014073b8b 100644 --- a/client/admin/cloud/CloudPage.js +++ b/client/admin/cloud/CloudPage.js @@ -58,7 +58,7 @@ function CloudPage() { }; acceptOAuthAuthorization(); - }, [errorCode, code, state]); + }, [errorCode, code, state, page, dispatchToastMessage, t, cloudRoute, finishOAuthAuthorization]); const [registerStatus, setRegisterStatus] = useSafely(useState()); const [modal, setModal] = useState(null); @@ -92,7 +92,7 @@ function CloudPage() { }; acceptWorkspaceToken(); - }, [token]); + }, [connectWorkspace, dispatchToastMessage, fetchRegisterStatus, t, token]); const handleManualWorkspaceRegistrationButtonClick = () => { const handleModalClose = () => { diff --git a/client/admin/cloud/ManualWorkspaceRegistrationModal.js b/client/admin/cloud/ManualWorkspaceRegistrationModal.js index ffd3a68bc4b00..b424942a1e729 100644 --- a/client/admin/cloud/ManualWorkspaceRegistrationModal.js +++ b/client/admin/cloud/ManualWorkspaceRegistrationModal.js @@ -24,7 +24,7 @@ function CopyStep({ onNextButtonClick }) { }; loadWorkspaceRegisterData(); - }, []); + }, [getWorkspaceRegisterData]); const copyRef = useRef(); @@ -37,7 +37,7 @@ function CopyStep({ onNextButtonClick }) { return () => { clipboard.destroy(); }; - }, []); + }, [dispatchToastMessage, t]); return <> diff --git a/client/admin/cloud/WorkspaceLoginSection.js b/client/admin/cloud/WorkspaceLoginSection.js index 7e2bad6cb0dd9..bc98cc544755b 100644 --- a/client/admin/cloud/WorkspaceLoginSection.js +++ b/client/admin/cloud/WorkspaceLoginSection.js @@ -82,7 +82,7 @@ function WorkspaceLoginSection({ }; checkLoginState(); - }, []); + }, [checkUserLoggedIn, dispatchToastMessage, setLoading, setLoggedIn]); return diff --git a/client/admin/customEmoji/AddCustomEmoji.js b/client/admin/customEmoji/AddCustomEmoji.js index f429710763d74..67b9997553eb1 100644 --- a/client/admin/customEmoji/AddCustomEmoji.js +++ b/client/admin/customEmoji/AddCustomEmoji.js @@ -32,7 +32,7 @@ export function AddCustomEmoji({ close, onChange, ...props }) { onChange(); close(); } - }, [name, aliases, emojiFile]); + }, [emojiFile, name, aliases, saveAction, onChange, close]); const clickUpload = useFileInput(setEmojiPreview, 'emoji'); diff --git a/client/admin/customEmoji/CustomEmoji.js b/client/admin/customEmoji/CustomEmoji.js index 84c7d5867ddfb..3effa9b5cc2d2 100644 --- a/client/admin/customEmoji/CustomEmoji.js +++ b/client/admin/customEmoji/CustomEmoji.js @@ -11,7 +11,7 @@ const FilterByText = ({ setFilter, ...props }) => { useEffect(() => { setFilter({ text }); - }, [text]); + }, [setFilter, text]); return e.preventDefault(), [])} display='flex' flexDirection='column' {...props}> } onChange={handleChange} value={text} /> ; @@ -30,7 +30,7 @@ export function CustomEmoji({ const header = useMemo(() => [ {t('Name')}, {t('Aliases')}, - ], [sort]); + ], [onHeaderClick, sort, t]); const renderRow = (emojis) => { const { _id, name, aliases } = emojis; diff --git a/client/admin/customEmoji/CustomEmojiRoute.js b/client/admin/customEmoji/CustomEmojiRoute.js index fa80981c77c10..24a51f54a0fa1 100644 --- a/client/admin/customEmoji/CustomEmojiRoute.js +++ b/client/admin/customEmoji/CustomEmojiRoute.js @@ -15,12 +15,13 @@ import VerticalBar from '../../components/basic/VerticalBar'; const sortDir = (sortDir) => (sortDir === 'asc' ? 1 : -1); -export const useQuery = (params, sort, cache) => useMemo(() => ({ - query: JSON.stringify({ name: { $regex: params.text || '', $options: 'i' } }), - sort: JSON.stringify({ [sort[0]]: sortDir(sort[1]) }), - ...params.itemsPerPage && { count: params.itemsPerPage }, - ...params.current && { offset: params.current }, -}), [JSON.stringify(params), JSON.stringify(sort), cache]); +export const useQuery = ({ text, itemsPerPage, current }, [column, direction], cache) => useMemo(() => ({ + query: JSON.stringify({ name: { $regex: text || '', $options: 'i' } }), + sort: JSON.stringify({ [column]: sortDir(direction) }), + ...itemsPerPage && { count: itemsPerPage }, + ...current && { offset: current }, +// TODO: remove cache. Is necessary for data invalidation +}), [text, itemsPerPage, current, column, direction, cache]); export default function CustomEmojiRoute({ props }) { const t = useTranslation(); diff --git a/client/admin/customEmoji/EditCustomEmoji.js b/client/admin/customEmoji/EditCustomEmoji.js index e56af8ce2db79..5e37344463cb8 100644 --- a/client/admin/customEmoji/EditCustomEmoji.js +++ b/client/admin/customEmoji/EditCustomEmoji.js @@ -52,6 +52,7 @@ export function EditCustomEmojiWithData({ _id, cache, onChange, ...props }) { const t = useTranslation(); const query = useMemo(() => ({ query: JSON.stringify({ _id }), + // TODO: remove cache. Is necessary for data invalidation }), [_id, cache]); const { data = { emojis: {} }, state, error } = useEndpointDataExperimental('emoji-custom.list', query); @@ -101,7 +102,7 @@ export function EditCustomEmoji({ close, onChange, data, ...props }) { setNewEmojiPreview(URL.createObjectURL(file)); }, [setEmojiFile]); - const hasUnsavedChanges = useMemo(() => previousName !== name || aliases !== previousAliases.join(', ') || !!emojiFile, [name, aliases, emojiFile]); + const hasUnsavedChanges = useMemo(() => previousName !== name || aliases !== previousAliases.join(', ') || !!emojiFile, [previousName, name, aliases, previousAliases, emojiFile]); const saveAction = useEndpointUpload('emoji-custom.update', {}, t('Custom_Emoji_Updated_Successfully')); @@ -115,7 +116,7 @@ export function EditCustomEmoji({ close, onChange, data, ...props }) { if (result.success) { onChange(); } - }, [name, _id, aliases, emojiFile]); + }, [emojiFile, _id, name, aliases, saveAction, onChange]); const deleteAction = useEndpointAction('POST', 'emoji-custom.delete', useMemo(() => ({ emojiId: _id }), [_id])); @@ -124,11 +125,11 @@ export function EditCustomEmoji({ close, onChange, data, ...props }) { if (result.success) { setModal(() => { setModal(undefined); close(); onChange(); }}/>); } - }, [_id]); + }, [close, deleteAction, onChange]); const openConfirmDelete = useCallback(() => setModal(() => setModal(undefined)}/>), [onDeleteConfirm, setModal]); - const handleAliasesChange = useCallback((e) => setAliases(e.currentTarget.value, [])); + const handleAliasesChange = useCallback((e) => setAliases(e.currentTarget.value), [setAliases]); const clickUpload = useFileInput(setEmojiPreview, 'emoji'); diff --git a/client/admin/customSounds/AddCustomSound.js b/client/admin/customSounds/AddCustomSound.js index 15be04aceb6e8..7ced1e33aa33d 100644 --- a/client/admin/customSounds/AddCustomSound.js +++ b/client/admin/customSounds/AddCustomSound.js @@ -19,15 +19,15 @@ export function AddCustomSound({ goToNew, close, onChange, ...props }) { const insertOrUpdateSound = useMethod('insertOrUpdateSound'); - const handleChangeFile = (soundFile) => { + const handleChangeFile = useCallback((soundFile) => { setSound(soundFile); - }; + }, []); const clickUpload = useFileInput(handleChangeFile, 'audio/mp3'); - const saveAction = async (name, soundFile) => { + const saveAction = useCallback(async (name, soundFile) => { const soundData = createSoundData(soundFile, name); - const validation = validate(soundData, sound); + const validation = validate(soundData, soundFile); if (validation.length === 0) { let soundId; try { @@ -56,7 +56,7 @@ export function AddCustomSound({ goToNew, close, onChange, ...props }) { return soundId; } validation.forEach((error) => { throw new Error({ type: 'error', message: t('error-the-field-is-required', { field: t(error) }) }); }); - }; + }, [dispatchToastMessage, insertOrUpdateSound, t, uploadCustomSound]); const handleSave = useCallback(async () => { try { @@ -70,7 +70,7 @@ export function AddCustomSound({ goToNew, close, onChange, ...props }) { } catch (error) { dispatchToastMessage({ type: 'error', message: error }); } - }, [name, sound]); + }, [dispatchToastMessage, goToNew, name, onChange, saveAction, sound, t]); return diff --git a/client/admin/customSounds/AdminSoundsRoute.js b/client/admin/customSounds/AdminSoundsRoute.js index 9a5ad6e8516a8..0497f6a6bd2a9 100644 --- a/client/admin/customSounds/AdminSoundsRoute.js +++ b/client/admin/customSounds/AdminSoundsRoute.js @@ -17,12 +17,13 @@ import NotAuthorizedPage from '../NotAuthorizedPage'; const sortDir = (sortDir) => (sortDir === 'asc' ? 1 : -1); -export const useQuery = (params, sort, cache) => useMemo(() => ({ - query: JSON.stringify({ name: { $regex: params.text || '', $options: 'i' } }), - sort: JSON.stringify({ [sort[0]]: sortDir(sort[1]) }), - ...params.itemsPerPage && { count: params.itemsPerPage }, - ...params.current && { offset: params.current }, -}), [JSON.stringify(params), JSON.stringify(sort), cache]); +export const useQuery = ({ text, itemsPerPage, current }, [column, direction], cache) => useMemo(() => ({ + query: JSON.stringify({ name: { $regex: text || '', $options: 'i' } }), + sort: JSON.stringify({ [column]: sortDir(direction) }), + ...itemsPerPage && { count: itemsPerPage }, + ...current && { offset: current }, + // TODO: remove cache. Is necessary for data invalidation +}), [text, itemsPerPage, current, column, direction, cache]); export default function CustomSoundsRoute({ props }) { const t = useTranslation(); @@ -50,12 +51,12 @@ export default function CustomSoundsRoute({ props }) { const context = useRouteParameter('context'); const id = useRouteParameter('id'); - const onClick = (_id) => () => { + const onClick = useCallback((_id) => () => { router.push({ context: 'edit', id: _id, }); - }; + }, [router]); const onHeaderClick = (id) => { const [sortBy, sortDirection] = sort; @@ -71,18 +72,18 @@ export default function CustomSoundsRoute({ props }) { router.push({ context }); }, [router]); - const close = () => { + const close = useCallback(() => { router.push({}); - }; - - if (!canManageCustomSounds) { - return ; - } + }, [router]); const onChange = useCallback(() => { setCache(new Date()); }, []); + if (!canManageCustomSounds) { + return ; + } + return diff --git a/client/admin/customSounds/EditCustomSound.js b/client/admin/customSounds/EditCustomSound.js index d7938a96a5899..da2551ee23817 100644 --- a/client/admin/customSounds/EditCustomSound.js +++ b/client/admin/customSounds/EditCustomSound.js @@ -56,7 +56,7 @@ export function EditCustomSound({ _id, cache, ...props }) { const { data, state, error } = useEndpointDataExperimental('custom-sounds.list', query); - + console.count('EditCustomSound'); if (state === ENDPOINT_STATES.LOADING) { return @@ -100,13 +100,13 @@ function EditSound({ close, onChange, data, ...props }) { const uploadCustomSound = useMethod('uploadCustomSound'); const insertOrUpdateSound = useMethod('insertOrUpdateSound'); - const handleChangeFile = (soundFile) => { + const handleChangeFile = useCallback((soundFile) => { setSound(soundFile); - }; + }, []); - const hasUnsavedChanges = useMemo(() => previousName !== name || previousSound !== sound, [name, sound]); + const hasUnsavedChanges = useMemo(() => previousName !== name || previousSound !== sound, [name, previousName, previousSound, sound]); - const saveAction = async (sound) => { + const saveAction = useCallback(async (sound) => { const soundData = createSoundData(sound, name, { previousName, previousSound, _id }); const validation = validate(soundData, sound); if (validation.length === 0) { @@ -137,12 +137,12 @@ function EditSound({ close, onChange, data, ...props }) { } validation.forEach((error) => dispatchToastMessage({ type: 'error', message: t('error-the-field-is-required', { field: t(error) }) })); - }; + }, [_id, dispatchToastMessage, insertOrUpdateSound, name, previousName, previousSound, t, uploadCustomSound]); const handleSave = useCallback(async () => { saveAction(sound); onChange(); - }, [name, _id, sound]); + }, [saveAction, sound, onChange]); const onDeleteConfirm = useCallback(async () => { try { @@ -152,14 +152,12 @@ function EditSound({ close, onChange, data, ...props }) { dispatchToastMessage({ type: 'error', message: error }); onChange(); } - }, [_id]); + }, [_id, close, deleteCustomSound, dispatchToastMessage, onChange]); const openConfirmDelete = () => setModal(() => setModal(undefined)}/>); - const clickUpload = useFileInput(handleChangeFile, 'audio/mp3'); - return <> diff --git a/client/admin/customSounds/EditSound.js b/client/admin/customSounds/EditSound.js deleted file mode 100644 index 0496a617fb0e2..0000000000000 --- a/client/admin/customSounds/EditSound.js +++ /dev/null @@ -1,202 +0,0 @@ -import React, { useCallback, useState, useMemo, useEffect } from 'react'; -import { Box, Button, ButtonGroup, Margins, TextInput, Field, Icon, Skeleton, Throbber, InputBox } from '@rocket.chat/fuselage'; - -import { useTranslation } from '../../contexts/TranslationContext'; -import { useMethod } from '../../contexts/ServerContext'; -import { useToastMessageDispatch } from '../../contexts/ToastMessagesContext'; -import { Modal } from '../../components/basic/Modal'; -import { useFileInput } from '../../hooks/useFileInput'; -import { useEndpointDataExperimental, ENDPOINT_STATES } from '../../hooks/useEndpointDataExperimental'; -import { validate, createSoundData } from './lib'; - -const DeleteWarningModal = ({ onDelete, onCancel, ...props }) => { - const t = useTranslation(); - return - - - {t('Are_you_sure')} - - - - {t('Custom_Sound_Status_Delete_Warning')} - - - - - - - - ; -}; - -const SuccessModal = ({ onClose, ...props }) => { - const t = useTranslation(); - return - - - {t('Deleted')} - - - - {t('Custom_Sound_Has_Been_Deleted')} - - - - - - - ; -}; - -export function EditSound({ _id, cache, ...props }) { - const t = useTranslation(); - const query = useMemo(() => ({ - query: JSON.stringify({ _id }), - }), [_id]); - - const { data, state, error } = useEndpointDataExperimental('custom-sounds.list', query); - - - if (state === ENDPOINT_STATES.LOADING) { - return - - - - - - - - - - - - ; - } - - if (error || !data || data.sounds.length < 1) { - return {t('Custom_User_Status_Error_Invalid_User_Status')}; - } - - return ; -} - -export function EditCustomSound({ close, onChange, data, ...props }) { - const t = useTranslation(); - const dispatchToastMessage = useToastMessageDispatch(); - - const { _id, name: previousName } = data || {}; - const previousSound = data || {}; - - const [name, setName] = useState(''); - const [sound, setSound] = useState(); - const [modal, setModal] = useState(); - - useEffect(() => { - setName(previousName || ''); - setSound(previousSound || ''); - }, [previousName, previousSound, _id]); - - const deleteCustomSound = useMethod('deleteCustomSound'); - const uploadCustomSound = useMethod('uploadCustomSound'); - const insertOrUpdateSound = useMethod('insertOrUpdateSound'); - - const handleChangeFile = (soundFile) => { - setSound(soundFile); - }; - - const hasUnsavedChanges = useMemo(() => previousName !== name || previousSound !== sound, [name, sound]); - - const saveAction = async (sound) => { - const soundData = createSoundData(sound, name, { previousName, previousSound, _id }); - const validation = validate(soundData, sound); - if (validation.length === 0) { - let soundId; - try { - soundId = await insertOrUpdateSound(soundData); - } catch (error) { - dispatchToastMessage({ type: 'error', message: error }); - } - - soundData._id = soundId; - soundData.random = Math.round(Math.random() * 1000); - - if (sound && sound !== previousSound) { - dispatchToastMessage({ type: 'success', message: t('Uploading_file') }); - - const reader = new FileReader(); - reader.readAsBinaryString(sound); - reader.onloadend = () => { - try { - uploadCustomSound(reader.result, sound.type, soundData); - return dispatchToastMessage({ type: 'success', message: t('File_uploaded') }); - } catch (error) { - dispatchToastMessage({ type: 'error', message: error }); - } - }; - } - } - - validation.forEach((error) => dispatchToastMessage({ type: 'error', message: t('error-the-field-is-required', { field: t(error) }) })); - }; - - const handleSave = useCallback(async () => { - saveAction(sound); - onChange(); - }, [name, _id, sound]); - - const onDeleteConfirm = useCallback(async () => { - try { - await deleteCustomSound(_id); - setModal(() => { setModal(undefined); close(); onChange(); }}/>); - } catch (error) { - dispatchToastMessage({ type: 'error', message: error }); - onChange(); - } - }, [_id]); - - const openConfirmDelete = () => setModal(() => setModal(undefined)}/>); - - - const clickUpload = useFileInput(handleChangeFile, 'audio/mp3'); - - - return <> - - - - {t('Name')} - - setName(e.currentTarget.value)} placeholder={t('Name')} /> - - - - - {t('Sound_File_mp3')} - - - - {(sound && sound.name) || 'none'} - - - - - - - - - - - - - - - - - - - - - - { modal } - ; -} diff --git a/client/admin/customSounds/NewSound.js b/client/admin/customSounds/NewSound.js deleted file mode 100644 index b77671f1b07a7..0000000000000 --- a/client/admin/customSounds/NewSound.js +++ /dev/null @@ -1,101 +0,0 @@ -import React, { useState, useCallback } from 'react'; -import { Field, TextInput, Box, Icon, Margins, Button, ButtonGroup } from '@rocket.chat/fuselage'; - -import { useToastMessageDispatch } from '../../contexts/ToastMessagesContext'; -import { useTranslation } from '../../contexts/TranslationContext'; -import { useMethod } from '../../contexts/ServerContext'; -import { useFileInput } from '../../hooks/useFileInput'; -import { validate, createSoundData } from './lib'; - -export function NewSound({ goToNew, close, onChange, ...props }) { - const t = useTranslation(); - const dispatchToastMessage = useToastMessageDispatch(); - - const [name, setName] = useState(''); - const [sound, setSound] = useState(); - - const uploadCustomSound = useMethod('uploadCustomSound'); - - const insertOrUpdateSound = useMethod('insertOrUpdateSound'); - - const handleChangeFile = (soundFile) => { - setSound(soundFile); - }; - - const clickUpload = useFileInput(handleChangeFile, 'audio/mp3'); - - const saveAction = async (name, soundFile) => { - const soundData = createSoundData(soundFile, name); - const validation = validate(soundData, sound); - if (validation.length === 0) { - let soundId; - try { - soundId = await insertOrUpdateSound(soundData); - } catch (error) { - dispatchToastMessage({ type: 'error', message: error }); - } - - soundData._id = soundId; - soundData.random = Math.round(Math.random() * 1000); - - if (soundId) { - dispatchToastMessage({ type: 'success', message: t('Uploading_file') }); - - const reader = new FileReader(); - reader.readAsBinaryString(soundFile); - reader.onloadend = () => { - try { - uploadCustomSound(reader.result, soundFile.type, soundData); - dispatchToastMessage({ type: 'success', message: t('File_uploaded') }); - } catch (error) { - dispatchToastMessage({ type: 'error', message: error }); - } - }; - } - return soundId; - } - validation.forEach((error) => { throw new Error({ type: 'error', message: t('error-the-field-is-required', { field: t(error) }) }); }); - }; - - const handleSave = useCallback(async () => { - try { - const result = await saveAction( - name, - sound, - ); - dispatchToastMessage({ type: 'success', message: t('Custom_Sound_Updated_Successfully') }); - goToNew(result)(); - onChange(); - } catch (error) { - dispatchToastMessage({ type: 'error', message: error }); - } - }, [name, sound]); - - return - - - {t('Name')} - - setName(e.currentTarget.value)} placeholder={t('Name')} /> - - - - {t('Sound_File_mp3')} - - - - {(sound && sound.name) || 'none'} - - - - - - - - - - - - - ; -} diff --git a/client/admin/customUserStatus/AddCustomUserStatus.js b/client/admin/customUserStatus/AddCustomUserStatus.js index 1597e90ef00c6..573d50d4c82d4 100644 --- a/client/admin/customUserStatus/AddCustomUserStatus.js +++ b/client/admin/customUserStatus/AddCustomUserStatus.js @@ -26,7 +26,7 @@ export function AddCustomUserStatus({ goToNew, close, onChange, ...props }) { } catch (error) { dispatchToastMessage({ type: 'error', message: error }); } - }, [name, statusType]); + }, [dispatchToastMessage, goToNew, name, onChange, saveStatus, statusType, t]); const presenceOptions = [ ['online', t('Online')], diff --git a/client/admin/customUserStatus/CustomUserStatus.js b/client/admin/customUserStatus/CustomUserStatus.js index bd63cc6d5aaa4..743a880490d79 100644 --- a/client/admin/customUserStatus/CustomUserStatus.js +++ b/client/admin/customUserStatus/CustomUserStatus.js @@ -13,7 +13,7 @@ const FilterByText = ({ setFilter, ...props }) => { useEffect(() => { setFilter({ text }); - }, [text]); + }, [setFilter, text]); return e.preventDefault(), [])} display='flex' flexDirection='column' {...props}> } onChange={handleChange} value={text} /> ; @@ -32,7 +32,7 @@ export function CustomUserStatus({ const header = useMemo(() => [ {t('Name')}, {t('Presence')}, - ].filter(Boolean), [sort]); + ].filter(Boolean), [onHeaderClick, sort, t]); const renderRow = (status) => { const { _id, name, statusType } = status; diff --git a/client/admin/customUserStatus/CustomUserStatusRoute.js b/client/admin/customUserStatus/CustomUserStatusRoute.js index c5e5aef163ec3..1862d3336b510 100644 --- a/client/admin/customUserStatus/CustomUserStatusRoute.js +++ b/client/admin/customUserStatus/CustomUserStatusRoute.js @@ -15,12 +15,13 @@ import NotAuthorizedPage from '../NotAuthorizedPage'; const sortDir = (sortDir) => (sortDir === 'asc' ? 1 : -1); -export const useQuery = (params, sort, cache) => useMemo(() => ({ - query: JSON.stringify({ name: { $regex: params.text || '', $options: 'i' } }), - sort: JSON.stringify({ [sort[0]]: sortDir(sort[1]) }), - ...params.itemsPerPage && { count: params.itemsPerPage }, - ...params.current && { offset: params.current }, -}), [JSON.stringify(params), JSON.stringify(sort), cache]); +export const useQuery = ({ text, itemsPerPage, current }, [column, direction], cache) => useMemo(() => ({ + query: JSON.stringify({ name: { $regex: text || '', $options: 'i' } }), + sort: JSON.stringify({ [column]: sortDir(direction) }), + ...itemsPerPage && { count: itemsPerPage }, + ...current && { offset: current }, + // TODO: remove cache. Is necessary for data invalidation +}), [text, itemsPerPage, current, column, direction, cache]); export default function CustomUserStatusRoute({ props }) { const t = useTranslation(); @@ -68,18 +69,18 @@ export default function CustomUserStatusRoute({ props }) { router.push({ context }); }, [router]); - const close = () => { + const close = useCallback(() => { router.push({}); - }; - - if (!canManageUserStatus) { - return ; - } + }, [router]); const onChange = useCallback(() => { setCache(new Date()); }, []); + if (!canManageUserStatus) { + return ; + } + return diff --git a/client/admin/customUserStatus/EditCustomUserStatus.js b/client/admin/customUserStatus/EditCustomUserStatus.js index 7a7853d5b1aa7..6b9fda2aa6cca 100644 --- a/client/admin/customUserStatus/EditCustomUserStatus.js +++ b/client/admin/customUserStatus/EditCustomUserStatus.js @@ -51,7 +51,8 @@ export function EditCustomUserStatusWithData({ _id, cache, ...props }) { const t = useTranslation(); const query = useMemo(() => ({ query: JSON.stringify({ _id }), - }), [_id]); + // TODO: remove cache. Is necessary for data invalidation + }), [_id, cache]); const { data, state, error } = useEndpointDataExperimental('custom-user-status.list', query); @@ -96,7 +97,7 @@ export function EditCustomUserStatus({ close, onChange, data, ...props }) { const saveStatus = useMethod('insertOrUpdateUserStatus'); const deleteStatus = useMethod('deleteCustomUserStatus'); - const hasUnsavedChanges = useMemo(() => previousName !== name || previousStatusType !== statusType, [name, statusType]); + const hasUnsavedChanges = useMemo(() => previousName !== name || previousStatusType !== statusType, [name, previousName, previousStatusType, statusType]); const handleSave = useCallback(async () => { try { await saveStatus({ @@ -111,7 +112,7 @@ export function EditCustomUserStatus({ close, onChange, data, ...props }) { } catch (error) { dispatchToastMessage({ type: 'error', message: error }); } - }, [name, statusType, _id]); + }, [saveStatus, _id, previousName, previousStatusType, name, statusType, dispatchToastMessage, t, onChange]); const onDeleteConfirm = useCallback(async () => { try { @@ -121,7 +122,7 @@ export function EditCustomUserStatus({ close, onChange, data, ...props }) { dispatchToastMessage({ type: 'error', message: error }); onChange(); } - }, [_id]); + }, [_id, close, deleteStatus, dispatchToastMessage, onChange]); const openConfirmDelete = () => setModal(() => setModal(undefined)}/>); diff --git a/client/admin/import/ImportHistoryPage.js b/client/admin/import/ImportHistoryPage.js index 4474d2b7cc1f8..8414c23ba34a5 100644 --- a/client/admin/import/ImportHistoryPage.js +++ b/client/admin/import/ImportHistoryPage.js @@ -47,10 +47,10 @@ function ImportHistoryPage() { }; loadData(); - }, []); + }, [dispatchToastMessage, getCurrentImportOperation, getLatestImportOperations, setCurrentOperation, setLatestOperations, setLoading, t]); const hasAnySuccessfulSlackImport = useMemo(() => - latestOperations?.some(({ importerKey, status }) => importerKey === 'slack' && status === ProgressStep.DONE), [latestOperations]); + latestOperations?.some(({ importerKey, status }) => importerKey === 'slack' && status === ProgressStep.DONE), [latestOperations?.some]); const handleNewImportClick = () => { newImportRoute.push(); diff --git a/client/admin/import/ImportProgressPage.js b/client/admin/import/ImportProgressPage.js index c06c034c46c99..ed5cbd7774fef 100644 --- a/client/admin/import/ImportProgressPage.js +++ b/client/admin/import/ImportProgressPage.js @@ -53,7 +53,7 @@ function ImportProgressPage() { }; loadCurrentOperation(); - }, []); + }, [getCurrentImportOperation, handleError, importHistoryRoute, prepareImportRoute, setCompleted, setImporterKey, setTotal, t]); useEffect(() => { if (!importerKey) { @@ -110,7 +110,7 @@ function ImportProgressPage() { return () => { streamer.removeListener('progress', handleProgressUpdated); }; - }, [importerKey]); + }, [dispatchToastMessage, getImportProgress, handleError, importHistoryRoute, importerKey, prepareImportRoute, setCompleted, setStep, setTotal, t]); const progressRate = useMemo(() => { if (total === 0) { @@ -118,7 +118,7 @@ function ImportProgressPage() { } return completed / total * 100; - }); + }, [completed, total]); return diff --git a/client/admin/import/NewImportPage.js b/client/admin/import/NewImportPage.js index 637201a159b0b..4e4e7d77eff52 100644 --- a/client/admin/import/NewImportPage.js +++ b/client/admin/import/NewImportPage.js @@ -49,7 +49,7 @@ function NewImportPage() { if (importerKey && !importer) { newImportRoute.replace(); } - }, [importerKey, !importer]); + }, [importer, importerKey, newImportRoute]); const formatMemorySize = useFormatMemorySize(); diff --git a/client/admin/import/PrepareImportPage.js b/client/admin/import/PrepareImportPage.js index 687e78fde25e1..298bb40278240 100644 --- a/client/admin/import/PrepareImportPage.js +++ b/client/admin/import/PrepareImportPage.js @@ -50,8 +50,8 @@ function PrepareUsers({ usersCount, users, setUsers }) { const t = useTranslation(); const [current, setCurrent] = useState(0); const [itemsPerPage, setItemsPerPage] = useState(25); - const showingResultsLabel = useCallback(({ count, current, itemsPerPage }) => t('Showing results %s - %s of %s', current + 1, Math.min(current + itemsPerPage, count), count), []); - const itemsPerPageLabel = useCallback(() => t('Items_per_page:'), []); + const showingResultsLabel = useCallback(({ count, current, itemsPerPage }) => t('Showing results %s - %s of %s', current + 1, Math.min(current + itemsPerPage, count), count), [t]); + const itemsPerPageLabel = useCallback(() => t('Items_per_page:'), [t]); return <> @@ -118,8 +118,8 @@ function PrepareChannels({ channels, channelsCount, setChannels }) { const t = useTranslation(); const [current, setCurrent] = useState(0); const [itemsPerPage, setItemsPerPage] = useState(25); - const showingResultsLabel = useCallback(({ count, current, itemsPerPage }) => t('Showing results %s - %s of %s', current + 1, Math.min(current + itemsPerPage, count), count), []); - const itemsPerPageLabel = useCallback(() => t('Items_per_page:'), []); + const showingResultsLabel = useCallback(({ count, current, itemsPerPage }) => t('Showing results %s - %s of %s', current + 1, Math.min(current + itemsPerPage, count), count), [t]); + const itemsPerPageLabel = useCallback(() => t('Items_per_page:'), [t]); return channels.length && <>
@@ -213,7 +213,7 @@ function PrepareImportPage() { return () => { streamer.removeListener('progress', handleProgressUpdated); }; - }, []); + }, [setProgressRate]); useEffect(() => { const loadImportFileData = async () => { @@ -286,7 +286,7 @@ function PrepareImportPage() { }; loadCurrentOperation(); - }, []); + }, [getCurrentImportOperation, getImportFileData, handleError, importHistoryRoute, importProgressRoute, newImportRoute, setMessageCount, setPreparing, setProgressRate, setStatus, t]); const handleBackToImportsButtonClick = () => { importHistoryRoute.push(); diff --git a/client/admin/info/InformationRoute.js b/client/admin/info/InformationRoute.js index 11cacee30aff2..d9393ef9c5194 100644 --- a/client/admin/info/InformationRoute.js +++ b/client/admin/info/InformationRoute.js @@ -50,7 +50,7 @@ export const InformationRoute = React.memo(function InformationRoute() { return () => { didCancel = true; }; - }, [canViewStatistics]); + }, [canViewStatistics, getInstances, getStatistics]); const info = useServerInformation(); diff --git a/client/admin/integrations/IncomingWebhookForm.js b/client/admin/integrations/IncomingWebhookForm.js index 4bf2e20a1f9da..cf07e910126ba 100644 --- a/client/admin/integrations/IncomingWebhookForm.js +++ b/client/admin/integrations/IncomingWebhookForm.js @@ -3,15 +3,13 @@ import { Field, TextInput, Box, ToggleSwitch, Icon, TextAreaInput, FieldGroup, M import { useTranslation } from '../../contexts/TranslationContext'; import { useAbsoluteUrl } from '../../contexts/ServerContext'; -import { useHilightCode } from '../../hooks/useHilightCode'; +import { useHighlightedCode } from '../../hooks/useHighlightedCode'; import { useExampleData } from './exampleIncomingData'; import Page from '../../components/basic/Page'; export default function IncomingWebhookForm({ formValues, formHandlers, extraData = {}, append, ...props }) { const t = useTranslation(); - const hilightCode = useHilightCode(); - const absoluteUrl = useAbsoluteUrl(); const { @@ -40,16 +38,18 @@ export default function IncomingWebhookForm({ formValues, formHandlers, extraDat const url = absoluteUrl(`hooks/${ extraData._id }/${ extraData.token }`); + const additionalFields = useMemo(() => ({ + ...alias && { alias }, + ...emoji && { emoji }, + ...avatarUrl && { avatar: avatarUrl }, + }), [alias, avatarUrl, emoji]); + const [exampleData, curlData] = useExampleData({ - aditionalFields: { - ...alias && { alias }, - ...emoji && { emoji }, - ...avatarUrl && { avatar: avatarUrl }, - }, + additionalFields, url, - }, [alias, emoji, avatarUrl]); + }); - const hilightedExampleJson = hilightCode('json', JSON.stringify(exampleData, null, 2)); + const hilightedExampleJson = useHighlightedCode('json', JSON.stringify(exampleData, null, 2)); return e.preventDefault(), [])} qa-admin-user-edit='form' { ...props }> @@ -59,14 +59,14 @@ export default function IncomingWebhookForm({ formValues, formHandlers, extraDat {t('Enabled')} - , [enabled, handleEnabled])} + , [enabled, handleEnabled, t])} {useMemo(() => {t('Name_optional')} {t('You_should_name_it_to_easily_manage_your_integrations')} - , [name, handleName])} + , [t, name, handleName])} {useMemo(() => {t('Post_to_Channel')} @@ -74,7 +74,7 @@ export default function IncomingWebhookForm({ formValues, formHandlers, extraDat {t('Messages_that_are_sent_to_the_Incoming_WebHook_will_be_posted_here')} - , [channel, handleChannel])} + , [channel, handleChannel, t])} {useMemo(() => {t('Post_as')} @@ -82,14 +82,14 @@ export default function IncomingWebhookForm({ formValues, formHandlers, extraDat {t('Choose_the_username_that_this_integration_will_post_as')} {t('Should_exists_a_user_with_this_username')} - , [username, handleUsername])} + , [t, username, handleUsername])} {useMemo(() => {`${ t('Alias') } (${ t('optional') })`} }/> {t('Choose_the_alias_that_will_appear_before_the_username_in_messages')} - , [alias, handleAlias])} + , [alias, handleAlias, t])} {useMemo(() => {`${ t('Avatar_URL') } (${ t('optional') })`} @@ -97,7 +97,7 @@ export default function IncomingWebhookForm({ formValues, formHandlers, extraDat {t('You_can_change_a_different_avatar_too')} {t('Should_be_a_URL_of_an_image')} - , [avatarUrl, handleAvatarUrl])} + , [avatarUrl, handleAvatarUrl, t])} {useMemo(() => {`${ t('Emoji') } (${ t('optional') })`} @@ -105,19 +105,19 @@ export default function IncomingWebhookForm({ formValues, formHandlers, extraDat {t('You_can_use_an_emoji_as_avatar')} - , [emoji, handleEmoji])} + , [emoji, handleEmoji, t])} {useMemo(() => {t('Script_Enabled')} - , [scriptEnabled, handleScriptEnabled])} + , [t, scriptEnabled, handleScriptEnabled])} {useMemo(() => {t('Script')} }/> - , [script, handleScript])} + , [t, script, handleScript])} {useMemo(() => !extraData._id && <> {t('Webhook_URL')} @@ -130,7 +130,7 @@ export default function IncomingWebhookForm({ formValues, formHandlers, extraDat } disabled/> - , [extraData._id])} + , [extraData._id, t])} {useMemo(() => extraData._id && <> {t('Webhook_URL')} @@ -143,7 +143,7 @@ export default function IncomingWebhookForm({ formValues, formHandlers, extraDat }/> - , [url, extraData._id, extraData.token])} + , [extraData._id, extraData.token, t, url])} {useMemo(() => {t('Example_payload')} @@ -151,13 +151,13 @@ export default function IncomingWebhookForm({ formValues, formHandlers, extraDat
-
, [hilightedExampleJson])} + , [hilightedExampleJson, t])} {useMemo(() => extraData._id && {t('Curl')} }/> - , [curlData])} + , [curlData, extraData._id, t])} { append }
diff --git a/client/admin/integrations/IntegrationsPage.js b/client/admin/integrations/IntegrationsPage.js index ec9dc1adb6882..b980da09c21e5 100644 --- a/client/admin/integrations/IntegrationsPage.js +++ b/client/admin/integrations/IntegrationsPage.js @@ -15,21 +15,21 @@ function IntegrationsPage() { const handleNewButtonClick = useCallback(() => { router.push({ context: 'new', type: 'incoming' }); - }, []); + }, [router]); const context = useRouteParameter('context'); useEffect(() => { if (!context) { router.push({ context: 'webhook-incoming' }); } - }, [context]); + }, [context, router]); const showTable = !['zapier', 'bots'].includes(context); - const goToIncoming = useCallback(() => router.push({ context: 'webhook-incoming' }), []); - const goToOutgoing = useCallback(() => router.push({ context: 'webhook-outgoing' }), []); - const goToZapier = useCallback(() => router.push({ context: 'zapier' }), []); - const goToBots = useCallback(() => router.push({ context: 'bots' }), []); + const goToIncoming = useCallback(() => router.push({ context: 'webhook-incoming' }), [router]); + const goToOutgoing = useCallback(() => router.push({ context: 'webhook-outgoing' }), [router]); + const goToZapier = useCallback(() => router.push({ context: 'zapier' }), [router]); + const goToBots = useCallback(() => router.push({ context: 'bots' }), [router]); return diff --git a/client/admin/integrations/IntegrationsTable.js b/client/admin/integrations/IntegrationsTable.js index ba64ecf399302..f362ffabbb1c9 100644 --- a/client/admin/integrations/IntegrationsTable.js +++ b/client/admin/integrations/IntegrationsTable.js @@ -19,24 +19,24 @@ const FilterByTypeAndText = React.memo(({ setFilter, ...props }) => { useEffect(() => { setFilter({ text }); - }, [text]); + }, [setFilter, text]); return e.preventDefault(), [])} display='flex' flexDirection='column' {...props}> } onChange={handleChange} value={text} /> ; }); -const useQuery = (params, sort) => useMemo(() => ({ - query: JSON.stringify({ name: { $regex: params.text || '', $options: 'i' }, type: params.type }), - sort: JSON.stringify({ [sort[0]]: sort[1] === 'asc' ? 1 : -1 }), - ...params.itemsPerPage && { count: params.itemsPerPage }, - ...params.current && { offset: params.current }, -}), [JSON.stringify(params), JSON.stringify(sort)]); +const useQuery = ({ text, type, itemsPerPage, current }, [column, direction]) => useMemo(() => ({ + query: JSON.stringify({ name: { $regex: text || '', $options: 'i' }, type }), + sort: JSON.stringify({ [column]: direction === 'asc' ? 1 : -1 }), + ...itemsPerPage && { count: itemsPerPage }, + ...current && { offset: current }, +}), [column, current, direction, itemsPerPage, text, type]); const useResizeInlineBreakpoint = (sizes = [], debounceDelay = 0) => { const { ref, borderBoxSize } = useResizeObserver({ debounceDelay }); const inlineSize = borderBoxSize ? borderBoxSize.inlineSize : 0; - sizes = useMemo(() => sizes.map((current) => (inlineSize ? inlineSize > current : true)), [inlineSize]); + sizes = useMemo(() => sizes.map((current) => (inlineSize ? inlineSize > current : true)), [inlineSize, sizes]); return [ref, ...sizes]; }; @@ -56,11 +56,11 @@ export function IntegrationsTable({ type }) { const router = useRoute('admin-integrations'); - const onClick = (_id, type) => () => router.push({ + const onClick = useCallback((_id, type) => () => router.push({ context: 'edit', type: type === 'webhook-incoming' ? 'incoming' : 'outgoing', id: _id, - }); + }), [router]); const onHeaderClick = useCallback((id) => { const [sortBy, sortDirection] = sort; @@ -78,10 +78,10 @@ export function IntegrationsTable({ type }) {
, isBig && , , - ].filter(Boolean), [sort, isBig]); + ].filter(Boolean), [sort, onHeaderClick, isBig, t]); - const renderRow = useCallback(({ name, _id, type, username, _createdAt, _createdBy: { username: createdBy }, channel = [] }) => { - const handler = useMemo(() => onClick(_id, type), []); + const renderRow = useCallback(function IntegrationRow({ name, _id, type, username, _createdAt, _createdBy: { username: createdBy }, channel = [] }) { + const handler = useMemo(() => onClick(_id, type), [_id, type]); return {name} {channel.join(', ')} @@ -89,7 +89,7 @@ export function IntegrationsTable({ type }) { {isBig && {formatDateAndTime(_createdAt)}} {username} ; - }, []); + }, [formatDateAndTime, isBig, onClick]); return ; } diff --git a/client/admin/integrations/OutgoiongWebhookForm.js b/client/admin/integrations/OutgoiongWebhookForm.js index 7435dbb6fbc4c..f86d9ac4c67f4 100644 --- a/client/admin/integrations/OutgoiongWebhookForm.js +++ b/client/admin/integrations/OutgoiongWebhookForm.js @@ -12,12 +12,13 @@ import { } from '@rocket.chat/fuselage'; import React, { useMemo, useCallback } from 'react'; -import { useHilightCode } from '../../hooks/useHilightCode'; +import { useHighlightedCode } from '../../hooks/useHighlightedCode'; import { useExampleData } from './exampleIncomingData'; import { useTranslation } from '../../contexts/TranslationContext'; import Page from '../../components/basic/Page'; import { integrations as eventList } from '../../../app/integrations/lib/rocketchat'; +const { outgoingEvents } = eventList; export default function OutgoingWebhookForm({ formValues, formHandlers, append, ...props }) { const t = useTranslation(); @@ -72,28 +73,26 @@ export default function OutgoingWebhookForm({ formValues, formHandlers, append, ['powers-of-ten', t('powers-of-ten')], ['powers-of-two', t('powers-of-two')], ['increments-of-two', t('increments-of-two')], - ], []); + ], [t]); - const { outgoingEvents } = eventList; - - const eventOptions = useMemo(() => Object.entries(outgoingEvents).map(([key, val]) => [key, t(val.label)]), []); - - const hilightCode = useHilightCode(); + const eventOptions = useMemo(() => Object.entries(outgoingEvents).map(([key, val]) => [key, t(val.label)]), [t]); const showChannel = useMemo(() => outgoingEvents[event].use.channel, [event]); const showTriggerWords = useMemo(() => outgoingEvents[event].use.triggerWords, [event]); const showTargetRoom = useMemo(() => outgoingEvents[event].use.targetRoom, [event]); + const additionalFields = useMemo(() => ({ + ...alias && { alias }, + ...emoji && { emoji }, + ...avatarUrl && { avatar: avatarUrl }, + }), [alias, avatarUrl, emoji]); + const [exampleData] = useExampleData({ - aditionalFields: { - ...alias && { alias }, - ...emoji && { emoji }, - ...avatarUrl && { avatar: avatarUrl }, - }, + additionalFields, url: null, - }, [alias, emoji, avatarUrl]); + }); - const hilightedExampleJson = hilightCode('json', JSON.stringify(exampleData, null, 2)); + const hilightedExampleJson = useHighlightedCode('json', JSON.stringify(exampleData, null, 2)); return e.preventDefault(), [])} qa-admin-user-edit='form' { ...props }> @@ -105,20 +104,20 @@ export default function OutgoingWebhookForm({ formValues, formHandlers, append, - , [retryDelay])} + , [handleRetryDelay, retryDelay, retryDelayOptions, t])} { useMemo(() => event === 'sendMessage' && @@ -249,12 +248,12 @@ export default function OutgoingWebhookForm({ formValues, formHandlers, append, - {t('Integration_Word_Trigger_Placement')} + {t('Integration_Run_When_Message_Is_Edited')} {t('Integration_Run_When_Message_Is_Edited_Description')} - , [triggerWordAnywhere, runOnEdits])} + , [event, t, triggerWordAnywhere, handleTriggerWordAnywhere, runOnEdits, handleRunOnEdits])} { append } diff --git a/client/admin/integrations/edit/EditIncomingWebhook.js b/client/admin/integrations/edit/EditIncomingWebhook.js index 7fa24155c2992..48852377eb2bb 100644 --- a/client/admin/integrations/edit/EditIncomingWebhook.js +++ b/client/admin/integrations/edit/EditIncomingWebhook.js @@ -1,4 +1,4 @@ -import React, { useMemo, useState } from 'react'; +import React, { useMemo, useState, useCallback } from 'react'; import { Field, Box, Skeleton, Margins, Button } from '@rocket.chat/fuselage'; import { SuccessModal, DeleteWarningModal } from './EditIntegrationsPage'; @@ -15,6 +15,7 @@ export default function EditIncomingWebhookWithData({ integrationId, ...props }) const t = useTranslation(); const [cache, setCache] = useState(); + // TODO: remove cache. Is necessary for data validation const { data, state, error } = useEndpointDataExperimental('integrations.get', useMemo(() => ({ integrationId }), [integrationId, cache])); const onChange = () => setCache(new Date()); @@ -65,7 +66,7 @@ function EditIncomingWebhook({ data, onChange, ...props }) { const router = useRoute('admin-integrations'); - const handleDeleteIntegration = () => { + const handleDeleteIntegration = useCallback(() => { const closeModal = () => setModal(); const onDelete = async () => { const result = await deleteIntegration(); @@ -73,9 +74,9 @@ function EditIncomingWebhook({ data, onChange, ...props }) { }; setModal(); - }; + }, [deleteIntegration, router]); - const handleSave = async () => { + const handleSave = useCallback(async () => { try { await saveIntegration(data._id, { ...formValues }); dispatchToastMessage({ type: 'success', message: t('Integration_updated') }); @@ -83,7 +84,7 @@ function EditIncomingWebhook({ data, onChange, ...props }) { } catch (e) { dispatchToastMessage({ type: 'error', message: e }); } - }; + }, [data._id, dispatchToastMessage, formValues, onChange, saveIntegration, t]); const actionButtons = useMemo(() => @@ -95,7 +96,7 @@ function EditIncomingWebhook({ data, onChange, ...props }) { - ); + , [handleDeleteIntegration, handleSave, reset, t]); return <> diff --git a/client/admin/integrations/edit/EditIntegrationsPage.js b/client/admin/integrations/edit/EditIntegrationsPage.js index 3ffdb8168c794..bc45da6e2e0ee 100644 --- a/client/admin/integrations/edit/EditIntegrationsPage.js +++ b/client/admin/integrations/edit/EditIntegrationsPage.js @@ -57,11 +57,11 @@ export default function NewIntegrationsPage({ ...props }) { const handleClickReturn = useCallback(() => { router.push({ }); - }, []); + }, [router]); const handleClickHistory = useCallback(() => { router.push({ context: 'history', type: 'outgoing', id: integrationId }); - }, [integrationId]); + }, [integrationId, router]); return diff --git a/client/admin/integrations/edit/EditOutgoingWebhook.js b/client/admin/integrations/edit/EditOutgoingWebhook.js index 22d10fcd58abf..cf086a1af3782 100644 --- a/client/admin/integrations/edit/EditOutgoingWebhook.js +++ b/client/admin/integrations/edit/EditOutgoingWebhook.js @@ -1,4 +1,4 @@ -import React, { useMemo, useState } from 'react'; +import React, { useMemo, useState, useCallback } from 'react'; import { Field, Box, @@ -21,6 +21,7 @@ export default function EditOutgoingWebhookWithData({ integrationId, ...props }) const t = useTranslation(); const [cache, setCache] = useState(); + // TODO: remove cache. Is necessary for data validation const { data, state, error } = useEndpointDataExperimental('integrations.get', useMemo(() => ({ integrationId }), [integrationId, cache])); const onChange = () => setCache(new Date()); @@ -83,7 +84,7 @@ function EditOutgoingWebhook({ data, onChange, setSaveAction, ...props }) { const deleteQuery = useMemo(() => ({ type: 'webhook-outgoing', integrationId: data._id }), [data._id]); const deleteIntegration = useEndpointAction('POST', 'integrations.remove', deleteQuery); - const handleDeleteIntegration = () => { + const handleDeleteIntegration = useCallback(() => { const closeModal = () => setModal(); const onDelete = async () => { const result = await deleteIntegration(); @@ -91,14 +92,14 @@ function EditOutgoingWebhook({ data, onChange, setSaveAction, ...props }) { }; setModal(); - }; + }, [deleteIntegration, router]); const { urls, triggerWords, } = formValues; - const handleSave = async () => { + const handleSave = useCallback(async () => { try { await saveIntegration(data._id, { ...formValues, @@ -111,7 +112,7 @@ function EditOutgoingWebhook({ data, onChange, setSaveAction, ...props }) { } catch (e) { dispatchToastMessage({ type: 'error', message: e }); } - }; + }, [data._id, dispatchToastMessage, formValues, onChange, saveIntegration, t, triggerWords, urls]); const actionButtons = useMemo(() => @@ -123,7 +124,7 @@ function EditOutgoingWebhook({ data, onChange, setSaveAction, ...props }) { - ); + , [handleDeleteIntegration, handleSave, reset, t]); return <> diff --git a/client/admin/integrations/edit/OutgoingWebhookHistoryPage.js b/client/admin/integrations/edit/OutgoingWebhookHistoryPage.js index 63752ce098d8b..13af690e5ce83 100644 --- a/client/admin/integrations/edit/OutgoingWebhookHistoryPage.js +++ b/client/admin/integrations/edit/OutgoingWebhookHistoryPage.js @@ -1,9 +1,9 @@ import { Button, ButtonGroup, Icon, Skeleton, Box, Accordion, Field, FieldGroup, Pagination } from '@rocket.chat/fuselage'; -import React, { useMemo, useCallback, useState, useEffect } from 'react'; +import React, { useMemo, useCallback, useState } from 'react'; import Page from '../../../components/basic/Page'; import { useTranslation } from '../../../contexts/TranslationContext'; -import { useHilightCode } from '../../../hooks/useHilightCode'; +import { useHighlightedCode } from '../../../hooks/useHighlightedCode'; import { integrations as eventList } from '../../../../app/integrations/lib/rocketchat'; import { useMethod } from '../../../contexts/ServerContext'; import { useEndpointDataExperimental, ENDPOINT_STATES } from '../../../hooks/useEndpointDataExperimental'; @@ -14,8 +14,6 @@ import { useToastMessageDispatch } from '../../../contexts/ToastMessagesContext' function HistoryItem({ data, onChange, ...props }) { const t = useTranslation(); - const hilightCode = useHilightCode(); - const replayOutgoingIntegration = useMethod('replayOutgoingIntegration'); const { @@ -40,10 +38,17 @@ function HistoryItem({ data, onChange, ...props }) { e.stopPropagation(); replayOutgoingIntegration({ integrationId, historyId: _id }); onChange(); - }, [_id]); + }, [_id, integrationId, onChange, replayOutgoingIntegration]); const formatDateAndTime = useFormatDateAndTime(); + const prepareSentMessageCode = useHighlightedCode('json', JSON.stringify(prepareSentMessage || '', null, 2)); + const processSentMessageCode = useHighlightedCode('json', JSON.stringify(processSentMessage || '', null, 2)); + const httpCallDataCode = useHighlightedCode('json', JSON.stringify(httpCallData || '', null, 2)); + const httpErrorCode = useHighlightedCode('json', JSON.stringify(httpError || '', null, 2)); + const httpResultCode = useHighlightedCode('json', JSON.stringify(httpResult || '', null, 2)); + const errorStackCode = useHighlightedCode('json', JSON.stringify(errorStack || '', null, 2)); + return @@ -100,7 +105,7 @@ function HistoryItem({ data, onChange, ...props }) { {t('Integration_Outgoing_WebHook_History_Data_Passed_To_Trigger')} -
+
@@ -108,7 +113,7 @@ function HistoryItem({ data, onChange, ...props }) { {t('Integration_Outgoing_WebHook_History_Messages_Sent_From_Prepare_Script')} -
+
} @@ -116,7 +121,7 @@ function HistoryItem({ data, onChange, ...props }) { {t('Integration_Outgoing_WebHook_History_Messages_Sent_From_Process_Script')} -
+
} @@ -132,7 +137,7 @@ function HistoryItem({ data, onChange, ...props }) { {t('Integration_Outgoing_WebHook_History_Data_Passed_To_URL')} -
+
} @@ -140,7 +145,7 @@ function HistoryItem({ data, onChange, ...props }) { {t('Integration_Outgoing_WebHook_History_Http_Response_Error')} -
+
} @@ -148,7 +153,7 @@ function HistoryItem({ data, onChange, ...props }) { {t('Integration_Outgoing_WebHook_History_Http_Response')} -
+
} @@ -156,7 +161,7 @@ function HistoryItem({ data, onChange, ...props }) { {t('Integration_Outgoing_WebHook_History_Error_Stacktrace')} -
+
} @@ -167,12 +172,7 @@ function HistoryItem({ data, onChange, ...props }) { function HistoryContent({ data, state, onChange, ...props }) { const t = useTranslation(); - const [loadedData, setLoadedData] = useState(); - useEffect(() => { - if (state === ENDPOINT_STATES.DONE) { setLoadedData(data); } - }, [state]); - - if (!loadedData || state === ENDPOINT_STATES.LOADING) { + if (!data || state === ENDPOINT_STATES.LOADING) { return @@ -183,13 +183,13 @@ function HistoryContent({ data, state, onChange, ...props }) { ; } - if (loadedData.history.length < 1) { + if (data.history.length < 1) { return {t('Integration_Outgoing_WebHook_No_History')}; } return <> - {loadedData.history.map((current) => { setCache(new Date()); - }); + }, []); const router = useRoute('admin-integrations'); @@ -231,13 +231,14 @@ function OutgoingWebhookHistoryPage(props) { const query = useMemo(() => ({ id, - cout: itemsPerPage, + count: itemsPerPage, offset: current, + // TODO: remove cache. Is necessary for data validation }), [id, itemsPerPage, current, cache]); const { data, state } = useEndpointDataExperimental('integrations.history', query); - const showingResultsLabel = useCallback(({ count, current, itemsPerPage }) => t('Showing results %s - %s of %s', current + 1, Math.min(current + itemsPerPage, count), count), []); + const showingResultsLabel = useCallback(({ count, current, itemsPerPage }) => t('Showing results %s - %s of %s', current + 1, Math.min(current + itemsPerPage, count), count), [t]); return diff --git a/client/admin/integrations/exampleIncomingData.js b/client/admin/integrations/exampleIncomingData.js index 0226156f69fcd..8ac603ada9f18 100644 --- a/client/admin/integrations/exampleIncomingData.js +++ b/client/admin/integrations/exampleIncomingData.js @@ -1,20 +1,22 @@ import { useMemo } from 'react'; -export function useExampleData({ aditionalFields, url }, dep) { - const exampleData = { - ...aditionalFields, - text: 'Example message', - attachments: [{ - title: 'Rocket.Chat', - title_link: 'https://rocket.chat', - text: 'Rocket.Chat, the best open source chat', - image_url: '/images/integration-attachment-example.png', - color: '#764FA5', - }], - }; +export function useExampleData({ additionalFields, url }) { + return useMemo(() => { + const exampleData = { + ...additionalFields, + text: 'Example message', + attachments: [{ + title: 'Rocket.Chat', + title_link: 'https://rocket.chat', + text: 'Rocket.Chat, the best open source chat', + image_url: '/images/integration-attachment-example.png', + color: '#764FA5', + }], + }; - return useMemo(() => [ - exampleData, - `curl -X POST -H 'Content-Type: application/json' --data '${ JSON.stringify(exampleData) }' ${ url }`, - ], dep); + return [ + exampleData, + `curl -X POST -H 'Content-Type: application/json' --data '${ JSON.stringify(exampleData) }' ${ url }`, + ]; + }, [additionalFields, url]); } diff --git a/client/admin/integrations/new/NewIncomingWebhook.js b/client/admin/integrations/new/NewIncomingWebhook.js index 99cdbc628276f..6aac3bca2e7dc 100644 --- a/client/admin/integrations/new/NewIncomingWebhook.js +++ b/client/admin/integrations/new/NewIncomingWebhook.js @@ -1,5 +1,5 @@ import React, { useMemo } from 'react'; -import { Field, Box, Margins, Button } from '@rocket.chat/fuselage'; +import { Field, Box, Margins, Button, useCallback } from '@rocket.chat/fuselage'; import { useTranslation } from '../../../contexts/TranslationContext'; import { useRoute } from '../../../contexts/RouterContext'; @@ -26,14 +26,15 @@ export default function NewIncomingWebhook(props) { const { values: formValues, handlers: formHandlers, reset } = useForm(initialState); + // TODO: remove JSON.stringify. Is used to keep useEndpointAction from rerendering the page indefinitely. const saveAction = useEndpointAction('POST', 'integrations.create', useMemo(() => ({ ...formValues, type: 'webhook-incoming' }), [JSON.stringify(formValues)]), t('Integration_added')); - const handleSave = async () => { + const handleSave = useCallback(async () => { const result = await saveAction(); if (result.success) { router.push({ context: 'edit', type: 'incoming', id: result.integration._id }); } - }; + }, [router, saveAction]); const actionButtons = useMemo(() => @@ -44,7 +45,7 @@ export default function NewIncomingWebhook(props) {
- ); + , [handleSave, reset, t]); return ; } diff --git a/client/admin/integrations/new/NewIntegrationsPage.js b/client/admin/integrations/new/NewIntegrationsPage.js index 70dfc441127d9..805b34e571a83 100644 --- a/client/admin/integrations/new/NewIntegrationsPage.js +++ b/client/admin/integrations/new/NewIntegrationsPage.js @@ -13,19 +13,16 @@ export default function NewIntegrationsPage({ ...props }) { const router = useRoute('admin-integrations'); - const handleClickTab = (type) => () => { + const handleClickTab = useCallback((type) => () => { router.push({ context: 'new', type }); - }; + }, [router]); const handleClickReturn = useCallback(() => { router.push({ }); - }, []); + }, [router]); const tab = useRouteParameter('type'); - const handleIncomingTab = useCallback(handleClickTab('incoming'), []); - const handleOutgoingTab = useCallback(handleClickTab('outgoing'), []); - return @@ -37,13 +34,13 @@ export default function NewIntegrationsPage({ ...props }) { {t('Incoming')} {t('Outgoing')} diff --git a/client/admin/integrations/new/NewOutgoingWebhook.js b/client/admin/integrations/new/NewOutgoingWebhook.js index f211f07d5ba1d..c37665494c861 100644 --- a/client/admin/integrations/new/NewOutgoingWebhook.js +++ b/client/admin/integrations/new/NewOutgoingWebhook.js @@ -46,6 +46,7 @@ export default function NewOutgoingWebhook({ data = defaultData, onChange, setSa ...formValues, urls: urls.split('\n'), triggerWords: triggerWords.split(';'), + // TODO: remove JSON.stringify. Is used to keep useEndpointAction from rerendering the page indefinitely. }), [JSON.stringify(formValues)]); const saveIntegration = useEndpointAction('POST', 'integrations.create', query, t('Integration_added')); @@ -61,7 +62,7 @@ export default function NewOutgoingWebhook({ data = defaultData, onChange, setSa - ); + , [handleSave, t]); return ; diff --git a/client/admin/invites/InvitesPage.js b/client/admin/invites/InvitesPage.js index caf247692a605..682dd44e34fc6 100644 --- a/client/admin/invites/InvitesPage.js +++ b/client/admin/invites/InvitesPage.js @@ -124,7 +124,7 @@ function InvitesPage() { }; loadInvites(); - }, []); + }, [listInvites]); const handleInviteRemove = (_id) => { setInvites((invites = []) => invites.filter((invite) => invite._id !== _id)); diff --git a/client/admin/oauthApps/OAuthAddApp.js b/client/admin/oauthApps/OAuthAddApp.js index a31a083288bd3..dc4a8bc925839 100644 --- a/client/admin/oauthApps/OAuthAddApp.js +++ b/client/admin/oauthApps/OAuthAddApp.js @@ -42,7 +42,7 @@ export default function OAuthAddApp(props) { } catch (error) { dispatchToastMessage({ type: 'error', message: error }); } - }, [JSON.stringify(newData)]); + }, [close, dispatchToastMessage, newData, saveApp, t]); const handleChange = (field, getValue = (e) => e.currentTarget.value) => (e) => setNewData({ ...newData, [field]: getValue(e) }); diff --git a/client/admin/oauthApps/OAuthAppsTable.js b/client/admin/oauthApps/OAuthAppsTable.js index f4bd9060729d9..ad827da2fdee5 100644 --- a/client/admin/oauthApps/OAuthAppsTable.js +++ b/client/admin/oauthApps/OAuthAppsTable.js @@ -15,24 +15,23 @@ export function OAuthAppsTable() { const router = useRoute('admin-oauth-apps'); - const onClick = (_id) => () => router.push({ + const onClick = useCallback((_id) => () => router.push({ context: 'edit', id: _id, - }); + }), [router]); const header = useMemo(() => [
, , , - ]); + ], [t]); const renderRow = useCallback(({ _id, name, _createdAt, _createdBy: { username: createdBy } }) => {name} {createdBy} {formatDateAndTime(_createdAt)} - , - ); + , [formatDateAndTime, onClick]); return ; } diff --git a/client/admin/oauthApps/OAuthEditApp.js b/client/admin/oauthApps/OAuthEditApp.js index b525bbada5830..c10ce6f06ad10 100644 --- a/client/admin/oauthApps/OAuthEditApp.js +++ b/client/admin/oauthApps/OAuthEditApp.js @@ -72,6 +72,7 @@ export default function EditOauthAppWithData({ _id, ...props }) { const query = useMemo(() => ({ appId: _id, + // TODO: remove cache. Is necessary for data invalidation }), [_id, cache]); const { data, state, error } = useEndpointDataExperimental('oauth-apps.get', query); @@ -115,8 +116,8 @@ function EditOauthApp({ onChange, data, ...props }) { const close = useCallback(() => router.push({}), [router]); const absoluteUrl = useAbsoluteUrl(); - const authUrl = useMemo(() => absoluteUrl('oauth/authorize')); - const tokenUrl = useMemo(() => absoluteUrl('oauth/token')); + const authUrl = useMemo(() => absoluteUrl('oauth/authorize'), [absoluteUrl]); + const tokenUrl = useMemo(() => absoluteUrl('oauth/token'), [absoluteUrl]); const saveApp = useMethod('updateOAuthApp'); const deleteApp = useMethod('deleteOAuthApp'); @@ -132,7 +133,7 @@ function EditOauthApp({ onChange, data, ...props }) { } catch (error) { dispatchToastMessage({ type: 'error', message: error }); } - }, [JSON.stringify(newData)]); + }, [data._id, dispatchToastMessage, newData, onChange, saveApp, t]); const onDeleteConfirm = useCallback(async () => { try { @@ -141,7 +142,7 @@ function EditOauthApp({ onChange, data, ...props }) { } catch (error) { dispatchToastMessage({ type: 'error', message: error }); } - }, [data._id]); + }, [close, data._id, deleteApp, dispatchToastMessage]); const openConfirmDelete = () => setModal(() => setModal(undefined)}/>); diff --git a/client/admin/rooms/EditRoom.js b/client/admin/rooms/EditRoom.js index 06ad356d98284..790d58edab9ad 100644 --- a/client/admin/rooms/EditRoom.js +++ b/client/admin/rooms/EditRoom.js @@ -18,6 +18,7 @@ export function EditRoomContextBar({ rid }) { function EditRoomWithData({ rid }) { const [cache, setState] = useState(); + // TODO: remove cache. Is necessary for data invalidation const { data = {}, state, error } = useEndpointDataExperimental('rooms.adminRooms.getRoom', useMemo(() => ({ rid }), [rid, cache])); if (state === ENDPOINT_STATES.LOADING) { @@ -47,12 +48,12 @@ function EditRoom({ room, onChange }) { const canDelete = usePermission(`delete-${ room.t }`); - const hasUnsavedChanges = useMemo(() => Object.values(newData).filter((current) => current === null).length < Object.keys(newData).length, [JSON.stringify(newData)]); - const saveQuery = useMemo(() => ({ rid: room._id, ...Object.fromEntries(Object.entries(newData).filter(([, value]) => value !== null)) }), [room._id, JSON.stringify(newData)]); + const hasUnsavedChanges = useMemo(() => Object.values(newData).filter((current) => current === null).length < Object.keys(newData).length, [newData]); + const saveQuery = useMemo(() => ({ rid: room._id, ...Object.fromEntries(Object.entries(newData).filter(([, value]) => value !== null)) }), [room._id, newData]); const archiveSelector = room.archived ? 'unarchive' : 'archive'; const archiveMessage = archiveSelector === 'archive' ? 'Room_has_been_archived' : 'Room_has_been_archived'; - const archiveQuery = useMemo(() => ({ rid: room._id, action: room.archived ? 'unarchive' : 'archive' }), [room.rid, changeArchivation]); + const archiveQuery = useMemo(() => ({ rid: room._id, action: room.archived ? 'unarchive' : 'archive' }), [room._id, room.archived]); const saveAction = useEndpointAction('POST', 'rooms.saveRoomSettings', saveQuery, t('Room_updated_successfully')); const archiveAction = useEndpointAction('POST', 'rooms.changeArchivationState', archiveQuery, t(archiveMessage)); @@ -71,7 +72,7 @@ function EditRoom({ room, onChange }) { const handleDelete = useCallback(async () => { await deleteRoom(room._id); setDeleted(true); - }, [room]); + }, [deleteRoom, room._id]); const roomName = room.t === 'd' ? room.usernames.join(' x ') : roomTypes.getRoomName(room.t, { type: room.t, ...room }); const roomType = newData.roomType ?? room.t; diff --git a/client/admin/rooms/RoomsTable.js b/client/admin/rooms/RoomsTable.js index 195c00a6cff7d..6703c69b03e9f 100644 --- a/client/admin/rooms/RoomsTable.js +++ b/client/admin/rooms/RoomsTable.js @@ -36,7 +36,7 @@ const FilterByTypeAndText = ({ setFilter, ...props }) => { } const _types = Object.entries(types).filter(([, value]) => Boolean(value)).map(([key]) => key); setFilter({ text, types: _types }); - }, [text, types]); + }, [setFilter, text, types]); const idDirect = useUniqueId(); const idDPublic = useUniqueId(); @@ -75,13 +75,13 @@ const FilterByTypeAndText = ({ setFilter, ...props }) => { ; }; -const useQuery = (params, sort) => useMemo(() => ({ - filter: params.text || '', - types: params.types, - sort: JSON.stringify({ [sort[0]]: sort[1] === 'asc' ? 1 : -1 }), - ...params.itemsPerPage && { count: params.itemsPerPage }, - ...params.current && { offset: params.current }, -}), [JSON.stringify(params), JSON.stringify(sort)]); +const useQuery = ({ text, types, itemsPerPage, current }, [column, direction]) => useMemo(() => ({ + filter: text || '', + types, + sort: JSON.stringify({ [column]: direction === 'asc' ? 1 : -1 }), + ...itemsPerPage && { count: itemsPerPage }, + ...current && { offset: current }, +}), [text, types, itemsPerPage, current, column, direction]); function RoomsTable() { const t = useTranslation(); @@ -102,12 +102,12 @@ function RoomsTable() { const router = useRoute(routeName); - const onClick = (rid) => () => router.push({ + const onClick = useCallback((rid) => () => router.push({ context: 'edit', id: rid, - }); + }), [router]); - const onHeaderClick = (id) => { + const onHeaderClick = useCallback((id) => { const [sortBy, sortDirection] = sort; if (sortBy === id) { @@ -115,7 +115,7 @@ function RoomsTable() { return; } setSort([id, 'asc']); - }; + }, [sort]); if (sort[0] === 'name' && data.rooms) { data.rooms = data.rooms.sort((a, b) => { @@ -134,7 +134,7 @@ function RoomsTable() { mediaQuery && , mediaQuery && , mediaQuery && , - ].filter(Boolean), [sort, mediaQuery]); + ].filter(Boolean), [sort, onHeaderClick, t, mediaQuery]); const renderRow = useCallback(({ _id, name, t: type, usersCount, msgs, default: isDefault, featured, usernames, ...args }) => { const icon = roomTypes.getIcon({ t: type, usernames, ...args }); @@ -160,7 +160,7 @@ function RoomsTable() { {mediaQuery && {isDefault ? t('True') : t('False')}} {mediaQuery && {featured ? t('True') : t('False')}} ; - }, [mediaQuery]); + }, [mediaQuery, onClick, t]); return ; } diff --git a/client/admin/settings/Setting.js b/client/admin/settings/Setting.js index 38e3b205f059b..4257ef2c71189 100644 --- a/client/admin/settings/Setting.js +++ b/client/admin/settings/Setting.js @@ -115,8 +115,8 @@ export function Setting({ settingId, sectionChanged }) { } = setting; const label = (i18nLabel && t(i18nLabel)) || (_id || t(_id)); - const hint = useMemo(() => t.has(i18nDescription) && , [i18nDescription]); - const callout = useMemo(() => alert && , [alert]); + const hint = useMemo(() => t.has(i18nDescription) && , [i18nDescription, t]); + const callout = useMemo(() => alert && , [alert, t]); const hasResetButton = !disableReset && !readonly && type !== 'asset' && (JSON.stringify(packageEditor) !== JSON.stringify(editor) || JSON.stringify(value) !== JSON.stringify(packageValue)) && !disabled; return { setValue(valueProp); diff --git a/client/admin/settings/inputs/ColorSettingInput.js b/client/admin/settings/inputs/ColorSettingInput.js index 1469a9b3d8bb0..c958671cf2e5e 100644 --- a/client/admin/settings/inputs/ColorSettingInput.js +++ b/client/admin/settings/inputs/ColorSettingInput.js @@ -31,11 +31,11 @@ export function ColorSettingInput({ const handleChange = useCallback((event) => { onChangeValue && onChangeValue(event.currentTarget.value); - }, []); + }, [onChangeValue]); const handleEditorTypeChange = useCallback((value) => { onChangeEditor && onChangeEditor(value); - }, []); + }, [onChangeEditor]); return <> diff --git a/client/admin/settings/inputs/RoomPickSettingInput.js b/client/admin/settings/inputs/RoomPickSettingInput.js index 31cc259e30691..4ba25865caa38 100644 --- a/client/admin/settings/inputs/RoomPickSettingInput.js +++ b/client/admin/settings/inputs/RoomPickSettingInput.js @@ -69,7 +69,7 @@ export function RoomPickSettingInput({ return () => { Blaze.remove(view); }; - }, [valueRef]); + }, [_id, autocomplete, disabled, onChangeValue, placeholder, readonly, valueRef]); return <> diff --git a/client/admin/users/AddUser.js b/client/admin/users/AddUser.js index 6547c9d8e00ae..b6592e4c5cb3b 100644 --- a/client/admin/users/AddUser.js +++ b/client/admin/users/AddUser.js @@ -37,11 +37,12 @@ export function AddUser({ roles, ...props }) { customFields: {}, }); - const goToUser = (id) => router.push({ + const goToUser = useCallback((id) => router.push({ context: 'info', id, - }); + }), [router]); + // TODO: remove JSON.stringify. Is used to keep useEndpointAction from rerendering the page indefinitely. const saveQuery = useMemo(() => values, [JSON.stringify(values)]); const saveAction = useEndpointAction('POST', 'users.create', saveQuery, t('User_created_successfully')); @@ -51,9 +52,9 @@ export function AddUser({ roles, ...props }) { if (result.success) { goToUser(result.user._id); } - }, [saveAction]); + }, [goToUser, saveAction]); - const availableRoles = useMemo(() => (roleData && roleData.roles ? roleData.roles.map(({ _id, description }) => [_id, description || _id]) : []), [JSON.stringify(roleData)]); + const availableRoles = useMemo(() => (roleData && roleData.roles ? roleData.roles.map(({ _id, description }) => [_id, description || _id]) : []), [roleData]); const append = useMemo(() => @@ -62,7 +63,7 @@ export function AddUser({ roles, ...props }) { - , [reset, handleSave]); + , [hasUnsavedChanges, reset, t, handleSave]); return ; } diff --git a/client/admin/users/CustomFieldsForm.js b/client/admin/users/CustomFieldsForm.js index e6ee3074fa5b0..1df7d88f5caef 100644 --- a/client/admin/users/CustomFieldsForm.js +++ b/client/admin/users/CustomFieldsForm.js @@ -14,7 +14,7 @@ const CustomTextInput = (props) => { if (!state && required) { error.push(t('Field_required')); } if (state.length < minLength) { error.push(t('Min_length_is', minLength)); } return error.join(', '); - }, [required, minLength, maxLength, state]); + }, [state, required, minLength, t]); return useMemo(() => {name} @@ -22,14 +22,14 @@ const CustomTextInput = (props) => { setState(e.currentTarget.value)}/> {verify} - , [name, verify, state, required]); + , [name, verify, maxLength, state, required, setState]); }; const CustomSelect = (props) => { const t = useTranslation(); const { name, required, options, setState, state } = props; - const mappedOptions = useMemo(() => Object.values(options).map((value) => [value, value]), [...options]); - const verify = useMemo(() => (!state.length && required ? t('Field_required') : ''), [required, state]); + const mappedOptions = useMemo(() => Object.values(options).map((value) => [value, value]), [options]); + const verify = useMemo(() => (!state.length && required ? t('Field_required') : ''), [required, state.length, t]); return useMemo(() => {name} @@ -37,7 +37,7 @@ const CustomSelect = (props) => { , mediaQuery && , , - ].filter(Boolean), [sort, mediaQuery]); + ].filter(Boolean), [sort, onHeaderClick, t, mediaQuery]); const renderRow = useCallback(({ emails, _id, username, name, roles, status, ...args }) => { const avatarUrl = roomTypes.getConfig('d').getAvatarPath({ name: username || name, type: 'd', _id, ...args }); @@ -100,7 +100,7 @@ export function UsersTable() { {mediaQuery && {roles && roles.join(', ')}} {status}; - }, [mediaQuery]); + }, [mediaQuery, onClick]); return ; } diff --git a/client/admin/viewLogs/ViewLogs.js b/client/admin/viewLogs/ViewLogs.js index e7445d9eca337..5b84d172b223e 100644 --- a/client/admin/viewLogs/ViewLogs.js +++ b/client/admin/viewLogs/ViewLogs.js @@ -61,7 +61,7 @@ function ViewLogs() { }; fetchLines(); - }, []); + }, [dispatchToastMessage, getStdoutQueue]); useEffect(() => { const stdoutStreamer = new Meteor.Streamer('stdout'); @@ -117,13 +117,14 @@ function ViewLogs() { }, [isAtBottom, sendToBottom]); useEffect(() => { + const wrapper = wrapperRef.current; if (window.MutationObserver) { const observer = new MutationObserver((mutations) => { mutations.forEach(() => { sendToBottomIfNecessary(); }); }); - observer.observe(wrapperRef.current, { childList: true }); + observer.observe(wrapper, { childList: true }); return () => { observer.disconnect(); @@ -133,10 +134,10 @@ function ViewLogs() { const handleSubtreeModified = () => { sendToBottomIfNecessary(); }; - wrapperRef.current.addEventListener('DOMSubtreeModified', handleSubtreeModified); + wrapper.addEventListener('DOMSubtreeModified', handleSubtreeModified); return () => { - wrapperRef.current.removeEventListener('DOMSubtreeModified', handleSubtreeModified); + wrapper.removeEventListener('DOMSubtreeModified', handleSubtreeModified); }; }, [sendToBottomIfNecessary]); diff --git a/client/components/GenericTable.js b/client/components/GenericTable.js index cafd8fba5fffa..d71d5e96b9158 100644 --- a/client/components/GenericTable.js +++ b/client/components/GenericTable.js @@ -56,13 +56,13 @@ export const GenericTable = forwardRef(function GenericTable({ useEffect(() => { setParams({ ...params, current, itemsPerPage }); - }, [params, current, itemsPerPage]); + }, [params, current, itemsPerPage, setParams]); - const Loading = useCallback(() => Array.from({ length: 10 }, (_, i) => ), [header && header.length]); + const Loading = useCallback(() => Array.from({ length: 10 }, (_, i) => ), [header.length]); - const showingResultsLabel = useCallback(({ count, current, itemsPerPage }) => t('Showing results %s - %s of %s', current + 1, Math.min(current + itemsPerPage, count), count), []); + const showingResultsLabel = useCallback(({ count, current, itemsPerPage }) => t('Showing results %s - %s of %s', current + 1, Math.min(current + itemsPerPage, count), count), [t]); - const itemsPerPageLabel = useCallback(() => t('Items_per_page:'), []); + const itemsPerPageLabel = useCallback(() => t('Items_per_page:'), [t]); return <> diff --git a/client/components/SortList.js b/client/components/SortList.js index debde9fd6bae6..d9906e49b5248 100644 --- a/client/components/SortList.js +++ b/client/components/SortList.js @@ -47,10 +47,10 @@ function SortModeList() { const saveUserPreferences = useMethod('saveUserPreferences'); const sidebarSortBy = useUserPreference('sidebarSortby', 'activity'); - const handleChange = (value) => () => saveUserPreferences({ sidebarSortby: value }); + const useHandleChange = (value) => useCallback(() => saveUserPreferences({ sidebarSortby: value }), [value]); - const setToAlphabetical = useCallback(handleChange('alphabetical'), []); - const setToActivity = useCallback(handleChange('activity'), []); + const setToAlphabetical = useHandleChange('alphabetical'); + const setToActivity = useHandleChange('activity'); return <> @@ -71,16 +71,16 @@ function ViewModeList() { const saveUserPreferences = useMethod('saveUserPreferences'); - const handleChange = (value) => () => saveUserPreferences({ sidebarViewMode: value }); + const useHandleChange = (value) => useCallback(() => saveUserPreferences({ sidebarViewMode: value }), [value]); const sidebarViewMode = useUserPreference('sidebarViewMode', 'extended'); const sidebarHideAvatar = useUserPreference('sidebarHideAvatar', false); - const setToExtended = useCallback(handleChange('extended'), []); - const setToMedium = useCallback(handleChange('medium'), []); - const setToCondensed = useCallback(handleChange('condensed'), []); + const setToExtended = useHandleChange('extended'); + const setToMedium = useHandleChange('medium'); + const setToCondensed = useHandleChange('condensed'); - const handleChangeSidebarHideAvatar = useCallback(() => saveUserPreferences({ sidebarHideAvatar: !sidebarHideAvatar }), [sidebarHideAvatar]); + const handleChangeSidebarHideAvatar = useCallback(() => saveUserPreferences({ sidebarHideAvatar: !sidebarHideAvatar }), [saveUserPreferences, sidebarHideAvatar]); return <> @@ -106,12 +106,12 @@ function GroupingList() { const saveUserPreferences = useMethod('saveUserPreferences'); - const handleChange = (key, value) => () => saveUserPreferences({ [key]: value }); + const useHandleChange = (key, value) => useCallback(() => saveUserPreferences({ [key]: value }), [key, value]); - const handleChangeShowDicussion = useCallback(handleChange('sidebarShowDiscussion', !sidebarShowDiscussion), [sidebarShowDiscussion]); - const handleChangeGroupByType = useCallback(handleChange('sidebarGroupByType', !sidebarGroupByType), [sidebarGroupByType]); - const handleChangeShoFavorite = useCallback(handleChange('sidebarShowFavorites', !sidebarShowFavorites), [sidebarShowFavorites]); - const handleChangeShowUnread = useCallback(handleChange('sidebarShowUnread', !sidebarShowUnread), [sidebarShowUnread]); + const handleChangeShowDicussion = useHandleChange('sidebarShowDiscussion', !sidebarShowDiscussion); + const handleChangeGroupByType = useHandleChange('sidebarGroupByType', !sidebarGroupByType); + const handleChangeShoFavorite = useHandleChange('sidebarShowFavorites', !sidebarShowFavorites); + const handleChangeShowUnread = useHandleChange('sidebarShowUnread', !sidebarShowUnread); const t = useTranslation(); diff --git a/client/components/basic/Modal.js b/client/components/basic/Modal.js index 7444bcb3c56d8..95aa45206ac1a 100644 --- a/client/components/basic/Modal.js +++ b/client/components/basic/Modal.js @@ -23,7 +23,7 @@ function ModalPortal({ children = '' }) { useEffect(() => { modalRoot.appendChild(node); return () => modalRoot.removeChild(node); - }, [node]); + }, [modalRoot, node]); return createPortal(
{children}
, node, diff --git a/client/contexts/RouterContext.js b/client/contexts/RouterContext.js index fda98b78b5ca1..dafd6a7d31a43 100644 --- a/client/contexts/RouterContext.js +++ b/client/contexts/RouterContext.js @@ -24,7 +24,7 @@ export const useRoute = (name) => { getUrl: (...args) => getRouteUrl(name, ...args), push: (...args) => pushRoute(name, ...args), replace: (...args) => replaceRoute(name, ...args), - }), [getRoutePath, pushRoute, replaceRoute]); + }), [getRoutePath, getRouteUrl, name, pushRoute, replaceRoute]); }; export const useRoutePath = (name, params, queryStringParams) => { diff --git a/client/contexts/ServerContext.ts b/client/contexts/ServerContext.ts index 7934932a5fb22..df3d93571f593 100644 --- a/client/contexts/ServerContext.ts +++ b/client/contexts/ServerContext.ts @@ -47,7 +47,7 @@ export const useUpload = (endpoint: string): () => Promise => { export const useStream = (streamName: string, options?: object): IServerStream => { const { getStream } = useContext(ServerContext); - return useMemo(() => getStream(streamName, options), [streamName, options]); + return useMemo(() => getStream(streamName, options), [getStream, streamName, options]); }; export enum AsyncState { @@ -105,7 +105,7 @@ export const usePolledMethodData = (methodName: string, args: any[] = [], int return (): void => { clearInterval(timer); }; - }, []); + }, [fetchData, intervalMs]); return [data, state, fetchData]; }; diff --git a/client/hooks/useEndpointAction.js b/client/hooks/useEndpointAction.js index 7abf93140fabf..ff7962091d8f5 100644 --- a/client/hooks/useEndpointAction.js +++ b/client/hooks/useEndpointAction.js @@ -22,5 +22,5 @@ export const useEndpointAction = (httpMethod, endpoint, params = {}, successMess dispatchToastMessage({ type: 'error', message: error }); return { success: false }; } - }, [JSON.stringify(params)]); + }, [dispatchToastMessage, params, sendData, successMessage]); }; diff --git a/client/hooks/useEndpointData.js b/client/hooks/useEndpointData.js index b97bb47d38099..73e2352a84748 100644 --- a/client/hooks/useEndpointData.js +++ b/client/hooks/useEndpointData.js @@ -46,7 +46,7 @@ export const useEndpointData = (endpoint, params = {}) => { return () => { mounted = false; }; - }, [getData, params]); + }, [dispatchToastMessage, getData, params]); return data; }; diff --git a/client/hooks/useEndpointDataExperimental.js b/client/hooks/useEndpointDataExperimental.js index dd284a7a6c84c..6bf3d9368cf18 100644 --- a/client/hooks/useEndpointDataExperimental.js +++ b/client/hooks/useEndpointDataExperimental.js @@ -55,7 +55,7 @@ export const useEndpointDataExperimental = (endpoint, params = {}, { delayTimeou return () => { mounted = false; }; - }, [getData, params]); + }, [delayTimeout, dispatchToastMessage, getData, params]); return data; }; diff --git a/client/hooks/useEndpointUpload.js b/client/hooks/useEndpointUpload.js index 7a73876c3882e..0e15e74bd2cee 100644 --- a/client/hooks/useEndpointUpload.js +++ b/client/hooks/useEndpointUpload.js @@ -24,5 +24,5 @@ export const useEndpointUpload = (endpoint, params = {}, successMessage) => { dispatchToastMessage({ type: 'error', message: error }); return { success: false }; } - }, [JSON.stringify(params)]); + }, [dispatchToastMessage, params, sendData, successMessage]); }; diff --git a/client/hooks/useForm.js b/client/hooks/useForm.js index 90d967d93fcca..a47a1a4b0684b 100644 --- a/client/hooks/useForm.js +++ b/client/hooks/useForm.js @@ -1,3 +1,4 @@ +/* eslint-disable react-hooks/rules-of-hooks */ import { useState, useCallback } from 'react'; import { capitalize } from '../helpers/capitalize'; diff --git a/client/hooks/useFormatTime.js b/client/hooks/useFormatTime.js index de72d4e502643..4ff287c710112 100644 --- a/client/hooks/useFormatTime.js +++ b/client/hooks/useFormatTime.js @@ -18,5 +18,5 @@ export const useFormatTime = () => { default: return moment(time).format(format); } - }, [clockMode, format]); + }, [clockMode, format, sameDay]); }; diff --git a/client/hooks/useHighlightedCode.js b/client/hooks/useHighlightedCode.js new file mode 100644 index 0000000000000..8ce75542eae97 --- /dev/null +++ b/client/hooks/useHighlightedCode.js @@ -0,0 +1,6 @@ +import hljs from 'highlight.js'; +import { useMemo } from 'react'; + +export function useHighlightedCode(language, text) { + return useMemo(() => hljs.highlight(language, text).value, [language, text]); +} diff --git a/client/hooks/useHilightCode.js b/client/hooks/useHilightCode.js deleted file mode 100644 index eb081ef36a136..0000000000000 --- a/client/hooks/useHilightCode.js +++ /dev/null @@ -1,6 +0,0 @@ -import hljs from 'highlight.js'; -import { useMemo } from 'react'; - -export function useHilightCode() { - return (language, text) => useMemo(() => hljs.highlight(language, text).value, [language, text]); -} diff --git a/client/hooks/useObservableValue.js b/client/hooks/useObservableValue.js index 0b26e1350da9e..835eeaabdf23b 100644 --- a/client/hooks/useObservableValue.js +++ b/client/hooks/useObservableValue.js @@ -16,7 +16,7 @@ export const useObservableValue = (getValue) => { mounted = false; typeof unsubscribe === 'function' && unsubscribe(); }; - }, [getValue, name]); + }, [getValue]); return value; }; diff --git a/client/views/blocks/ModalBlock.js b/client/views/blocks/ModalBlock.js index d7f223baa9038..fa749622a0bcd 100644 --- a/client/views/blocks/ModalBlock.js +++ b/client/views/blocks/ModalBlock.js @@ -77,7 +77,7 @@ export function ModalBlock({ // save focus to restore after close const previousFocus = useMemo(() => document.activeElement, []); // restore the focus after the component unmount - useEffect(() => () => previousFocus && previousFocus.focus(), []); + useEffect(() => () => previousFocus && previousFocus.focus(), [previousFocus]); // Handle Tab, Shift + Tab, Enter and Escape const handleKeyDown = useCallback((event) => { if (event.keyCode === 13) { // ENTER @@ -115,7 +115,7 @@ export function ModalBlock({ event.preventDefault(); } } - }, [onSubmit]); + }, [onClose, onSubmit]); // Clean the events useEffect(() => { const element = document.querySelector('.rc-modal-wrapper'); @@ -143,7 +143,7 @@ export function ModalBlock({ document.removeEventListener('keydown', ignoreIfnotContains); element.removeEventListener('click', close); }; - }, handleKeyDown); + }, [handleKeyDown, onClose]); return ( diff --git a/client/views/directory/ChannelsTab.js b/client/views/directory/ChannelsTab.js index 583969ecb5d04..8b5994b8e3bb3 100644 --- a/client/views/directory/ChannelsTab.js +++ b/client/views/directory/ChannelsTab.js @@ -4,6 +4,7 @@ import React, { useMemo, useState, useCallback, useEffect } from 'react'; import { GenericTable, Th } from '../../components/GenericTable'; import MarkdownText from '../../components/basic/MarkdownText'; +import NotAuthorizedPage from '../../admin/NotAuthorizedPage'; import { useTranslation } from '../../contexts/TranslationContext'; import { usePermission } from '../../contexts/AuthorizationContext'; import { useRoute } from '../../contexts/RouterContext'; @@ -31,14 +32,14 @@ const FilterByText = ({ setFilter, ...props }) => { useEffect(() => { setFilter({ text }); - }, [text]); + }, [setFilter, text]); return } onChange={handleChange} value={text} /> ; }; -export function ChannelsTab() { +function ChannelsTable() { const t = useTranslation(); const [sort, setSort] = useState(['name', 'asc']); const [params, setParams] = useState({ current: 0, itemsPerPage: 25 }); @@ -62,13 +63,11 @@ export function ChannelsTab() {
, mediaQuery && , mediaQuery && , - ].filter(Boolean), [sort, mediaQuery]); + ].filter(Boolean), [sort, onHeaderClick, t, mediaQuery]); const channelRoute = useRoute('channel'); - const canViewPublicRooms = usePermission('view-c-room'); - - const data = (canViewPublicRooms && useEndpointData('directory', query)) || { result: [] }; + const data = useEndpointData('directory', query) || { result: [] }; const onClick = useMemo(() => (name) => (e) => { if (e.type === 'click' || e.key === 'Enter') { @@ -103,7 +102,17 @@ export function ChannelsTab() { } ; } - , [mediaQuery]); + , [formatDate, mediaQuery, onClick]); return ; } + +export default function ChannelsTab(props) { + const canViewPublicRooms = usePermission('view-c-room'); + + if (canViewPublicRooms) { + return ; + } + + return ; +} diff --git a/client/views/directory/DirectoryPage.js b/client/views/directory/DirectoryPage.js index b143715e28720..f5a7b8077e97a 100644 --- a/client/views/directory/DirectoryPage.js +++ b/client/views/directory/DirectoryPage.js @@ -3,8 +3,8 @@ import { Tabs } from '@rocket.chat/fuselage'; import Page from '../../components/basic/Page'; import { useTranslation } from '../../contexts/TranslationContext'; -import { UserTab } from './UserTab'; -import { ChannelsTab } from './ChannelsTab'; +import UserTab from './UserTab'; +import ChannelsTab from './ChannelsTab'; import { useRoute, useRouteParameter } from '../../contexts/RouterContext'; import { useSetting } from '../../contexts/SettingsContext'; @@ -18,7 +18,7 @@ function DirectoryPage() { const tab = useRouteParameter('tab'); const directoryRoute = useRoute('directory'); - const handleTabClick = useCallback((tab) => () => directoryRoute.push({ tab }), [tab]); + const handleTabClick = useCallback((tab) => () => directoryRoute.push({ tab }), [directoryRoute]); useEffect(() => { if (!tab || (tab === 'external' && !federationEnabled)) { diff --git a/client/views/directory/UserTab.js b/client/views/directory/UserTab.js index c1a978be34f1b..56d6fd6230b3a 100644 --- a/client/views/directory/UserTab.js +++ b/client/views/directory/UserTab.js @@ -10,6 +10,7 @@ import { useQuery } from './hooks'; import { roomTypes } from '../../../app/utils/client'; import { useEndpointData } from '../../hooks/useEndpointData'; import { useFormatDate } from '../../hooks/useFormatDate'; +import NotAuthorizedPage from '../../admin/NotAuthorizedPage'; const style = { whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }; @@ -20,14 +21,14 @@ const FilterByText = ({ setFilter, ...props }) => { useEffect(() => { setFilter({ text }); - }, [text]); + }, [text, setFilter]); return } onChange={handleChange} value={text} /> ; }; -export function UserTab({ +function UserTable({ workspace = 'local', }) { const [params, setParams] = useState({ current: 0, itemsPerPage: 25 }); @@ -41,7 +42,7 @@ export function UserTab({ const mediaQuery = useMediaQuery('(min-width: 1024px)'); - const onHeaderClick = (id) => { + const onHeaderClick = useCallback((id) => { const [sortBy, sortDirection] = sort; if (sortBy === id) { @@ -49,23 +50,20 @@ export function UserTab({ return; } setSort([id, 'asc']); - }; + }, [sort]); const header = useMemo(() => [ , mediaQuery && canViewFullOtherUserInfo && , federation && , mediaQuery && , - ].filter(Boolean), [sort, federation, canViewFullOtherUserInfo, mediaQuery]); + ].filter(Boolean), [sort, onHeaderClick, t, mediaQuery, canViewFullOtherUserInfo, federation]); const directRoute = useRoute('direct'); - const canViewOutsideRoom = usePermission('view-outside-room'); - const canViewDM = usePermission('view-d-room'); - - const data = (canViewOutsideRoom && canViewDM && useEndpointData('directory', query)) || { result: [] }; + const data = useEndpointData('directory', query) || {}; - const onClick = useMemo(() => (username) => (e) => { + const onClick = useCallback((username) => (e) => { if (e.type === 'click' || e.key === 'Enter') { directRoute.push({ rid: username }); } @@ -105,7 +103,18 @@ export function UserTab({ {formatDate(createdAt)} } ; - }, [mediaQuery, federation, canViewFullOtherUserInfo]); + }, [mediaQuery, federation, canViewFullOtherUserInfo, formatDate, onClick]); return ; } + +export default function UserTab(props) { + const canViewOutsideRoom = usePermission('view-outside-room'); + const canViewDM = usePermission('view-d-room'); + + if (canViewOutsideRoom && canViewDM) { + return ; + } + + return ; +} diff --git a/client/views/directory/hooks.js b/client/views/directory/hooks.js index 016849ffa4252..066aae7f32d13 100644 --- a/client/views/directory/hooks.js +++ b/client/views/directory/hooks.js @@ -1,14 +1,14 @@ import { useMemo } from 'react'; -export function useQuery(params, sort, type, workspace = 'local') { +export function useQuery({ text, itemsPerPage, current }, [column, direction], type, workspace = 'local') { return useMemo(() => ({ query: JSON.stringify({ type, - text: params.text, + text, workspace, }), - sort: JSON.stringify({ [sort[0]]: sort[1] === 'asc' ? 1 : 0 }), - ...params.itemsPerPage && { count: params.itemsPerPage }, - ...params.current && { offset: params.current }, - }), [params.itemsPerPage, params.current, sort, type, workspace, params.text]); + sort: JSON.stringify({ [column]: direction === 'asc' ? 1 : 0 }), + ...itemsPerPage && { count: itemsPerPage }, + ...current && { offset: current }, + }), [itemsPerPage, current, column, direction, type, workspace, text]); } diff --git a/client/views/setupWizard/SetupWizardRoute.js b/client/views/setupWizard/SetupWizardRoute.js index 51cbf0647be63..81f0921cc843c 100644 --- a/client/views/setupWizard/SetupWizardRoute.js +++ b/client/views/setupWizard/SetupWizardRoute.js @@ -36,7 +36,7 @@ const useRouteLock = () => { } setLocked(false); - }, [homeRoute, setupWizardState, userId, user, hasAdminRole]); + }, [homeRoute, setupWizardState, userId, user, hasAdminRole, locked]); return locked; }; diff --git a/client/views/setupWizard/SetupWizardState.js b/client/views/setupWizard/SetupWizardState.js index 5383e4a7cefcc..2a3874587a25b 100644 --- a/client/views/setupWizard/SetupWizardState.js +++ b/client/views/setupWizard/SetupWizardState.js @@ -72,7 +72,7 @@ const useParameters = () => { return () => { mounted = false; }; - }, []); + }, [getSetupWizardParameters]); return { loaded, @@ -110,12 +110,7 @@ function SetupWizardState() { goToPreviousStep, goToNextStep, goToFinalStep, - }), [ - currentStep, - loaded, - settings, - canDeclineServerRegistration, - ]); + }), [currentStep, loaded, settings, canDeclineServerRegistration, goToPreviousStep, goToNextStep, goToFinalStep]); return diff --git a/ee/app/engagement-dashboard/client/components/UsersTab/UsersByTimeOfTheDaySection.js b/ee/app/engagement-dashboard/client/components/UsersTab/UsersByTimeOfTheDaySection.js index 52046a6976974..d921776eaf9dc 100644 --- a/ee/app/engagement-dashboard/client/components/UsersTab/UsersByTimeOfTheDaySection.js +++ b/ee/app/engagement-dashboard/client/components/UsersTab/UsersByTimeOfTheDaySection.js @@ -75,7 +75,7 @@ export function UsersByTimeOfTheDaySection() { dates.map((date) => date.toISOString()), values, ]; - }, [data]); + }, [data, period.end, period.start]); return
Date: Wed, 17 Jun 2020 17:07:14 -0300 Subject: [PATCH 2/8] Add eslint rules for React hooks --- .eslintrc | 7 +- package-lock.json | 254 ++++++++++++++++++++++++---------------------- package.json | 1 + 3 files changed, 136 insertions(+), 126 deletions(-) diff --git a/.eslintrc b/.eslintrc index efef5b7d68d80..de12661f255b1 100644 --- a/.eslintrc +++ b/.eslintrc @@ -11,7 +11,8 @@ "jscolor": false }, "plugins": [ - "react" + "react", + "react-hooks" ], "rules": { "jsx-quotes": [ @@ -24,7 +25,9 @@ "react/jsx-fragments": [ "error", "syntax" - ] + ], + "react-hooks/rules-of-hooks": "error", + "react-hooks/exhaustive-deps": "warn" }, "settings": { "import/resolver": { diff --git a/package-lock.json b/package-lock.json index 38d8b1e3a3fc5..c5fd840984824 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14292,6 +14292,12 @@ } } }, + "eslint-plugin-react-hooks": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.0.4.tgz", + "integrity": "sha512-equAdEIsUETLFNCmmCkiCGq6rkSK5MoJhXFPFYeUebcjKgBmWWcgVOqZyQC8Bv1BwVCnTq9tBxgJFgAJTWoJtA==", + "dev": true + }, "eslint-scope": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", @@ -15476,28 +15482,28 @@ "dependencies": { "abbrev": { "version": "1.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true, "optional": true }, "ansi-regex": { "version": "2.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true, "optional": true }, "aproba": { "version": "1.2.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true, "optional": true }, "are-we-there-yet": { "version": "1.1.5", - "resolved": false, + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "dev": true, "optional": true, @@ -15508,14 +15514,14 @@ }, "balanced-match": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true, "optional": true }, "brace-expansion": { "version": "1.1.11", - "resolved": false, + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "optional": true, @@ -15533,28 +15539,28 @@ }, "code-point-at": { "version": "1.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "dev": true, "optional": true }, "concat-map": { "version": "0.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true, "optional": true }, "console-control-strings": { "version": "1.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "dev": true, "optional": true }, "core-util-is": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true, "optional": true @@ -15571,21 +15577,21 @@ }, "deep-extend": { "version": "0.6.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true, "optional": true }, "delegates": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "dev": true, "optional": true }, "detect-libc": { "version": "1.0.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", "dev": true, "optional": true @@ -15602,14 +15608,14 @@ }, "fs.realpath": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true, "optional": true }, "gauge": { "version": "2.7.4", - "resolved": false, + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, "optional": true, @@ -15641,14 +15647,14 @@ }, "has-unicode": { "version": "2.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "dev": true, "optional": true }, "iconv-lite": { "version": "0.4.24", - "resolved": false, + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "optional": true, @@ -15668,7 +15674,7 @@ }, "inflight": { "version": "1.0.6", - "resolved": false, + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "optional": true, @@ -15686,14 +15692,14 @@ }, "ini": { "version": "1.3.5", - "resolved": false, + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true, "optional": true }, "is-fullwidth-code-point": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "optional": true, @@ -15703,14 +15709,14 @@ }, "isarray": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true, "optional": true }, "minimatch": { "version": "3.0.4", - "resolved": false, + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "optional": true, @@ -15720,7 +15726,7 @@ }, "minimist": { "version": "0.0.8", - "resolved": false, + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true, "optional": true @@ -15748,7 +15754,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, "optional": true, @@ -15803,7 +15809,7 @@ }, "nopt": { "version": "4.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", "dev": true, "optional": true, @@ -15832,7 +15838,7 @@ }, "npmlog": { "version": "4.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, "optional": true, @@ -15845,21 +15851,21 @@ }, "number-is-nan": { "version": "1.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true, "optional": true }, "object-assign": { "version": "4.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true, "optional": true }, "once": { "version": "1.4.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "optional": true, @@ -15869,21 +15875,21 @@ }, "os-homedir": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true, "optional": true }, "os-tmpdir": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true, "optional": true }, "osenv": { "version": "0.1.5", - "resolved": false, + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "dev": true, "optional": true, @@ -15894,7 +15900,7 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true, "optional": true @@ -15908,7 +15914,7 @@ }, "rc": { "version": "1.2.8", - "resolved": false, + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, "optional": true, @@ -15921,7 +15927,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true, "optional": true @@ -15930,7 +15936,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": false, + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "optional": true, @@ -15956,21 +15962,21 @@ }, "safe-buffer": { "version": "5.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true, "optional": true }, "safer-buffer": { "version": "2.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true, "optional": true }, "sax": { "version": "1.2.4", - "resolved": false, + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true, "optional": true @@ -15984,21 +15990,21 @@ }, "set-blocking": { "version": "2.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true, "optional": true }, "signal-exit": { "version": "3.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true, "optional": true }, "string-width": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "optional": true, @@ -16010,7 +16016,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "optional": true, @@ -16020,7 +16026,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "optional": true, @@ -16030,7 +16036,7 @@ }, "strip-json-comments": { "version": "2.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true, "optional": true @@ -16053,14 +16059,14 @@ }, "util-deprecate": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true, "optional": true }, "wide-align": { "version": "1.1.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, "optional": true, @@ -16070,7 +16076,7 @@ }, "wrappy": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true, "optional": true @@ -21047,7 +21053,7 @@ "dependencies": { "asn1.js": { "version": "4.10.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", "requires": { "bn.js": "^4.0.0", @@ -21057,7 +21063,7 @@ }, "assert": { "version": "1.4.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", "requires": { "util": "0.10.3" @@ -21065,7 +21071,7 @@ "dependencies": { "util": { "version": "0.10.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", "requires": { "inherits": "2.0.1" @@ -21075,22 +21081,22 @@ }, "base64-js": { "version": "1.3.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==" }, "bn.js": { "version": "4.11.8", - "resolved": false, + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" }, "brorand": { "version": "1.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" }, "browserify-aes": { "version": "1.2.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "requires": { "buffer-xor": "^1.0.3", @@ -21103,7 +21109,7 @@ }, "browserify-cipher": { "version": "1.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", "requires": { "browserify-aes": "^1.0.4", @@ -21113,7 +21119,7 @@ }, "browserify-des": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", "requires": { "cipher-base": "^1.0.1", @@ -21124,7 +21130,7 @@ }, "browserify-rsa": { "version": "4.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "requires": { "bn.js": "^4.1.0", @@ -21133,7 +21139,7 @@ }, "browserify-sign": { "version": "4.0.4", - "resolved": false, + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", "requires": { "bn.js": "^4.1.1", @@ -21147,7 +21153,7 @@ }, "browserify-zlib": { "version": "0.2.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", "requires": { "pako": "~1.0.5" @@ -21155,7 +21161,7 @@ }, "buffer": { "version": "5.2.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", "requires": { "base64-js": "^1.0.2", @@ -21164,17 +21170,17 @@ }, "buffer-xor": { "version": "1.0.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" }, "builtin-status-codes": { "version": "3.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=" }, "cipher-base": { "version": "1.0.4", - "resolved": false, + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", "requires": { "inherits": "^2.0.1", @@ -21183,7 +21189,7 @@ }, "console-browserify": { "version": "1.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", "requires": { "date-now": "^0.1.4" @@ -21191,17 +21197,17 @@ }, "constants-browserify": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=" }, "core-util-is": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "create-ecdh": { "version": "4.0.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", "requires": { "bn.js": "^4.1.0", @@ -21210,7 +21216,7 @@ }, "create-hash": { "version": "1.2.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "requires": { "cipher-base": "^1.0.1", @@ -21222,7 +21228,7 @@ }, "create-hmac": { "version": "1.1.7", - "resolved": false, + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "requires": { "cipher-base": "^1.0.3", @@ -21235,7 +21241,7 @@ }, "crypto-browserify": { "version": "3.12.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", "requires": { "browserify-cipher": "^1.0.0", @@ -21253,12 +21259,12 @@ }, "date-now": { "version": "0.1.4", - "resolved": false, + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=" }, "des.js": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", "requires": { "inherits": "^2.0.1", @@ -21267,7 +21273,7 @@ }, "diffie-hellman": { "version": "5.0.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", "requires": { "bn.js": "^4.1.0", @@ -21277,12 +21283,12 @@ }, "domain-browser": { "version": "1.2.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==" }, "elliptic": { "version": "6.4.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==", "requires": { "bn.js": "^4.4.0", @@ -21296,12 +21302,12 @@ }, "events": { "version": "3.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz", "integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==" }, "evp_bytestokey": { "version": "1.0.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", "requires": { "md5.js": "^1.3.4", @@ -21310,7 +21316,7 @@ }, "hash-base": { "version": "3.0.4", - "resolved": false, + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", "requires": { "inherits": "^2.0.1", @@ -21319,7 +21325,7 @@ }, "hash.js": { "version": "1.1.7", - "resolved": false, + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", "requires": { "inherits": "^2.0.3", @@ -21328,14 +21334,14 @@ "dependencies": { "inherits": { "version": "2.0.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" } } }, "hmac-drbg": { "version": "1.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", "requires": { "hash.js": "^1.0.3", @@ -21345,27 +21351,27 @@ }, "https-browserify": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" }, "ieee754": { "version": "1.1.13", - "resolved": false, + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" }, "inherits": { "version": "2.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" }, "isarray": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "md5.js": { "version": "1.3.5", - "resolved": false, + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", "requires": { "hash-base": "^3.0.0", @@ -21375,7 +21381,7 @@ }, "miller-rabin": { "version": "4.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", "requires": { "bn.js": "^4.0.0", @@ -21384,27 +21390,27 @@ }, "minimalistic-assert": { "version": "1.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" }, "minimalistic-crypto-utils": { "version": "1.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" }, "os-browserify": { "version": "0.3.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=" }, "pako": { "version": "1.0.10", - "resolved": false, + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==" }, "parse-asn1": { "version": "5.1.4", - "resolved": false, + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.4.tgz", "integrity": "sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==", "requires": { "asn1.js": "^4.0.0", @@ -21417,12 +21423,12 @@ }, "path-browserify": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.0.tgz", "integrity": "sha512-Hkavx/nY4/plImrZPHRk2CL9vpOymZLgEbMNX1U0bjcBL7QN9wODxyx0yaMZURSQaUtSEvDrfAvxa9oPb0at9g==" }, "pbkdf2": { "version": "3.0.17", - "resolved": false, + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", "requires": { "create-hash": "^1.1.2", @@ -21434,17 +21440,17 @@ }, "process": { "version": "0.11.10", - "resolved": false, + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" }, "process-nextick-args": { "version": "2.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" }, "public-encrypt": { "version": "4.0.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", "requires": { "bn.js": "^4.1.0", @@ -21457,22 +21463,22 @@ }, "punycode": { "version": "2.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, "querystring": { "version": "0.2.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" }, "querystring-es3": { "version": "0.2.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=" }, "randombytes": { "version": "2.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "requires": { "safe-buffer": "^5.1.0" @@ -21480,7 +21486,7 @@ }, "randomfill": { "version": "1.0.4", - "resolved": false, + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", "requires": { "randombytes": "^2.0.5", @@ -21489,7 +21495,7 @@ }, "readable-stream": { "version": "3.3.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.3.0.tgz", "integrity": "sha512-EsI+s3k3XsW+fU8fQACLN59ky34AZ14LoeVZpYwmZvldCFo0r0gnelwF2TcMjLor/BTL5aDJVBMkss0dthToPw==", "requires": { "inherits": "^2.0.3", @@ -21499,14 +21505,14 @@ "dependencies": { "inherits": { "version": "2.0.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" } } }, "ripemd160": { "version": "2.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", "requires": { "hash-base": "^3.0.0", @@ -21515,17 +21521,17 @@ }, "safe-buffer": { "version": "5.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "setimmediate": { "version": "1.0.5", - "resolved": false, + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" }, "sha.js": { "version": "2.4.11", - "resolved": false, + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "requires": { "inherits": "^2.0.1", @@ -21534,7 +21540,7 @@ }, "stream-browserify": { "version": "2.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", "requires": { "inherits": "~2.0.1", @@ -21543,7 +21549,7 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "resolved": false, + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -21557,14 +21563,14 @@ "dependencies": { "inherits": { "version": "2.0.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" } } }, "string_decoder": { "version": "1.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { "safe-buffer": "~5.1.0" @@ -21574,7 +21580,7 @@ }, "stream-http": { "version": "3.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.0.0.tgz", "integrity": "sha512-JELJfd+btL9GHtxU3+XXhg9NLYrKFnhybfvRuDghtyVkOFydz3PKNT1df07AMr88qW03WHF+FSV0PySpXignCA==", "requires": { "builtin-status-codes": "^3.0.0", @@ -21585,7 +21591,7 @@ }, "string_decoder": { "version": "1.2.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz", "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==", "requires": { "safe-buffer": "~5.1.0" @@ -21593,7 +21599,7 @@ }, "timers-browserify": { "version": "2.0.10", - "resolved": false, + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==", "requires": { "setimmediate": "^1.0.4" @@ -21601,12 +21607,12 @@ }, "tty-browserify": { "version": "0.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==" }, "url": { "version": "0.11.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", "requires": { "punycode": "1.3.2", @@ -21615,14 +21621,14 @@ "dependencies": { "punycode": { "version": "1.3.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" } } }, "util": { "version": "0.11.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", "requires": { "inherits": "2.0.3" @@ -21630,24 +21636,24 @@ "dependencies": { "inherits": { "version": "2.0.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" } } }, "util-deprecate": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "vm-browserify": { "version": "1.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.0.tgz", "integrity": "sha512-iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw==" }, "xtend": { "version": "4.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" } } diff --git a/package.json b/package.json index 53ad8abd757f1..71c536e8d4461 100644 --- a/package.json +++ b/package.json @@ -87,6 +87,7 @@ "eslint": "^6.7.2", "eslint-plugin-import": "^2.19.1", "eslint-plugin-react": "^7.17.0", + "eslint-plugin-react-hooks": "^4.0.4", "fast-glob": "^2.2.6", "husky": "^1.2.0", "i18next": "^17.0.17", From 7af15bb0c1fd4db51903fbd805292cb14d490365 Mon Sep 17 00:00:00 2001 From: Gabriel Henriques Date: Wed, 17 Jun 2020 17:09:26 -0300 Subject: [PATCH 3/8] Lint --- client/admin/settings/GroupPage.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/admin/settings/GroupPage.js b/client/admin/settings/GroupPage.js index 2b8448b0b28f7..9ca39d6853897 100644 --- a/client/admin/settings/GroupPage.js +++ b/client/admin/settings/GroupPage.js @@ -5,6 +5,7 @@ import Page from '../../components/basic/Page'; import { useTranslation } from '../../contexts/TranslationContext'; import { Section } from './Section'; +const style = { margin: '0 auto', width: '100%', maxWidth: '590px' }; export function GroupPage({ children, headerButtons, save, cancel, _id, i18nLabel, i18nDescription, changed }) { const t = useTranslation(); @@ -47,7 +48,7 @@ export function GroupPage({ children, headerButtons, save, cancel, _id, i18nLabe - ({ margin: '0 auto', width: '100%', maxWidth: '590px' }), [])}> + {t.has(i18nDescription) && {t(i18nDescription)}} From b3ee2a04bfa5ab87b34508b249e6c8b1579a4e56 Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Wed, 17 Jun 2020 23:41:08 -0300 Subject: [PATCH 4/8] Remove console.count call --- client/admin/customSounds/EditCustomSound.js | 1 - package-lock.json | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/client/admin/customSounds/EditCustomSound.js b/client/admin/customSounds/EditCustomSound.js index da2551ee23817..0f1bbb2b1437c 100644 --- a/client/admin/customSounds/EditCustomSound.js +++ b/client/admin/customSounds/EditCustomSound.js @@ -56,7 +56,6 @@ export function EditCustomSound({ _id, cache, ...props }) { const { data, state, error } = useEndpointDataExperimental('custom-sounds.list', query); - console.count('EditCustomSound'); if (state === ENDPOINT_STATES.LOADING) { return diff --git a/package-lock.json b/package-lock.json index c5fd840984824..826ea915ada51 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2720,6 +2720,11 @@ "uuid": "^3.2.1" }, "dependencies": { + "adm-zip": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.14.tgz", + "integrity": "sha512-/9aQCnQHF+0IiCl0qhXoK7qs//SwYE7zX8lsr/DNk1BRAHYxeLZPL4pguwK29gUEqasYQjqPtEpDRSWEkdHn9g==" + }, "typescript": { "version": "2.9.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", From f515741e881643d4af265a67aa850ac917973ea2 Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Wed, 17 Jun 2020 23:46:21 -0300 Subject: [PATCH 5/8] Fix wrong hook dependency with optional chaining --- client/admin/import/ImportHistoryPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/admin/import/ImportHistoryPage.js b/client/admin/import/ImportHistoryPage.js index 8414c23ba34a5..8edf7ba5f00de 100644 --- a/client/admin/import/ImportHistoryPage.js +++ b/client/admin/import/ImportHistoryPage.js @@ -50,7 +50,7 @@ function ImportHistoryPage() { }, [dispatchToastMessage, getCurrentImportOperation, getLatestImportOperations, setCurrentOperation, setLatestOperations, setLoading, t]); const hasAnySuccessfulSlackImport = useMemo(() => - latestOperations?.some(({ importerKey, status }) => importerKey === 'slack' && status === ProgressStep.DONE), [latestOperations?.some]); + latestOperations?.some(({ importerKey, status }) => importerKey === 'slack' && status === ProgressStep.DONE), [latestOperations]); const handleNewImportClick = () => { newImportRoute.push(); From f779f0da60749bf52716fedf868e78588d349513 Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Wed, 17 Jun 2020 23:50:25 -0300 Subject: [PATCH 6/8] Move IntegrationRow outside IntegrationsTable --- .../admin/integrations/IntegrationsTable.js | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/client/admin/integrations/IntegrationsTable.js b/client/admin/integrations/IntegrationsTable.js index f362ffabbb1c9..8efe505a287ec 100644 --- a/client/admin/integrations/IntegrationsTable.js +++ b/client/admin/integrations/IntegrationsTable.js @@ -40,9 +40,31 @@ const useResizeInlineBreakpoint = (sizes = [], debounceDelay = 0) => { return [ref, ...sizes]; }; +function IntegrationRow({ + name, + _id, + type, + username, + _createdAt, + _createdBy: { username: createdBy }, + channel = [], + onClick, + isBig, +}) { + const formatDateAndTime = useFormatDateAndTime(); + + const handler = useMemo(() => onClick(_id, type), [onClick, _id, type]); + return + {name} + {channel.join(', ')} + {createdBy} + {isBig && {formatDateAndTime(_createdAt)}} + {username} + ; +} + export function IntegrationsTable({ type }) { const t = useTranslation(); - const formatDateAndTime = useFormatDateAndTime(); const [ref, isBig] = useResizeInlineBreakpoint([700], 200); const [params, setParams] = useState({ text: '', current: 0, itemsPerPage: 25 }); @@ -80,16 +102,7 @@ export function IntegrationsTable({ type }) {
, ].filter(Boolean), [sort, onHeaderClick, isBig, t]); - const renderRow = useCallback(function IntegrationRow({ name, _id, type, username, _createdAt, _createdBy: { username: createdBy }, channel = [] }) { - const handler = useMemo(() => onClick(_id, type), [_id, type]); - return - {name} - {channel.join(', ')} - {createdBy} - {isBig && {formatDateAndTime(_createdAt)}} - {username} - ; - }, [formatDateAndTime, isBig, onClick]); + const renderRow = useCallback((props) => , [isBig, onClick]); return ; } From 0aa616f592d6acb4985d99604ec8b508f0e10cac Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Wed, 17 Jun 2020 23:51:10 -0300 Subject: [PATCH 7/8] Fix import --- client/admin/integrations/new/NewIncomingWebhook.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/admin/integrations/new/NewIncomingWebhook.js b/client/admin/integrations/new/NewIncomingWebhook.js index 6aac3bca2e7dc..a04bd5aaa3d48 100644 --- a/client/admin/integrations/new/NewIncomingWebhook.js +++ b/client/admin/integrations/new/NewIncomingWebhook.js @@ -1,5 +1,5 @@ -import React, { useMemo } from 'react'; -import { Field, Box, Margins, Button, useCallback } from '@rocket.chat/fuselage'; +import React, { useCallback, useMemo } from 'react'; +import { Field, Box, Margins, Button } from '@rocket.chat/fuselage'; import { useTranslation } from '../../../contexts/TranslationContext'; import { useRoute } from '../../../contexts/RouterContext'; From 3702ff5d43eb84e975a7bb6a5874f192d4b2d87a Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Wed, 17 Jun 2020 23:52:27 -0300 Subject: [PATCH 8/8] Add TODO message to useForm --- client/hooks/useForm.js | 1 + 1 file changed, 1 insertion(+) diff --git a/client/hooks/useForm.js b/client/hooks/useForm.js index a47a1a4b0684b..0da0149920d70 100644 --- a/client/hooks/useForm.js +++ b/client/hooks/useForm.js @@ -8,6 +8,7 @@ const getValue = (e) => (e.currentTarget ? e.currentTarget.value : e); export const useForm = (obj) => { const resetCallbacks = []; const hasUnsavedChanges = []; + // TODO: use useReducer hook as we can't assure that obj will have the same structure on each render const ret = Object.keys(obj).sort().reduce((ret, key) => { const value = obj[key]; const [data, setData] = useState(value);
{t('Created_by')}{t('Created_at')}{t('Post_as')}{t('Name')}{t('Created_by')}{t('Created_at')}{t('Msgs')}{t('Default')}{t('Featured')}{t('Email')}{t('Roles')}{t('Status')} {t('Users')}{t('Created_at')}{t('Last_Message')}{t('Name')}{t('Email')}{t('Domain')}{t('Joined_at')}{t('Post_as')}