diff --git a/client/src/components/Anonymous.jsx b/client/src/components/Anonymous.jsx
index 539aa80d..09e5e2c1 100644
--- a/client/src/components/Anonymous.jsx
+++ b/client/src/components/Anonymous.jsx
@@ -28,6 +28,8 @@ import { createClassesFromArray, isExplicitDisconnection } from 'src/lib/utils';
import useKeyPress, { ShortcutFlags } from 'src/hooks/useKeyPress';
import useCheckTimePassed from 'src/hooks/useCheckTimePassed';
+import { useAuth } from 'src/context/AuthContext';
+import { api } from 'src/lib/axios';
const centerItems = `flex items-center justify-center`;
@@ -36,6 +38,7 @@ const Anonymous = ({
}) => {
const { app, endSearch } = useApp();
+ const { authState } = useAuth()
const { currentChatId, onlineStatus } = app;
const { clearTimer } = useCheckTimePassed();
@@ -54,7 +57,7 @@ const Anonymous = ({
const typingStatusTimeoutRef = useRef(null);
const navigate = useNavigate();
- const { closeChat } = useChat();
+ const { messages: state, closeChat } = useChat();
const { setDialog } = useDialog();
const onDisplay = useCallback(({ isTyping, chatId }) => {
@@ -170,6 +173,59 @@ const Anonymous = ({
});
};
+ const blockUser = async () => {
+ // Get the other user id
+ const chattingPartnersId = state[currentChatId]?.userIds.find(
+ id => id !== authState.loginId && id !== authState.email
+ );
+
+ if (!chattingPartnersId) {
+ return { success: false, message: "could not find user to block" };
+ }
+
+ try {
+ const res = await api.post('/blockUser', {
+ userIdToBlock: chattingPartnersId,
+ currentUserId: authState.loginId
+ });
+
+ if (res.status === 200) {
+ return { success: true };
+ } else {
+ return { success: false, message: "Error reporting user" };
+ }
+ } catch (error) {
+ console.error("Error in reportUser:", error);
+ return { success: false, message: "An unexpected error occurred" };
+ }
+ }
+
+ const handleBlock = async () => {
+ // Check if user have an account i.e. not a anonymous user
+ if(authState.loginType === "anonymous") {
+ setDialog({
+ isOpen: true,
+ text: "You have to create an account first to access this feature!",
+ yesBtnText: "Create an account",
+ noBtnText: "Back to chat",
+ handler: () => navigate("/profile")
+ })
+ return
+ }
+
+ try {
+ const result = await blockUser();
+ if (result.success) {
+ alert('User blocked successfully');
+ closeChatHandler(false)
+ } else {
+ alert(result.message || "Error blocking user. Please try again later.");
+ }
+ } catch (err) {
+ console.error("Error in handleBlock:", err);
+ }
+ }
+
useKeyPress(['x'], () => handleClose(), ShortcutFlags.ctrl | ShortcutFlags.shift);
useKeyPress(['n'], () => handleClose(true), ShortcutFlags.ctrl | ShortcutFlags.alt);
@@ -279,6 +335,11 @@ const Anonymous = ({
Ctrl + Alt + N
+ handleBlock()} className="sm:w-[200px]">
+
+ Block User
+
+
{
);
};
-export default Chat;
+export default Chat;
\ No newline at end of file
diff --git a/server/controllers/userController.js b/server/controllers/userController.js
index 6e5164af..d29bc640 100644
--- a/server/controllers/userController.js
+++ b/server/controllers/userController.js
@@ -11,11 +11,13 @@ const imageUpload = multer({ storage: storage });
const User = require('../models/UserModel');
const { emailValidator, generateObjectId } = require('../utils/helper');
+const { isUserBlocked, blockUser } = require('../utils/lib.js');
const {
OK,
NOT_FOUND,
INTERNAL_SERVER_ERROR,
CONFLICT,
+ BAD_REQUEST,
} = require('../httpStatusCodes.js');
const createUserWithAutoId = async (email) => {
@@ -146,6 +148,27 @@ const deleteUser = async (req, res) => {
}
};
+const blockUserHandler = async (req, res) => {
+ const {userIdToBlock, currentUserId} = req.body
+
+ try {
+ // Check if the user is already blocked
+ if (await isUserBlocked([userIdToBlock, currentUserId])) {
+ return res.status(BAD_REQUEST).json({ message: "This user is already blocked." });
+ }
+
+ // Block the user
+ await blockUser(userIdToBlock, currentUserId)
+
+ res.status(OK).json({ message: "User blocked successfully" });
+ } catch (error) {
+ console.error(error);
+ return res
+ .status(INTERNAL_SERVER_ERROR)
+ .json({ error: 'Internal server error' });
+ }
+}
+
UserRouter.route('/login').post(emailValidator, loginUser);
UserRouter.route('/profile').post(
imageUpload.single('profileImage'),
@@ -155,4 +178,6 @@ UserRouter.route('/profile').post(
UserRouter.route('/profile/:email').get(getProfile);
UserRouter.route('/deleteUser').delete(emailValidator, deleteUser); //Email validation applied to the required request handlers
+UserRouter.route("/blockUser").post(blockUserHandler)
+
module.exports = UserRouter;
diff --git a/server/models/UserModel.js b/server/models/UserModel.js
index 0ba0e6db..7c3cdc64 100644
--- a/server/models/UserModel.js
+++ b/server/models/UserModel.js
@@ -37,6 +37,10 @@ const UserSchema = new Schema(
type: Schema.Types.ObjectId,
ref: 'Chat',
},
+ blockedUsers: {
+ type: [String],
+ default: []
+ }
},
{
timestamps: true,
@@ -63,6 +67,7 @@ const UserSchema = new Schema(
socketIds: [],
currentChatId: this.currentChat?._id?.toString() || null,
chatIds: [],
+ blockedUsers: []
};
},
},
diff --git a/server/sockets/join.js b/server/sockets/join.js
index 66b77fad..b3f75b09 100644
--- a/server/sockets/join.js
+++ b/server/sockets/join.js
@@ -11,6 +11,7 @@ const {
getRandomPairFromWaitingList,
createChat,
getActiveUser,
+ isUserBlocked,
} = require('../utils/lib');
/**
@@ -19,16 +20,24 @@ const {
*
* @param {Server} io
*/
+
const matchMaker = async (io) => {
while (getWaitingUserLen() > 1) {
- const chat = await createChat(getRandomPairFromWaitingList());
+ const users = getRandomPairFromWaitingList();
+
+ // Check if either user is blocked
+ if ( await isUserBlocked(users) ) {
+ continue
+ }
+ const chat = await createChat(users);
io.to(chat.id).emit(NEW_EVENT_JOINED, {
roomId: chat.id,
userIds: chat.userIds,
});
}
};
+
module.exports = (io, socket) => {
socket.on(NEW_EVENT_JOIN, ({ loginId, email }) => {
/**
diff --git a/server/utils/lib.js b/server/utils/lib.js
index 328a5eea..916ad741 100644
--- a/server/utils/lib.js
+++ b/server/utils/lib.js
@@ -5,6 +5,7 @@ const { ObjectId } = require('mongodb');
const ActiveUser = require('../models/UserModel');
const Chat = require('../models/ChatModel');
+const User = require('../models/UserModel');
const Message = require('../models/MessageModel');
const { generateObjectId } = require('./helper');
@@ -502,6 +503,38 @@ function getWaitingUserLen() {
return Object.keys(waitingUsers).length;
}
+async function blockUser(userIdToBlock, currentUserId) {
+ try {
+ await User.findOneAndUpdate({ loginId: currentUserId }, {
+ $addToSet: { blockedUsers: userIdToBlock }
+ });
+ } catch (error) {
+ console.log(`error blocking user: ${error}`);
+ }
+}
+
+async function isUserBlocked(users) {
+ const [userOne, userTwo] = users;
+
+ try {
+ const [userOneData, userTwoData] = await Promise.all([
+ User.findOne({ loginId: userOne.loginId }),
+ User.findOne({ loginId: userTwo.loginId })
+ ]);
+
+ // Using 'OR' because one of the users might be anonymously logged in,
+ // and in such cases, userData could be null.
+ if (userOneData || userTwoData) {
+ const userOneBlocked = userOneData?.blockedUsers.includes(userTwo.loginId);
+ const userTwoBlocked = userTwoData?.blockedUsers.includes(userOne.loginId);
+ return userOneBlocked || userTwoBlocked;
+ }
+ return false;
+ } catch (error) {
+ console.log(`error checking if users are blocked: ${error}`);
+ }
+}
+
module.exports = {
init,
createChat,
@@ -521,4 +554,6 @@ module.exports = {
addToWaitingList,
delActiveUser,
seenMessage,
+ blockUser,
+ isUserBlocked
};