Skip to content
Merged
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
22 changes: 21 additions & 1 deletion src/app/api/listings/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,29 @@ export async function GET(req: Request) {
.sort({ createdAt: -1 }) // Newest first
.limit(100); // Limit to 100 listings for performance

// For user's own listings, update userDetails with current user data to ensure avatars are up-to-date
let processedListings = listings;

if (queryType === 'mine') {
const currentUser = await User.findById(userId).select('firstName lastName avatar');

if (currentUser) {
processedListings = listings.map(listing => {
const listingObj = listing.toObject();
// Update userDetails with current user data for own listings
listingObj.userDetails = {
firstName: currentUser.firstName,
lastName: currentUser.lastName,
avatar: currentUser.avatar
};
return listingObj;
});
}
}

return NextResponse.json({
success: true,
data: listings
data: processedListings
});
} catch (error) {
console.error('Error fetching listings:', error);
Expand Down
44 changes: 36 additions & 8 deletions src/app/api/matches/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { NextRequest, NextResponse } from 'next/server';
import jwt from 'jsonwebtoken';
import dbConnect from '@/lib/db';
import SkillMatch from '@/lib/models/skillMatch';
import User from '@/lib/models/userSchema';

// Helper function to get user ID from the token
function getUserIdFromToken(req: NextRequest): string | null {
Expand Down Expand Up @@ -67,26 +68,53 @@ export async function GET(request: NextRequest) {
const matches = await SkillMatch.find(query)
.sort({ createdAt: -1 });

// Transform matches to identify the current user's perspective
// Get unique user IDs to fetch current avatar data
const allUserIds = new Set<string>();
matches.forEach(match => {
allUserIds.add(match.userOneId);
allUserIds.add(match.userTwoId);
});

// Fetch current user data for all users involved in matches
const currentUserData = await User.find(
{ _id: { $in: Array.from(allUserIds) } },
'firstName lastName avatar'
).lean();

// Create a map for quick lookup
const userDataMap = new Map();
currentUserData.forEach(user => {
userDataMap.set(user._id.toString(), user);
});

// Transform matches to identify the current user's perspective with updated avatars
const transformedMatches = matches.map(match => {
const isUserOne = match.userOneId === userId;
const otherUserId = isUserOne ? match.userTwoId : match.userOneId;
const otherUserData = userDataMap.get(otherUserId) || {};
const currentUserData = userDataMap.get(userId) || {};

return {
id: match.id,
matchPercentage: match.matchPercentage,
matchType: match.matchType,
status: match.status,
createdAt: match.createdAt,
// Current user's data
myDetails: isUserOne ? match.userOneDetails : match.userTwoDetails,
// Current user's data with updated avatar
myDetails: {
...(isUserOne ? match.userOneDetails : match.userTwoDetails),
firstName: currentUserData.firstName || (isUserOne ? match.userOneDetails.firstName : match.userTwoDetails.firstName),
lastName: currentUserData.lastName || (isUserOne ? match.userOneDetails.lastName : match.userTwoDetails.lastName),
avatar: currentUserData.avatar
},
myListingId: isUserOne ? match.listingOneId : match.listingTwoId,
// Other user's data
// Other user's data with updated avatar
otherUser: {
userId: isUserOne ? match.userTwoId : match.userOneId,
userId: otherUserId,
listingId: isUserOne ? match.listingTwoId : match.listingOneId,
firstName: isUserOne ? match.userTwoDetails.firstName : match.userOneDetails.firstName,
lastName: isUserOne ? match.userTwoDetails.lastName : match.userOneDetails.lastName,
avatar: isUserOne ? match.userTwoDetails.avatar : match.userOneDetails.avatar,
firstName: otherUserData.firstName || (isUserOne ? match.userTwoDetails.firstName : match.userOneDetails.firstName),
lastName: otherUserData.lastName || (isUserOne ? match.userTwoDetails.lastName : match.userOneDetails.lastName),
avatar: otherUserData.avatar,
offeringSkill: isUserOne ? match.userTwoDetails.offeringSkill : match.userOneDetails.offeringSkill,
seekingSkill: isUserOne ? match.userTwoDetails.seekingSkill : match.userOneDetails.seekingSkill
}
Expand Down
34 changes: 32 additions & 2 deletions src/app/user/chat/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,45 @@ function ChatPageContent() {
const [preloadProgress, setPreloadProgress] = useState<{ loaded: number; total: number }>({ loaded: 0, total: 0 });
const [forceRefresh, setForceRefresh] = useState<boolean>(false); // Add state to force refresh

// Check if user came from dashboard (via URL param or recent navigation)
// Check if user came from dashboard and handle auto-selection of chat room
useEffect(() => {
const fromDashboard = searchParams.get('from') === 'dashboard';
const roomId = searchParams.get('roomId');

if (fromDashboard) {
setForceRefresh(true);
// Reset the flag after a short delay to avoid affecting subsequent navigations
setTimeout(() => setForceRefresh(false), 1000);
}
}, [searchParams]);

// Auto-select chat room if roomId is provided
if (roomId && userId) {
// Retry mechanism for newly created chat rooms
const trySelectRoom = async (attempts = 0) => {
const maxAttempts = 3;

try {
// Check if the room exists in user's chat rooms
const chatRooms = await fetchUserChatRooms(userId);
const roomExists = chatRooms.some(room => room._id === roomId);

if (roomExists) {
handleChatSelect(roomId);
} else if (attempts < maxAttempts) {
// Room might still be being created, wait and retry
setTimeout(() => trySelectRoom(attempts + 1), 1000);
} else {
console.warn(`Chat room ${roomId} not found after ${maxAttempts} attempts`);
}
} catch (error) {
console.error('Error finding chat room:', error);
}
};

// Small delay to ensure sidebar is loaded, then try to select room
setTimeout(() => trySelectRoom(), 500);
}
}, [searchParams, userId]);

/**
* * Event Handlers
Expand Down
18 changes: 16 additions & 2 deletions src/components/Dashboard/listings/ListingCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Image from 'next/image';
import { SkillListing } from '@/types/skillListing';
import { BadgeCheck, Edit, Trash2, Eye, Users, Shield, CheckCircle, Clock, XCircle } from 'lucide-react';
import { useAuth } from '@/lib/context/AuthContext';
import { processAvatarUrl } from '@/utils/avatarUtils';

interface ListingCardProps {
listing: SkillListing & {
Expand Down Expand Up @@ -60,6 +61,9 @@ const ListingCard: React.FC<ListingCardProps> = ({ listing, onDelete, onEdit })
const statusConfig = getStatusConfig(listing.status);
const StatusIcon = statusConfig.icon;
const canModify = isOwner && !listing.isUsedInMatches;

// Process avatar URL or provide fallback
const userAvatar = processAvatarUrl(listing.userDetails.avatar) || '/Avatar.png';

return (
<>
Expand All @@ -70,11 +74,16 @@ const ListingCard: React.FC<ListingCardProps> = ({ listing, onDelete, onEdit })
<div className="flex items-center flex-1 min-w-0">
<div className="w-10 h-10 rounded-full overflow-hidden bg-gray-200 mr-3 flex-shrink-0">
<Image
src={'/Avatar.png'}
src={userAvatar}
alt={`${listing.userDetails.firstName} ${listing.userDetails.lastName}`}
width={40}
height={40}
className="object-cover w-full h-full"
onError={(e) => {
const target = e.target as HTMLImageElement;
target.src = '/Avatar.png';
target.onerror = null;
}}
/>
</div>
<div className="flex-1 min-w-0">
Expand Down Expand Up @@ -202,11 +211,16 @@ const ListingCard: React.FC<ListingCardProps> = ({ listing, onDelete, onEdit })
<div className="flex items-center mb-6">
<div className="w-12 h-12 rounded-full overflow-hidden bg-gray-200 mr-4">
<Image
src={'/Avatar.png'}
src={userAvatar}
alt={`${listing.userDetails.firstName} ${listing.userDetails.lastName}`}
width={48}
height={48}
className="object-cover w-full h-full"
onError={(e) => {
const target = e.target as HTMLImageElement;
target.src = '/Avatar.png';
target.onerror = null;
}}
/>
</div>
<div className="flex-1">
Expand Down
Loading
Loading