diff --git a/.gitignore b/.gitignore index 194c92a..4d29575 100644 --- a/.gitignore +++ b/.gitignore @@ -10,8 +10,6 @@ # production /build -/public - # misc .DS_Store diff --git a/frontend/.eslintrc.yml b/frontend/.eslintrc.yml index 0a167f4..bf3b8e3 100644 --- a/frontend/.eslintrc.yml +++ b/frontend/.eslintrc.yml @@ -1,8 +1,3 @@ -# Линтер подключаем именно в папку, созданную create-react-app -# Все зависимости линтера должны быть установлены на этом уровне, -# а не в корне проекта -# Не забудьте удалить ключ eslintConfig из package.json, -# автоматический созданный при запуске CRA env: browser: true es2021: true @@ -29,13 +24,12 @@ rules: react/prop-types: 0 no-console: 0 react/react-in-jsx-scope: 0 - functional/no-conditional-statements: 0 - functional/no-expression-statements: 0 + functional/no-conditional-statement: 0 + functional/no-expression-statement: 0 functional/immutable-data: 0 functional/functional-parameters: 0 - functional/no-try-statements: 0 - functional/no-throw-statements: 0 - functional/no-return-void: 0 + functional/no-try-statement: 0 + functional/no-throw-statement: 0 no-underscore-dangle: [2, { "allow": ["__filename", "__dirname"] }] react/function-component-definition: [2, { "namedComponents": "arrow-function" }] testing-library/no-debug: 0 diff --git a/frontend/.gitignore b/frontend/.gitignore index d989ba2..4d29575 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -10,7 +10,6 @@ # production /build -/public # misc .DS_Store diff --git a/frontend/package.json b/frontend/package.json index 74d10e2..fc99787 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -52,12 +52,14 @@ ] }, "devDependencies": { - "eslint": "^8.57.0", + "@eslint/js": "^9.9.0", + "eslint": "^9.9.0", "eslint-config-airbnb": "^19.0.4", "eslint-plugin-functional": "^4.4.1", "eslint-plugin-import": "^2.26.0", "eslint-plugin-jsx-a11y": "^6.6.1", "eslint-plugin-react": "^7.35.0", - "eslint-plugin-react-hooks": "^4.6.2" + "eslint-plugin-react-hooks": "^4.6.2", + "globals": "^15.9.0" } } diff --git a/frontend/src/Components/ErrorPage.jsx b/frontend/src/Components/ErrorPage.jsx index 6d1d4db..e2fa0c8 100644 --- a/frontend/src/Components/ErrorPage.jsx +++ b/frontend/src/Components/ErrorPage.jsx @@ -3,7 +3,7 @@ import Row from 'react-bootstrap/Row'; import Col from 'react-bootstrap/Col'; import MainContainer from './MainContainer'; -export default function () { +const ErrorPage = () => { const { t } = useTranslation(); return ( @@ -17,4 +17,6 @@ export default function () { ); -} +}; + +export default ErrorPage; diff --git a/frontend/src/Components/Header.jsx b/frontend/src/Components/Header.jsx index 057c68a..543ba1c 100644 --- a/frontend/src/Components/Header.jsx +++ b/frontend/src/Components/Header.jsx @@ -21,7 +21,7 @@ const BurgerButton = ({ isMenuOpen, onClick, ref }) => ( ); -export default function () { +const Header = () => { const { t } = useTranslation(); const mobileMenuRef = useRef(null); const dispatch = useDispatch(); @@ -52,7 +52,7 @@ export default function () { return () => { document.removeEventListener('mousedown', listener); }; - }, [isMenuOpen]); + }, [dispatch, isMenuOpen]); return ( @@ -74,4 +74,6 @@ export default function () { ); -} +}; + +export default Header; diff --git a/frontend/src/Components/LoginPage.jsx b/frontend/src/Components/LoginPage.jsx index 9fbad31..eaf9a9b 100644 --- a/frontend/src/Components/LoginPage.jsx +++ b/frontend/src/Components/LoginPage.jsx @@ -16,7 +16,7 @@ const sendAuthRequest = async (dispatch, loginValues) => { const { token, username } = res.data; dispatch(login({ name: username, token })); }; -export default function () { +const LoginPage = () => { const { t } = useTranslation(); const dispatch = useDispatch(); @@ -80,4 +80,6 @@ export default function () { ); -} +}; + +export default LoginPage; diff --git a/frontend/src/Components/MainContainer.jsx b/frontend/src/Components/MainContainer.jsx index 0da0a70..c90e69e 100644 --- a/frontend/src/Components/MainContainer.jsx +++ b/frontend/src/Components/MainContainer.jsx @@ -1,13 +1,14 @@ +/* eslint-disable react/destructuring-assignment */ import { Container } from 'react-bootstrap'; import { ToastContainer } from 'react-toastify'; import Header from './Header'; -export default function (props) { - return ( - -
- {props.children} - - - ); -} +const MainContainer = (props) => ( + +
+ {props.children} + + +); + +export default MainContainer; diff --git a/frontend/src/Components/SignupPage.jsx b/frontend/src/Components/SignupPage.jsx index 0b8798f..d52b874 100644 --- a/frontend/src/Components/SignupPage.jsx +++ b/frontend/src/Components/SignupPage.jsx @@ -18,7 +18,7 @@ const sendAuthRequest = async (dispatch, loginValues) => { dispatch(login({ name: username, token })); }; -export default function () { +const SignupPage = () => { const { t } = useTranslation(); const dispatch = useDispatch(); const navigate = useNavigate(); @@ -132,4 +132,6 @@ export default function () { ); -} +}; + +export default SignupPage; diff --git a/frontend/src/Components/mainPage/Channels.jsx b/frontend/src/Components/mainPage/Channels.jsx index ee5c754..af78c69 100644 --- a/frontend/src/Components/mainPage/Channels.jsx +++ b/frontend/src/Components/mainPage/Channels.jsx @@ -1,3 +1,9 @@ +/* eslint-disable no-unused-expressions */ +/* eslint-disable no-param-reassign */ +/* eslint-disable no-use-before-define */ +/* eslint-disable default-case */ +/* eslint-disable functional/no-let */ +/* eslint-disable functional/no-expression-statement */ import { useDispatch, useSelector } from 'react-redux'; import { Nav, Button, Col, Modal, Form, Dropdown, @@ -60,7 +66,8 @@ const ChannelModal = ({ const f = useFormik({ onSubmit: (values) => { setLoading(true); - action.name == 'remove' ? action.handler(token) : action.handler(token, values.channelName); + // eslint-disable-next-line no-unused-expressions + action.name === 'remove' ? action.handler(token) : action.handler(token, values.channelName); handleCloseModal(); values.channelName = ''; setLoading(false); @@ -85,7 +92,7 @@ const ChannelModal = ({ {action.description} - {action.name == 'remove' ? ( + {action.name === 'remove' ? ( t('channelRemoveConfirmation')) : (
@@ -119,7 +126,7 @@ const ChannelModal = ({ ); }; -export default function ({ filter }) { +const Channels = ({ filter }) => { const messages = useSelector(selectMessages); const channels = useSelector(selectChannels); const currentChennelId = useSelector(selectCurrentChannelId); @@ -176,7 +183,7 @@ export default function ({ filter }) { m.channelId == channelId).map((m) => m.id); + const thisChannelMessagesIds = messages + .filter((m) => +m.channelId === +channelId).map((m) => m.id); thisChannelMessagesIds.map(async (messageId) => { await axios.delete(routes.messagePath(messageId), { headers: { @@ -269,4 +277,6 @@ export default function ({ filter }) { )} ); -} +}; + +export default Channels; diff --git a/frontend/src/Components/mainPage/MainPage.jsx b/frontend/src/Components/mainPage/MainPage.jsx index a072114..0417239 100644 --- a/frontend/src/Components/mainPage/MainPage.jsx +++ b/frontend/src/Components/mainPage/MainPage.jsx @@ -33,7 +33,7 @@ const fetchMessages = async (token, dispatch) => { return res.data; }; -export default function () { +const MainPage = () => { const [loading, setLoading] = useState(true); const currentUser = useSelector(selectCurrentUser); const dispatch = useDispatch(); @@ -76,4 +76,6 @@ export default function () { )} ); -} +}; + +export default MainPage; diff --git a/frontend/src/Components/mainPage/Message.jsx b/frontend/src/Components/mainPage/Message.jsx index 7dff6d6..e6d4673 100644 --- a/frontend/src/Components/mainPage/Message.jsx +++ b/frontend/src/Components/mainPage/Message.jsx @@ -55,9 +55,9 @@ const MessageRemoveModal = ({ ); }; -export default function ({ +const Message = ({ username, body, id, filter, -}) { +}) => { const { t } = useTranslation(); const currentUser = useSelector(selectCurrentUser); const isMessageMine = username === currentUser.name; @@ -65,6 +65,7 @@ export default function ({ const [isEditing, setIsEditing] = useState(false); const lines = body.split('\n').map((line, index) => ( + // eslint-disable-next-line react/no-array-index-key
{filter.clean(line)}
)); @@ -76,9 +77,9 @@ export default function ({ setShowModal(false); }; - const editMessageHandler = async (id, token, body) => { + const editMessageHandler = async (messageId, token, messageBody) => { try { - const res = await axios.patch(routes.messagePath(id), { body }, { + const res = await axios.patch(routes.messagePath(messageId), { messageBody }, { headers: { Authorization: `Bearer ${token}`, }, @@ -91,9 +92,9 @@ export default function ({ } }; - const removeMessageHandler = (id) => async (token) => { + const removeMessageHandler = (messageId) => async (token) => { try { - const res = await axios.delete(routes.messagePath(id), { + const res = await axios.delete(routes.messagePath(messageId), { headers: { Authorization: `Bearer ${token}`, }, @@ -169,4 +170,6 @@ export default function ({ /> ); -} +}; + +export default Message; diff --git a/frontend/src/Components/mainPage/Messages.jsx b/frontend/src/Components/mainPage/Messages.jsx index 73c0f77..6ef89c5 100644 --- a/frontend/src/Components/mainPage/Messages.jsx +++ b/frontend/src/Components/mainPage/Messages.jsx @@ -8,7 +8,7 @@ import MessagesHeader from './MessagesHeader'; import { selectMessages } from '../../slices/messagesSlice'; import { selectCurrentChannelId } from '../../slices/channelsSlice'; -export default function ({ filter }) { +const Messages = ({ filter }) => { const messages = useSelector(selectMessages); const currentChannelId = useSelector(selectCurrentChannelId); const container = useRef(null); @@ -79,4 +79,6 @@ export default function ({ filter }) { ); -} +}; + +export default Messages; diff --git a/frontend/src/Components/mainPage/MessagesForm.jsx b/frontend/src/Components/mainPage/MessagesForm.jsx index eee2259..48e09fb 100644 --- a/frontend/src/Components/mainPage/MessagesForm.jsx +++ b/frontend/src/Components/mainPage/MessagesForm.jsx @@ -1,6 +1,8 @@ +/* eslint-disable no-param-reassign */ import { BsSend } from 'react-icons/bs'; import { Form, Button } from 'react-bootstrap'; import { useSelector } from 'react-redux'; +// eslint-disable-next-line import/no-extraneous-dependencies import Picker from 'emoji-picker-react'; import { useFormik } from 'formik'; import { useTranslation } from 'react-i18next'; @@ -20,7 +22,7 @@ const postMessage = async (token, newMessage) => { return res.data; }; -export default function () { +const MessageForm = () => { const { t } = useTranslation(); const currentUser = useSelector(selectCurrentUser); const currentChannelId = useSelector(selectCurrentChannelId); @@ -31,6 +33,7 @@ export default function () { const [isMobileKeyboard, setIsMobileKeyboard] = useState(false); const [keyboardHeight, setKeyboardHeight] = useState(0); + // eslint-disable-next-line consistent-return useEffect(() => { if ('virtualKeyboard' in navigator) { navigator.virtualKeyboard.overlaysContent = true; @@ -61,13 +64,11 @@ export default function () { return () => { window.removeEventListener('resize', updateBodyHeight); }; - }, [keyboardHeight]); + }, [bodyEl.style, keyboardHeight]); const f = useFormik({ onSubmit: (values) => { - if (values.messageText === '') { - - } else { + if (values.messageText === '') { /* empty */ } else { setIsSending(true); const newMessage = { body: values.messageText, @@ -107,9 +108,9 @@ export default function () { const handleChange = (event) => { if (!isSending) { - let { value } = event.target; - value = value.replace(/^\s+/, ''); - f.setFieldValue('messageText', value); + const { value } = event.target; + const updatedValue = value.replace(/^\s+/, ''); + f.setFieldValue('messageText', updatedValue); } else { f.setFieldValue(''); } @@ -126,6 +127,7 @@ export default function () { f.handleSubmit(e); }; + // eslint-disable-next-line consistent-return const onKeyDown = (event) => { if (event.shiftKey && event.key === 'Enter') { event.preventDefault(); @@ -156,7 +158,7 @@ export default function () { {isEmojiPickerOpen && ( { + onEmojiClick={(emojiObject) => { f.setFieldValue('messageText', f.values.messageText + emojiObject.emoji); }} /> @@ -187,4 +189,6 @@ export default function () { ); -} +}; + +export default MessageForm; diff --git a/frontend/src/Components/mainPage/MessagesHeader.jsx b/frontend/src/Components/mainPage/MessagesHeader.jsx index d3be8c3..8c04508 100644 --- a/frontend/src/Components/mainPage/MessagesHeader.jsx +++ b/frontend/src/Components/mainPage/MessagesHeader.jsx @@ -4,15 +4,16 @@ import { useTranslation } from 'react-i18next'; import { selectCurrentChannelId, selectChannels } from '../../slices/channelsSlice'; import { selectMessages } from '../../slices/messagesSlice'; -export default function MessageHeader({ filter }) { +const MessageHeader = ({ filter }) => { const channelsList = useSelector(selectChannels); const currentChennelId = useSelector(selectCurrentChannelId); const messages = useSelector(selectMessages); const { t } = useTranslation(); - const currentChennel = channelsList.find((c) => c.id === currentChennelId); - const currentChannelMessages = messages.filter((message) => message.channelId === currentChennelId); - + const currentChennel = channelsList.find((c) => +c.id === +currentChennelId); + const currentChannelMessages = messages + .filter((message) => message.channelId === currentChennelId); + console.log(currentChennel, currentChennelId, channelsList); return (

@@ -26,4 +27,6 @@ export default function MessageHeader({ filter }) {

); -} +}; + +export default MessageHeader; diff --git a/frontend/src/index.js b/frontend/src/index.js index 7303e15..c166ceb 100644 --- a/frontend/src/index.js +++ b/frontend/src/index.js @@ -4,7 +4,7 @@ import ReactDOM from 'react-dom/client'; import { Provider } from 'react-redux'; import { I18nextProvider } from 'react-i18next'; import App from './Components/App'; -import { store } from './store'; +import store from './store'; import i18n from './localization/i18next'; import 'bootstrap/dist/css/bootstrap.min.css'; diff --git a/frontend/src/slices/authSlice.js b/frontend/src/slices/authSlice.js index 0aefef7..a22e36c 100644 --- a/frontend/src/slices/authSlice.js +++ b/frontend/src/slices/authSlice.js @@ -1,3 +1,4 @@ +/* eslint-disable no-param-reassign */ import { createSlice } from '@reduxjs/toolkit'; const getStateFromLocalStorage = () => { diff --git a/frontend/src/slices/channelsSlice.js b/frontend/src/slices/channelsSlice.js index 9dfe3fc..131b472 100644 --- a/frontend/src/slices/channelsSlice.js +++ b/frontend/src/slices/channelsSlice.js @@ -1,3 +1,4 @@ +/* eslint-disable no-param-reassign */ import { createSlice } from '@reduxjs/toolkit'; const initialState = { @@ -26,7 +27,9 @@ const slice = createSlice({ } }, renameChannel: (state, { payload }) => { - state.channelsList = state.channelsList.map((channel) => (channel.id === payload.id ? payload : channel)); + const updatedChannels = state + .channelsList.map((channel) => (channel.id === payload.id ? payload : channel)); + state.channelsList = updatedChannels; }, setCurrentChannelId: (state, { payload }) => { state.currentChannelId = payload; diff --git a/frontend/src/socket.js b/frontend/src/socket.js index 4de0790..2832734 100644 --- a/frontend/src/socket.js +++ b/frontend/src/socket.js @@ -1,7 +1,8 @@ +/* eslint-disable functional/no-let */ import { io } from 'socket.io-client'; import { addChannel, removeChannel, renameChannel } from './slices/channelsSlice'; import { addMessage, editMessage, removeMessage } from './slices/messagesSlice'; -import { store } from './store'; +import store from './store'; let socket; let isSubscribed = false; diff --git a/frontend/src/store.js b/frontend/src/store.js index 8457e7b..b6d4677 100644 --- a/frontend/src/store.js +++ b/frontend/src/store.js @@ -13,7 +13,7 @@ const saveAuthToLocalStorageMiddleware = (store) => (next) => (action) => { return result; }; -const store = configureStore({ +export default configureStore({ reducer: { auth: authReducer, channels: channelsReducer, @@ -23,5 +23,3 @@ const store = configureStore({ middleware: (getDefaultMiddleware) => ( getDefaultMiddleware().concat(saveAuthToLocalStorageMiddleware)), }); - -export { store }; diff --git a/package-lock.json b/package-lock.json index 359fc73..b3e6865 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,15 +49,7 @@ "web-vitals": "^2.1.4", "yup": "^0.32.11" }, - "devDependencies": { - "eslint": "^8.57.0", - "eslint-config-airbnb": "^19.0.4", - "eslint-plugin-functional": "^4.4.1", - "eslint-plugin-import": "^2.26.0", - "eslint-plugin-jsx-a11y": "^6.6.1", - "eslint-plugin-react": "^7.35.0", - "eslint-plugin-react-hooks": "^4.6.2" - } + "devDependencies": {} }, "frontend/node_modules/emittery": { "version": "0.6.0", @@ -6732,15 +6724,6 @@ "node": ">=0.10.0" } }, - "node_modules/deepmerge-ts": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-4.3.0.tgz", - "integrity": "sha512-if3ZYdkD2dClhnXR5reKtG98cwyaRT1NeugQoAPTTfsOpV9kqyeiBF9Qa5RHjemb3KzD5ulqygv6ED3t5j9eJw==", - "dev": true, - "engines": { - "node": ">=12.4.0" - } - }, "node_modules/default-gateway": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", @@ -7661,46 +7644,6 @@ "eslint": "^8.1.0" } }, - "node_modules/eslint-plugin-functional": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-functional/-/eslint-plugin-functional-4.4.1.tgz", - "integrity": "sha512-YhSfHS52Si62Sn126g9wGx+XnWMoWhwEt6ctVXfcJj+xMUiggjOqUVMca7fuLNzX8jYiNBIeU1Y0teHGePZ3NA==", - "dev": true, - "dependencies": { - "@typescript-eslint/utils": "^5.10.2", - "deepmerge-ts": "^4.0.3", - "escape-string-regexp": "^4.0.0", - "semver": "^7.3.7" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^8.0.0", - "tsutils": "^3.0.0", - "typescript": "^3.4.1 || ^4.0.0" - }, - "peerDependenciesMeta": { - "tsutils": { - "optional": true - }, - "typescript": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-functional/node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/eslint-plugin-import": { "version": "2.29.1", "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz",