Skip to content

Commit 0c37056

Browse files
authored
feat: implement block users (#684)
1 parent 1081b93 commit 0c37056

6 files changed

Lines changed: 138 additions & 3 deletions

File tree

client/src/components/Anonymous.jsx

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import { createClassesFromArray, isExplicitDisconnection } from 'src/lib/utils';
2828

2929
import useKeyPress, { ShortcutFlags } from 'src/hooks/useKeyPress';
3030
import useCheckTimePassed from 'src/hooks/useCheckTimePassed';
31+
import { useAuth } from 'src/context/AuthContext';
32+
import { api } from 'src/lib/axios';
3133

3234
const centerItems = `flex items-center justify-center`;
3335

@@ -36,6 +38,7 @@ const Anonymous = ({
3638

3739
}) => {
3840
const { app, endSearch } = useApp();
41+
const { authState } = useAuth()
3942
const { currentChatId, onlineStatus } = app;
4043
const { clearTimer } = useCheckTimePassed();
4144

@@ -54,7 +57,7 @@ const Anonymous = ({
5457
const typingStatusTimeoutRef = useRef(null);
5558

5659
const navigate = useNavigate();
57-
const { closeChat } = useChat();
60+
const { messages: state, closeChat } = useChat();
5861
const { setDialog } = useDialog();
5962

6063
const onDisplay = useCallback(({ isTyping, chatId }) => {
@@ -170,6 +173,59 @@ const Anonymous = ({
170173
});
171174
};
172175

176+
const blockUser = async () => {
177+
// Get the other user id
178+
const chattingPartnersId = state[currentChatId]?.userIds.find(
179+
id => id !== authState.loginId && id !== authState.email
180+
);
181+
182+
if (!chattingPartnersId) {
183+
return { success: false, message: "could not find user to block" };
184+
}
185+
186+
try {
187+
const res = await api.post('/blockUser', {
188+
userIdToBlock: chattingPartnersId,
189+
currentUserId: authState.loginId
190+
});
191+
192+
if (res.status === 200) {
193+
return { success: true };
194+
} else {
195+
return { success: false, message: "Error reporting user" };
196+
}
197+
} catch (error) {
198+
console.error("Error in reportUser:", error);
199+
return { success: false, message: "An unexpected error occurred" };
200+
}
201+
}
202+
203+
const handleBlock = async () => {
204+
// Check if user have an account i.e. not a anonymous user
205+
if(authState.loginType === "anonymous") {
206+
setDialog({
207+
isOpen: true,
208+
text: "You have to create an account first to access this feature!",
209+
yesBtnText: "Create an account",
210+
noBtnText: "Back to chat",
211+
handler: () => navigate("/profile")
212+
})
213+
return
214+
}
215+
216+
try {
217+
const result = await blockUser();
218+
if (result.success) {
219+
alert('User blocked successfully');
220+
closeChatHandler(false)
221+
} else {
222+
alert(result.message || "Error blocking user. Please try again later.");
223+
}
224+
} catch (err) {
225+
console.error("Error in handleBlock:", err);
226+
}
227+
}
228+
173229
useKeyPress(['x'], () => handleClose(), ShortcutFlags.ctrl | ShortcutFlags.shift);
174230
useKeyPress(['n'], () => handleClose(true), ShortcutFlags.ctrl | ShortcutFlags.alt);
175231

@@ -279,6 +335,11 @@ const Anonymous = ({
279335
<span className="text-gray-500 text-xs">Ctrl + Alt + N</span>
280336
</div>
281337
</Dropdown.Item>
338+
<Dropdown.Item onClick={() => handleBlock()} className="sm:w-[200px]">
339+
<div className="flex items-center justify-between gap-2 flex-wrap">
340+
<span className='text-red'>Block User</span>
341+
</div>
342+
</Dropdown.Item>
282343
</Dropdown>
283344
</div>
284345
<div

client/src/components/Chat.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -607,4 +607,4 @@ const Chat = () => {
607607
);
608608
};
609609

610-
export default Chat;
610+
export default Chat;

server/controllers/userController.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@ const imageUpload = multer({ storage: storage });
1111
const User = require('../models/UserModel');
1212
const { emailValidator, generateObjectId } = require('../utils/helper');
1313

14+
const { isUserBlocked, blockUser } = require('../utils/lib.js');
1415
const {
1516
OK,
1617
NOT_FOUND,
1718
INTERNAL_SERVER_ERROR,
1819
CONFLICT,
20+
BAD_REQUEST,
1921
} = require('../httpStatusCodes.js');
2022

2123
const createUserWithAutoId = async (email) => {
@@ -146,6 +148,27 @@ const deleteUser = async (req, res) => {
146148
}
147149
};
148150

151+
const blockUserHandler = async (req, res) => {
152+
const {userIdToBlock, currentUserId} = req.body
153+
154+
try {
155+
// Check if the user is already blocked
156+
if (await isUserBlocked([userIdToBlock, currentUserId])) {
157+
return res.status(BAD_REQUEST).json({ message: "This user is already blocked." });
158+
}
159+
160+
// Block the user
161+
await blockUser(userIdToBlock, currentUserId)
162+
163+
res.status(OK).json({ message: "User blocked successfully" });
164+
} catch (error) {
165+
console.error(error);
166+
return res
167+
.status(INTERNAL_SERVER_ERROR)
168+
.json({ error: 'Internal server error' });
169+
}
170+
}
171+
149172
UserRouter.route('/login').post(emailValidator, loginUser);
150173
UserRouter.route('/profile').post(
151174
imageUpload.single('profileImage'),
@@ -155,4 +178,6 @@ UserRouter.route('/profile').post(
155178
UserRouter.route('/profile/:email').get(getProfile);
156179
UserRouter.route('/deleteUser').delete(emailValidator, deleteUser); //Email validation applied to the required request handlers
157180

181+
UserRouter.route("/blockUser").post(blockUserHandler)
182+
158183
module.exports = UserRouter;

server/models/UserModel.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ const UserSchema = new Schema(
3737
type: Schema.Types.ObjectId,
3838
ref: 'Chat',
3939
},
40+
blockedUsers: {
41+
type: [String],
42+
default: []
43+
}
4044
},
4145
{
4246
timestamps: true,
@@ -63,6 +67,7 @@ const UserSchema = new Schema(
6367
socketIds: [],
6468
currentChatId: this.currentChat?._id?.toString() || null,
6569
chatIds: [],
70+
blockedUsers: []
6671
};
6772
},
6873
},

server/sockets/join.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const {
1111
getRandomPairFromWaitingList,
1212
createChat,
1313
getActiveUser,
14+
isUserBlocked,
1415
} = require('../utils/lib');
1516

1617
/**
@@ -19,16 +20,24 @@ const {
1920
*
2021
* @param {Server} io
2122
*/
23+
2224
const matchMaker = async (io) => {
2325
while (getWaitingUserLen() > 1) {
24-
const chat = await createChat(getRandomPairFromWaitingList());
26+
const users = getRandomPairFromWaitingList();
27+
28+
// Check if either user is blocked
29+
if ( await isUserBlocked(users) ) {
30+
continue
31+
}
2532

33+
const chat = await createChat(users);
2634
io.to(chat.id).emit(NEW_EVENT_JOINED, {
2735
roomId: chat.id,
2836
userIds: chat.userIds,
2937
});
3038
}
3139
};
40+
3241
module.exports = (io, socket) => {
3342
socket.on(NEW_EVENT_JOIN, ({ loginId, email }) => {
3443
/**

server/utils/lib.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const { ObjectId } = require('mongodb');
55

66
const ActiveUser = require('../models/UserModel');
77
const Chat = require('../models/ChatModel');
8+
const User = require('../models/UserModel');
89
const Message = require('../models/MessageModel');
910
const { generateObjectId } = require('./helper');
1011

@@ -502,6 +503,38 @@ function getWaitingUserLen() {
502503
return Object.keys(waitingUsers).length;
503504
}
504505

506+
async function blockUser(userIdToBlock, currentUserId) {
507+
try {
508+
await User.findOneAndUpdate({ loginId: currentUserId }, {
509+
$addToSet: { blockedUsers: userIdToBlock }
510+
});
511+
} catch (error) {
512+
console.log(`error blocking user: ${error}`);
513+
}
514+
}
515+
516+
async function isUserBlocked(users) {
517+
const [userOne, userTwo] = users;
518+
519+
try {
520+
const [userOneData, userTwoData] = await Promise.all([
521+
User.findOne({ loginId: userOne.loginId }),
522+
User.findOne({ loginId: userTwo.loginId })
523+
]);
524+
525+
// Using 'OR' because one of the users might be anonymously logged in,
526+
// and in such cases, userData could be null.
527+
if (userOneData || userTwoData) {
528+
const userOneBlocked = userOneData?.blockedUsers.includes(userTwo.loginId);
529+
const userTwoBlocked = userTwoData?.blockedUsers.includes(userOne.loginId);
530+
return userOneBlocked || userTwoBlocked;
531+
}
532+
return false;
533+
} catch (error) {
534+
console.log(`error checking if users are blocked: ${error}`);
535+
}
536+
}
537+
505538
module.exports = {
506539
init,
507540
createChat,
@@ -521,4 +554,6 @@ module.exports = {
521554
addToWaitingList,
522555
delActiveUser,
523556
seenMessage,
557+
blockUser,
558+
isUserBlocked
524559
};

0 commit comments

Comments
 (0)