diff --git a/client/html/pages/games/games.html b/client/html/pages/games/games.html
index 8728818..9f6125a 100644
--- a/client/html/pages/games/games.html
+++ b/client/html/pages/games/games.html
@@ -43,15 +43,6 @@
-
-
-
${type.charAt(0).toUpperCase() + type.slice(1)}
-
-
-
-
${item.Name}
-
+ const itemHtml = `
+
+
+
+
${type.charAt(0).toUpperCase() + type.slice(1)}
+
+
+
+
${item.Name}
+
+
-
- `;
-
- container.append(itemHtml);
+ `;
+ container.append(itemHtml);
}
}
@@ -310,62 +353,136 @@ function updateWearButton(type, itemId, isWearing) {
function loadUserAvatar() {
const token = localStorage.getItem('token');
- console.log("hahaahsha");
+ if (!token) {
+ console.error('No token found');
+ return;
+ }
+
$.ajax({
url: '/api/avatar',
method: 'GET',
headers: {
Authorization: `Bearer ${token}`,
+ 'Cache-Control': 'no-cache'
},
- success: function (avatar) {
- if (avatar.shirt && avatar.shirt._id) {
- wearItem('shirt', avatar.shirtId);
- }
+ success: function (response) {
+ console.log('Avatar data received:', response);
+ if (response && response.avatar) {
+ // Update the avatar display
+ updateAvatarDisplay('shirt', response.avatar.shirt);
+ updateCurrentlyWearing('shirt', response.avatar.shirt);
+
+ if (response.avatar.shirt) {
+ updateWearButton('shirt', response.avatar.shirt._id, true);
+ // Store the current state in localStorage
+ localStorage.setItem('currentAvatar', JSON.stringify(response.avatar));
+ }
+ }
+
+ // Load and display the rendered avatar
+ if (response.avatarRender && response.avatarRender.shirt) {
+ $('#avatar-display').attr('src', response.avatarRender.shirt)
+ .removeClass('hidden')
+ .css({
+ 'max-width': '100%',
+ 'height': 'auto',
+ 'display': 'block',
+ 'margin': '0 auto'
+ });
+ localStorage.setItem('avatarRender', JSON.stringify(response.avatarRender));
+ }
},
error: function (xhr, status, error) {
- console.error('Error loading avatar:', error);
- $('#avatar-display').html('
Error loading avatar. Please try again later.
');
- },
+ console.error('Error loading avatar:', {
+ error: error,
+ status: status,
+ response: xhr.responseText
+ });
+ showAlert('danger', 'Error loading avatar. Please try again later.');
+ }
});
}
function saveAvatarSelection(type, itemId) {
const token = localStorage.getItem('token');
const avatarData = { type, itemId };
- console.log(`Saving avatar selection: ${type}, ID: ${itemId}`);
+ console.log('Saving avatar selection:', avatarData);
$.ajax({
url: '/api/avatar',
method: 'PUT',
- contentType: 'application/json',
headers: {
Authorization: `Bearer ${token}`,
- 'Content-Type': 'application/json',
+ 'Content-Type': 'application/json'
},
data: JSON.stringify(avatarData),
success: function (response) {
- console.log('Avatar updated successfully:', response);
- if (itemId) {
- updateAvatarDisplay(type, response.avatar[type]);
- updateCurrentlyWearing(type, response.avatar[type]);
- updateWearButton(type, itemId, true);
- showAlert('success', `Wore your ${type} successfully.`);
- } else {
- $(`#avatar-${type}`).attr('src', '');
- $(`#currently-wearing [data-type="${type}"]`).remove();
- updateWearButton(type, null, false);
- showAlert('info', `Unwore your ${type}.`);
+ console.log('Avatar update response:', response);
+
+ if (response.avatar && response.avatarRender) {
+ // Store the updated state
+ localStorage.setItem('currentAvatar', JSON.stringify(response.avatar));
+ localStorage.setItem('avatarRender', JSON.stringify(response.avatarRender));
+
+ if (itemId) {
+ updateAvatarDisplay(type, response.avatar[type]);
+ updateCurrentlyWearing(type, response.avatar[type]);
+ updateWearButton(type, itemId, true);
+
+ // Update the rendered avatar display
+ if (response.avatarRender.shirt) {
+ $('#avatar-display').attr('src', response.avatarRender.shirt)
+ .removeClass('hidden');
+ }
+ showAlert('success', `Wore your ${type} successfully.`);
+ } else {
+ $(`#avatar-${type}`).attr('src', '');
+ $(`#currently-wearing [data-type="${type}"]`).remove();
+ updateWearButton(type, null, false);
+ $('#avatar-display').addClass('hidden');
+ showAlert('info', `Unwore your ${type}.`);
+ }
}
},
error: function (xhr, status, error) {
- console.error('Error updating avatar:', error);
- console.error('Status:', status);
- console.error('Response:', xhr.responseText);
+ console.error('Error updating avatar:', {
+ error: error,
+ status: status,
+ response: xhr.responseText
+ });
showAlert('danger', `Error ${itemId ? 'wearing' : 'unwearing'} ${type}. Please try again later.`);
- },
+ }
});
}
+function restoreAvatarState() {
+ const savedAvatar = localStorage.getItem('currentAvatar');
+ const savedRender = localStorage.getItem('avatarRender');
+
+ if (savedAvatar) {
+ const avatar = JSON.parse(savedAvatar);
+ if (avatar.shirt) {
+ updateAvatarDisplay('shirt', avatar.shirt);
+ updateCurrentlyWearing('shirt', avatar.shirt);
+ updateWearButton('shirt', avatar.shirt._id, true);
+ }
+ }
+
+ if (savedRender) {
+ const render = JSON.parse(savedRender);
+ if (render.shirt) {
+ $('#avatar-display').attr('src', render.shirt)
+ .removeClass('hidden')
+ .css({
+ 'max-width': '100%',
+ 'height': 'auto',
+ 'display': 'block',
+ 'margin': '0 auto'
+ });
+ }
+ }
+}
+
// basic body colors set up for seven
function setupBodyColors() {
const bodyParts = ['head', 'torso', 'leftArm', 'rightArm', 'leftLeg', 'rightLeg'];
@@ -409,3 +526,41 @@ function showAlert(type, message) {
}
+function loadRenderedAvatar() {
+ const token = localStorage.getItem('token');
+
+ $.ajax({
+ url: '/api/avatar/render',
+ method: 'GET',
+ headers: {
+ Authorization: `Bearer ${token}`,
+ 'Cache-Control': 'no-cache'
+ },
+ success: function(response) {
+ if (response && response.avatarRender) {
+ if (response.avatarRender.shirt) {
+ $('#avatar-shirt').attr('src', response.avatarRender.shirt)
+ .removeClass('hidden')
+ .css({
+ 'max-width': '100%',
+ 'height': 'auto',
+ 'display': 'block',
+ 'margin': '0 auto'
+ });
+ }
+ }
+ },
+ error: function(xhr, status, error) {
+ console.error('Error loading rendered avatar:', error);
+ }
+ });
+}
+
+// Update the document ready function
+$(document).ready(function () {
+ initializeAvatarEditor();
+ loadUserAvatar();
+ loadRenderedAvatar();
+ setupItemSelection();
+ restoreAvatarState();
+});
\ No newline at end of file
diff --git a/client/js/home.js b/client/js/home.js
index bef2db0..be88f7a 100644
--- a/client/js/home.js
+++ b/client/js/home.js
@@ -204,7 +204,7 @@ $(document).ready(function () {
Discord
Join our Discord server and talk to people in our community!
-
Join Discord
+
Join Discord
@@ -222,7 +222,7 @@ $(document).ready(function () {
Error fetching user profile. Please try again.
');
+ },
});
- }
+}
function fetchUserStatus(username) {
return new Promise((resolve, reject) => {
@@ -86,6 +77,11 @@ $(document).ready(function () {
}
function displayUserProfile(user) {
+ console.log('Displaying profile for user:', {
+ userId: user.userId,
+ hasAvatarRender: !!user.avatarRender,
+ avatarRenderDetails: user.avatarRender
+ });
const isOwnProfile = user.username === localStorage.getItem('username');
let actionButton = '';
let onlineStatus = user.isOnline
@@ -119,40 +115,31 @@ $(document).ready(function () {
@@ -575,6 +562,49 @@ $(document).ready(function () {
});
}
+
+
+
+ function loadUserAvatar(userId) {
+ if (!userId) {
+ console.error('No userId provided to loadUserAvatar');
+ return;
+ }
+
+ const token = localStorage.getItem('token');
+ $.ajax({
+ url: `/api/avatar/render/${userId}`,
+ method: 'GET',
+ headers: {
+ Authorization: `Bearer ${token}`
+ },
+ success: function(response) {
+ console.log('Avatar response:', response);
+ if (response && response.avatarRender && response.avatarRender.shirt) {
+ $('#profile-avatar').html(`
+
+ `);
+ } else {
+ console.warn('No avatar render data found');
+ $('#profile-avatar').html('
No avatar available
');
+ }
+ },
+ error: function(xhr, status, error) {
+ console.error('Error loading user avatar:', {
+ status: status,
+ error: error,
+ response: xhr.responseText,
+ userId: userId
+ });
+ $('#profile-avatar').html('
Error loading avatar
');
+ }
+ });
+}
+
function displayUserShirts(shirts, userId) {
const shirtsContainer = $('#user-shirts');
shirtsContainer.empty();
diff --git a/server/functions/api/models/User.js b/server/functions/api/models/User.js
index a056dd1..3ee2adc 100644
--- a/server/functions/api/models/User.js
+++ b/server/functions/api/models/User.js
@@ -161,6 +161,18 @@ const userSchema = new mongoose.Schema({
ref: 'Asset',
},
],
+
+ avatarRender: {
+ shirt: {
+ type: String,
+ default: null
+ },
+ lastUpdated: {
+ type: Date,
+ default: Date.now
+ }
+ }
+
});
userSchema.pre('save', async function (next) {
diff --git a/server/functions/api/routes/avatar.js b/server/functions/api/routes/avatar.js
index 75e9698..7c32fd4 100644
--- a/server/functions/api/routes/avatar.js
+++ b/server/functions/api/routes/avatar.js
@@ -11,91 +11,212 @@ router.get('/', authenticateToken, async (req, res) => {
res.set('Pragma', 'no-cache');
res.set('Expires', '0');
try {
- console.log('Fetching avatar for userId:', req.user.userId);
- const user = await User.findOne({ userId: req.user.userId }).select('avatar');
+ console.log('Fetching avatar and render data for userId:', req.user.userId);
+ const user = await User.findOne({ userId: req.user.userId })
+ .select('avatar avatarRender')
+ .populate('avatar.shirt');
if (!user) {
- console.error('User not found for userId:', req.user.userId);
+ console.error('User not found:', { userId: req.user.userId });
return res.status(404).json({ error: 'User not found' });
}
- console.log('Avatar fetched successfully:', user.avatar);
- res.json(user.avatar);
+ console.log('Avatar data fetched:', {
+ userId: req.user.userId,
+ hasAvatar: !!user.avatar,
+ avatarDetails: user.avatar,
+ hasAvatarRender: !!user.avatarRender,
+ renderDetails: user.avatarRender
+ });
+
+ res.json({
+ avatar: user.avatar,
+ avatarRender: user.avatarRender
+ });
} catch (error) {
- console.error('Error fetching avatar:', error);
+ console.error('Error fetching avatar data:', {
+ error: error.message,
+ stack: error.stack,
+ userId: req.user.userId
+ });
res.status(500).json({ error: 'Internal server error', details: error.message });
}
});
-// Update avatar
-router.put('/', authenticateToken, async (req, res) => {
+router.get('/render/:userId', async (req, res) => {
try {
- const { type, itemId } = req.body;
- console.log('Updating avatar:', { userId: req.user.userId, type, itemId });
-
- if (!type || !['shirt', 'pants', 'hat'].includes(type)) {
- console.error('Invalid or missing item type:', type);
- return res.status(400).json({ error: 'Invalid or missing item type' });
+ const user = await User.findOne({ userId: parseInt(req.params.userId) });
+
+ if (!user) {
+ console.log('User not found for avatar render:', req.params.userId);
+ return res.status(404).json({ error: 'User not found' });
}
- const user = await User.findOne({ userId: req.user.userId }).populate('inventory');
+ // Return both the shirt URL and display URL
+ res.json({
+ avatarRender: {
+ ...user.avatarRender,
+ displayUrl: user.avatarRender?.displayUrl || user.avatarRender?.shirt
+ }
+ });
+ } catch (error) {
+ console.error('Error fetching avatar render:', {
+ error: error.message,
+ stack: error.stack,
+ userId: req.params.userId
+ });
+ res.status(500).json({
+ error: 'Internal server error',
+ details: error.message
+ });
+ }
+});
+
+
+
+router.post('/render', authenticateToken, async (req, res) => {
+ try {
+ const { shirt, displayUrl } = req.body;
+ const user = await User.findOne({ userId: req.user.userId });
if (!user) {
- console.error('User not found for userId:', req.user.userId);
+ console.log('User not found:', req.user.userId);
return res.status(404).json({ error: 'User not found' });
}
- if (!user.avatar) {
- user.avatar = {};
+ if (!user.avatarRender) {
+ user.avatarRender = {};
}
- console.log('User inventory:', user.inventory.map(item => ({ id: item._id.toString(), type: item.AssetType })));
+ // Update the render data with both the shirt URL and display URL
+ user.avatarRender = {
+ ...user.avatarRender,
+ shirt,
+ displayUrl: displayUrl || shirt, // Use displayUrl if provided, otherwise use shirt URL
+ lastUpdated: new Date()
+ };
+ await user.save();
+ console.log('Avatar render saved:', user.avatarRender);
- switch (type) {
- case 'shirt':
- if (itemId) {
- if (!mongoose.Types.ObjectId.isValid(itemId)) {
- console.error('Invalid shirt ID:', itemId);
- return res.status(400).json({ error: 'Invalid shirt ID' });
- }
+ res.json({
+ message: 'Avatar render saved successfully',
+ avatarRender: user.avatarRender
+ });
+ } catch (error) {
+ console.error('Error saving avatar render:', {
+ error: error.message,
+ stack: error.stack,
+ userId: req.user.userId
+ });
+ res.status(500).json({
+ error: 'Internal server error',
+ details: error.message
+ });
+ }
+});
+
+// Update avatar
+router.put('/', authenticateToken, async (req, res) => {
+ try {
+ const { type, itemId } = req.body;
+ console.log('Updating avatar:', {
+ userId: req.user.userId,
+ type,
+ itemId,
+ timestamp: new Date().toISOString()
+ });
- console.log('Checking inventory for shirt:', itemId);
+ const user = await User.findOne({ userId: req.user.userId })
+ .populate('inventory')
+ .populate('avatar.shirt');
+
+ if (!user) {
+ console.error('User not found:', { userId: req.user.userId });
+ return res.status(404).json({ error: 'User not found' });
+ }
- const inventoryItem = user.inventory.find(item =>
- item._id.toString() === itemId && item.AssetType === 'Shirt'
- );
+ if (!user.avatar) user.avatar = {};
+ if (!user.avatarRender) user.avatarRender = {};
- const createdShirt = await Asset.findOne({
+ switch (type) {
+ case 'shirt':
+ if (itemId) {
+ const shirtAsset = await Asset.findOne({
_id: itemId,
- creator: user._id,
- AssetType: 'Shirt'
- }).populate('creator', 'username');
+ AssetType: 'Shirt',
+ $or: [
+ { _id: { $in: user.inventory } },
+ { creator: user._id }
+ ]
+ });
- if (!inventoryItem && !createdShirt) {
- console.error('Shirt not in user inventory or not created by user:', itemId);
- return res.status(400).json({ error: 'Shirt not in user inventory or not created by u' });
+ if (!shirtAsset) {
+ console.error('Shirt not accessible:', {
+ userId: req.user.userId,
+ shirtId: itemId
+ });
+ return res.status(400).json({
+ error: 'Shirt not in inventory or not created by user'
+ });
}
- console.log('Setting shirt:', itemId);
- user.avatar.shirt = createdShirt || inventoryItem;
+ console.log('Wearing shirt:', {
+ userId: req.user.userId,
+ shirtId: itemId,
+ shirtDetails: {
+ name: shirtAsset.Name,
+ imageUrl: shirtAsset.imageUrl
+ }
+ });
+
+ user.avatar.shirt = shirtAsset;
+ user.avatarRender = {
+ ...user.avatarRender,
+ shirt: shirtAsset.imageUrl,
+ displayUrl: shirtAsset.imageUrl,
+ lastUpdated: new Date()
+ };
} else {
- console.log('Unwearing shirt');
+ console.log('Removing shirt from avatar:', {
+ userId: req.user.userId,
+ previousShirt: user.avatar.shirt
+ });
user.avatar.shirt = null;
+ user.avatarRender = {
+ ...user.avatarRender,
+ shirt: null,
+ displayUrl: null,
+ lastUpdated: new Date()
+ };
}
break;
- // cases for pants and hats can be added here later
- default:
- console.error('Unsupported item type:', type);
- return res.status(400).json({ error: 'Unsupported item type' });
}
await user.save();
- console.log('Avatar updated successfully for userId:', req.user.userId, 'New avatar:', user.avatar);
- res.json({ message: 'Avatar updated successfully', avatar: user.avatar });
+ console.log('Avatar update successful:', {
+ userId: req.user.userId,
+ newAvatarState: user.avatar,
+ newRenderState: user.avatarRender,
+ timestamp: new Date().toISOString()
+ });
+
+ res.json({
+ message: 'Avatar updated successfully',
+ avatar: user.avatar,
+ avatarRender: user.avatarRender
+ });
} catch (error) {
- console.error('Error updating avatar:', error);
- res.status(500).json({ error: 'Internal server error', details: error.message, stack: error.stack });
+ console.error('Avatar update failed:', {
+ error: error.message,
+ stack: error.stack,
+ userId: req.user.userId,
+ requestBody: req.body
+ });
+ res.status(500).json({
+ error: 'Internal server error',
+ details: error.message
+ });
}
});
diff --git a/server/functions/api/routes/user.js b/server/functions/api/routes/user.js
index 208f99b..07f9efd 100644
--- a/server/functions/api/routes/user.js
+++ b/server/functions/api/routes/user.js
@@ -7,33 +7,62 @@ const User = require('../models/User');
router.get('/user/:username', authenticateToken, async (req, res) => {
try {
const { username } = req.params;
+ console.log('Fetching profile for username:', username);
+
const currentUser = await User.findOne({ userId: req.user.userId });
- const user = await User.findOne({ username }).select(
- 'username userId signupDate lastLoggedIn blurb friendRequests friends sentFriendRequests'
- );
+ if (!currentUser) {
+ console.error('Current user not found:', req.user.userId);
+ return res.status(404).json({ error: 'Current user not found' });
+ }
+
+ const user = await User.findOne({ username })
+ .select('username userId signupDate lastLoggedIn blurb friendRequests friends sentFriendRequests avatarRender')
+ .lean();
if (!user) {
+ console.error('Target user not found:', username);
return res.status(404).json({ error: 'User not found' });
}
+ console.log('User found:', {
+ userId: user.userId,
+ hasAvatarRender: !!user.avatarRender,
+ avatarRenderDetails: user.avatarRender
+ });
+
const isFriend = currentUser.friends.includes(user.userId);
const friendRequestSent = user.friendRequests.includes(currentUser.userId);
const friendRequestReceived = currentUser.friendRequests.includes(user.userId);
- const userObject = user.toObject();
- delete userObject.friendRequests;
- delete userObject.friends;
- delete userObject.sentFriendRequests;
+ delete user.friendRequests;
+ delete user.friends;
+ delete user.sentFriendRequests;
- res.json({
- ...userObject,
+ const responseData = {
+ ...user,
isFriend,
friendRequestSent,
friendRequestReceived,
+ };
+
+ console.log('Sending profile response:', {
+ userId: responseData.userId,
+ hasAvatarRender: !!responseData.avatarRender,
+ avatarRenderDetails: responseData.avatarRender
});
+
+ res.json(responseData);
} catch (error) {
- console.error('User profile error:', error);
- res.status(500).json({ error: 'Error fetching user profile' });
+ console.error('User profile error:', {
+ error: error.message,
+ stack: error.stack,
+ username: req.params.username,
+ userId: req.user.userId
+ });
+ res.status(500).json({
+ error: 'Error fetching user profile',
+ details: error.message
+ });
}
});
@@ -107,4 +136,36 @@ router.get('/user-info', authenticateToken, async (req, res) => {
}
});
+router.get('/:userId/avatar', async (req, res) => {
+ try {
+ console.log('Fetching avatar for userId:', req.params.userId);
+
+ const user = await User.findOne({ userId: req.params.userId });
+ if (!user) {
+ console.error('User not found for avatar request:', req.params.userId);
+ return res.status(404).json({ error: 'User not found' });
+ }
+
+ console.log('Avatar data found:', {
+ userId: user.userId,
+ hasAvatarRender: !!user.avatarRender,
+ avatarRenderDetails: user.avatarRender
+ });
+
+ res.json({
+ avatarRender: user.avatarRender
+ });
+ } catch (error) {
+ console.error('Error fetching user avatar render:', {
+ error: error.message,
+ stack: error.stack,
+ userId: req.params.userId
+ });
+ res.status(500).json({
+ error: 'Internal server error',
+ details: error.message
+ });
+ }
+});
+
module.exports = router;