Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions backend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ GOOGLE_CLIENT_ID=your_google_client_id
VAPID_PUBLIC_KEY=your_vapid_public_key
VAPID_PRIVATE_KEY=your_vapid_private_key
VAPID_SUBJECT=mailto:your@email.com
EMAIL=mailto:your@email.com
EMAIL_PASSWORD=vzktivbgeaqcdczr
64 changes: 64 additions & 0 deletions backend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"dependencies": {
"bcryptjs": "^3.0.3",
"cloudinary": "^2.10.0",
"compression": "^1.8.1",
"cookie-parser": "^1.4.7",
"cors": "^2.8.6",
"dotenv": "^17.4.2",
Expand Down
46 changes: 46 additions & 0 deletions backend/src/controllers/user.controller.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import User from "../models/user.model.js";
import { broadcastStatusMoodUpdate } from "../lib/socket.js";
import { catchAsync } from "../lib/utils.js";
import Message from "../models/message.model.js";
import bcrypt from "bcryptjs"

const ALLOWED_STATUS_MOODS = new Set([
"coding",
Expand Down Expand Up @@ -42,3 +44,47 @@ export const updateStatusMood = catchAsync(async (req, res) => {

res.status(200).json(user);
});


export const deleteProfile = catchAsync(async (req, res) => {
const userId = req.userId;
const { password } = req.body;

const user = await User.findById(userId);

if (!user) {
return res.status(404).json({
success: false,
message: "User not found",
});
}

const isMatch = await bcrypt.compare(password, user.password);

if (!isMatch) {
return res.status(400).json({
success: false,
message: "Incorrect Password",
});
}

// Delete user's messages
await Message.deleteMany({
$or: [
{ senderId: userId },
{ receiverId: userId },
],
});

// Delete user
await User.findByIdAndDelete(userId);

// Logout
res.clearCookie("jwt");
res.clearCookie("XSRF-TOKEN");

return res.status(200).json({
success: true,
message: "Account deleted successfully",
});
});
3 changes: 2 additions & 1 deletion backend/src/routes/user.route.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import express from "express";
import protectRoute from "../middleware/auth.middleware.js";
import { updateStatusMood } from "../controllers/user.controller.js";
import { deleteProfile, updateStatusMood } from "../controllers/user.controller.js";

const router = express.Router();

router.patch("/status-mood", protectRoute, updateStatusMood);
router.delete("/delete-account", protectRoute, deleteProfile);

export default router;
41 changes: 35 additions & 6 deletions frontend/components/chat/MessageBubble.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,37 @@ import Avatar from "./Avatar"
import { useState } from "react"
import ReplyPreview from "./ReplyPreview"

const formatTime = (d) =>
new Date(d).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })
const formatTime = (dateString) => {
const date = new Date(dateString);
const now = new Date();

const diffMs = now - date;
const diffMinutes = Math.floor(diffMs / (1000 * 60));
const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));

if (diffMinutes < 1) return "Just now";

if (diffMinutes < 60)
return `${diffMinutes} minute${diffMinutes > 1 ? "s" : ""} ago`;

if (diffHours < 24)
return `${diffHours} hour${diffHours > 1 ? "s" : ""} ago`;

if (diffDays === 1) return "Yesterday";

if (diffDays < 7) {
return date.toLocaleDateString([], {
weekday: "long",
});
}

return date.toLocaleDateString([], {
month: "short",
day: "numeric",
year: "numeric",
});
};

// Single message bubble with avatar, media, reactions, and read receipts
export default function MessageBubble({ msg, isMine, showTime, selectedUser, isOnline, authUser, onContextMenu, onTouchStart, onTouchEnd, onReact }) {
Expand Down Expand Up @@ -33,10 +62,10 @@ export default function MessageBubble({ msg, isMine, showTime, selectedUser, isO
return (
<div key={msg._id} id={`msg-${msg._id}`}>
{showTime && (
<p className="text-center text-xs text-base-content/30 my-3">
{formatTime(msg.createdAt)}
</p>
)}
<p className="text-center text-xs text-base-content/30 my-3">
{formatTime(msg.createdAt)}
</p>
)}
<div className={`chat w-full ${isMine ? "chat-end" : "chat-start"}`}>
{!isMine && (
<div className="chat-image">
Expand Down
Loading
Loading