Skip to content

Commit

Permalink
Add interaction with server on channel renaming + modal ux improve
Browse files Browse the repository at this point in the history
1. Add renaming channel fuction in socket
2. Modal emitting on server event of renaming
3. Validating new name: it must be unique
4. Improved usability: if validating failed, input will be focused again
  • Loading branch information
SamIvan-ark committed Dec 20, 2023
1 parent a153f86 commit 45cee6a
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 15 deletions.
11 changes: 11 additions & 0 deletions frontend/src/components/chat/Chat.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { addMessage } from '../../slices/messagesSlice';
import {
addChannel,
removeChannel,
renameChannel,
setNeedToMove,
setActive,
} from '../../slices/channelsSlice';
Expand All @@ -19,9 +20,19 @@ const Chat = () => {

useEffect(() => {
const onNewMessage = (message) => dispatch(addMessage(message));
const onRenamingChannel = ({ id, name }) => {
dispatch(renameChannel({
id,
changes: {
name,
},
}));
};
socket.on('newMessage', onNewMessage);
socket.on('renameChannel', onRenamingChannel);
return () => {
socket.off('newMessage', onNewMessage);
socket.off('renameChannel', onRenamingChannel);
};
}, []);

Expand Down
11 changes: 9 additions & 2 deletions frontend/src/components/modals/AddChannelModal.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useRef, useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { Modal, Form } from 'react-bootstrap';
import { useFormik } from 'formik';

import { closeModal } from '../../slices/modalsSlice';
import { setNeedToMove } from '../../slices/channelsSlice';
import { addChannel } from '../../socket';
Expand Down Expand Up @@ -34,6 +34,12 @@ const AddChannel = () => {
},
});

const inputRef = useRef();

useEffect(() => {
inputRef.current.focus();
}, [formik.errors.channelName]);

return (
<Modal show onHide={() => handleClose()}>
<Modal.Header closeButton>
Expand All @@ -46,9 +52,10 @@ const AddChannel = () => {
Имя канала
</Form.Label>
<Form.Control
ref={inputRef}
isInvalid={formik.errors.channelName && formik.touched.channelName}
disabled={formik.isSubmitting}
className="form-control"
autoFocus
required
id="channelName"
name="channelName"
Expand Down
37 changes: 25 additions & 12 deletions frontend/src/components/modals/RenameChannelModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,38 @@ import { useFormik } from 'formik';
import { useDispatch, useSelector } from 'react-redux';

import { closeModal } from '../../slices/modalsSlice';
import { renameChannel } from '../../slices/channelsSlice';
import { renameChannel } from '../../socket';

const RenameChannelModal = () => {
const allChannels = useSelector((state) => state.channels.entities);
const takenNames = Object.values(allChannels).map(({ name }) => name);
const dispatch = useDispatch();
const { invokedOn } = useSelector((state) => state.modals);
const { name } = useSelector((state) => state
.channels
.entities[invokedOn]);

const handleClose = () => dispatch(closeModal());
const handleSubmit = ({ name: newName, id }) => {
dispatch(renameChannel({
id,
changes: {
name: newName,
},
}));
handleClose();
};

const formik = useFormik({
initialValues: {
newNameOfChannel: name,
},
onSubmit: ({ newNameOfChannel }) => {
handleSubmit({ name: newNameOfChannel, id: invokedOn });
if (takenNames.includes(newNameOfChannel)) {
formik.setErrors({ newNameOfChannel: 'Канал с таким именем уже существует' });
formik.setSubmitting(false);
return;
}
formik.setErrors({});
renameChannel(
{ name: newNameOfChannel, id: invokedOn },
({ status }) => {
if (status === 'ok') {
handleClose();
}
},
);
},
});

Expand All @@ -39,6 +45,10 @@ const RenameChannelModal = () => {
inputRef.current.select();
}, []);

useEffect(() => {
inputRef.current.focus();
}, [formik.errors.newNameOfChannel]);

return (
<Modal show onHide={() => handleClose()}>
<Modal.Header closeButton>
Expand All @@ -51,6 +61,8 @@ const RenameChannelModal = () => {
Новое имя канала:
</Form.Label>
<Form.Control
isInvalid={formik.errors.newNameOfChannel && formik.touched.newNameOfChannel}
disabled={formik.isSubmitting}
ref={inputRef}
className="form-control"
selected
Expand All @@ -61,9 +73,10 @@ const RenameChannelModal = () => {
onChange={formik.handleChange}
value={formik.values.newNameOfChannel}
/>
{formik.errors.newNameOfChannel ? <div className="text-danger">{formik.errors.newNameOfChannel}</div> : null}
<div className="d-flex justify-content-end">
<Form.Control onClick={() => handleClose()} className="me-2 btn btn-secondary" type="button" value="Отменить" />
<Form.Control className="btn btn-primary" type="submit" value="Отправить" />
<Form.Control disabled={formik.isSubmitting} className="btn btn-primary" type="submit" value="Отправить" />
</div>
</Form.Group>
</Form>
Expand Down
10 changes: 9 additions & 1 deletion frontend/src/socket.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@ const socketEventsNames = {
NEW_MESSAGE: 'newMessage',
NEW_CHANNEL: 'newChannel',
REMOVE_CHANNEL: 'removeChannel',
RENAME_CHANNEL: 'renameChannel',
};

const socket = io({
autoConnect: false,
});

socket.on('connect', () => console.log('connected'));

// TODO: а как лучше — оставить так, что на каждое событие своя функция,
// или сделать одну функцию на все события сразу?
// Если одна абстракция лучше, где хранить все эти красивые имена событий?
// С одной стороны, сейчас достаточно удобно сделано, с другой — много дублирования
export const sendMessage = (data, cb = null) => {
socket.emit(socketEventsNames.NEW_MESSAGE, data, cb);
};
Expand All @@ -24,4 +28,8 @@ export const removeChannel = (data, cb = null) => {
socket.emit(socketEventsNames.REMOVE_CHANNEL, data, cb);
};

export const renameChannel = (data, cb = null) => {
socket.emit(socketEventsNames.RENAME_CHANNEL, data, cb);
};

export default socket;

0 comments on commit 45cee6a

Please sign in to comment.