Loading...
+
+
+
Avatar Editor
+
+
+
+
+
+
Colors
+
+
+
Click a body part to change its color:
+
+
+
+
+
+
+
+
+
+
+
Customize Your Avatar
+
+
+
+
+
+
+
Currently Wearing
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/client/js/avatar.js b/client/js/avatar.js
new file mode 100644
index 0000000..3ec77b0
--- /dev/null
+++ b/client/js/avatar.js
@@ -0,0 +1,250 @@
+// client/js/avatar.js
+$(document).ready(function () {
+ initializeAvatarEditor();
+ loadUserAvatar();
+});
+
+function initializeAvatarEditor() {
+ loadAvatarsItems();
+ setupItemSelection();
+}
+
+function loadAvatarsItems() {
+ loadShirts();
+ // Uncomment these when i do these
+ // loadPants();
+ // loadHats();
+}
+
+function loadShirts() {
+ const token = localStorage.getItem('token');
+ console.log('Loading shirts for user');
+
+ $.ajax({
+ url: '/api/shirts/user', // Correct server route
+ method: 'GET',
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ success: function (shirts) {
+ console.log('Shirts loaded successfully:', shirts.length);
+ displayUserShirts(shirts);
+ },
+ error: function (xhr, status, error) {
+ console.error('Error fetching shirts:', error);
+ console.error('Status:', status);
+ console.error('Response:', xhr.responseText);
+ $('#shirts-container').html('
Error loading shirts. Please try again later.
');
+ },
+ });
+}
+
+function displayUserShirts(shirts) {
+ const container = $('#shirts-container');
+ container.empty();
+
+ if (!Array.isArray(shirts) || shirts.length === 0) {
+ console.log('No shirts available to display');
+ container.append('
No shirts available.
');
+ return;
+ }
+
+ console.log('Displaying shirts:', shirts.length);
+ shirts.forEach((shirt) => {
+ if (!shirt || !shirt.Name || !shirt.ThumbnailLocation) {
+ console.error('Invalid shirt object:', shirt);
+ return;
+ }
+ const shirtHtml = generateItemHtml(
+ shirt.Name,
+ shirt.ThumbnailLocation,
+ shirt.creator ? shirt.creator.username : 'Unknown',
+ shirt.Price,
+ shirt._id,
+ 'shirt'
+ );
+ container.append(shirtHtml);
+ });
+}
+
+function generateItemHtml(name, imageSrc, creator, price, id, type) {
+ const priceDisplay = price === 0 ? 'Free' : `$${price}`;
+ return `
+
+
+
+
+
+
+
${name}
+
Creator: ${creator}
+
Price: ${priceDisplay}
+
+
+
+
+ `;
+}
+
+function setupItemSelection() {
+ $(document).on('click', '.select-item', function () {
+ const type = $(this).data('type');
+ const itemId = $(this).data('id');
+ wearItem(type, itemId);
+ saveAvatarSelection(type, itemId);
+ });
+}
+
+function wearItem(type, itemId) {
+ const token = localStorage.getItem('token');
+ let apiUrl = '';
+
+ switch (type) {
+ case 'shirt':
+ apiUrl = `/api/shirts/${itemId}`;
+ break;
+ // Add cases for 'pants' and 'hat' when made
+ default:
+ return;
+ }
+
+ $.ajax({
+ url: apiUrl,
+ method: 'GET',
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ success: function (item) {
+ updateAvatarDisplay(type, item.thumbnailUrl); // item.ThumbnailLocation
+ updateCurrentlyWearing(type, item);
+ },
+ error: function (xhr, status, error) {
+ console.error(`Error fetching ${type}:`, error);
+ },
+ });
+}
+
+function updateAvatarDisplay(type, imageUrl) {
+ switch (type) {
+ case 'shirt':
+ $('#avatar-shirt').attr('src', imageUrl);
+ break;
+ // Add cases for 'pants' and 'hat' when i makethem
+ default:
+ break;
+ }
+}
+
+
+function updateCurrentlyWearing(type, item) {
+ const container = $('#currently-wearing');
+ const existingItem = container.find(`[data-type="${type}"]`);
+
+ if (existingItem.length) {
+ existingItem.remove();
+ }
+
+ const itemHtml = `
+
+
+
+
+
${item.Name}
+
+
+
+
+ `;
+
+ container.append(itemHtml);
+}
+
+function loadUserAvatar() {
+ const token = localStorage.getItem('token');
+ $.ajax({
+ url: '/api/avatar',
+ method: 'GET',
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ success: function (avatar) {
+ if (avatar.shirtId) {
+ wearItem('shirt', avatar.shirtId);
+ }
+ },
+ error: function (xhr, status, error) {
+ console.error('Error loading avatar:', error);
+ $('#avatar-display').html('
Error loading avatar. Please try again later.
');
+ },
+ });
+}
+
+function saveAvatarSelection(type, itemId) {
+ const token = localStorage.getItem('token');
+ const avatarData = {};
+
+ switch (type) {
+ case 'shirt':
+ avatarData.shirtId = itemId;
+ break;
+ default:
+ return;
+ }
+
+ $.ajax({
+ url: '/api/avatar',
+ method: 'PUT',
+ data: JSON.stringify(avatarData),
+ contentType: 'application/json',
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ success: function (response) {
+ console.log('Avatar updated successfully.');
+ },
+ error: function (xhr, status, error) {
+ console.error('Error updating avatar:', error);
+ },
+ });
+}
+
+// basic body colors set up for seven
+function setupBodyColors() {
+ const bodyParts = ['head', 'torso', 'leftArm', 'rightArm', 'leftLeg', 'rightLeg'];
+ const colorPicker = $('
');
+ const bodyColorContainer = $('#body-colors');
+
+ bodyParts.forEach(part => {
+ const partButton = $(`
`);
+ bodyColorContainer.append(partButton);
+ });
+
+ bodyColorContainer.append(colorPicker);
+
+ $('.body-part').on('click', function() {
+ const part = $(this).data('part');
+ colorPicker.data('currentPart', part);
+ colorPicker.click();
+ });
+
+ colorPicker.on('change', function() {
+ const color = $(this).val();
+ const part = $(this).data('currentPart');
+ updateBodyColor(part, color);
+ });
+}
+
+function updateBodyColor(part, color) {
+ // to do
+}
+
+$(document).on('click', '.remove-item', function() {
+ const type = $(this).data('type');
+ removeItem(type);
+});
+
+function removeItem(type) {
+ $(`#avatar-${type}`).attr('src', '');
+ $(`#currently-wearing [data-type="${type}"]`).remove();
+ saveAvatarSelection(type, null);
+}
\ No newline at end of file
diff --git a/server/functions/api/middleware/authenticateToken.js b/server/functions/api/middleware/authenticateToken.js
index a2f93e6..2b3cb62 100644
--- a/server/functions/api/middleware/authenticateToken.js
+++ b/server/functions/api/middleware/authenticateToken.js
@@ -1,21 +1,24 @@
const jwt = require('jsonwebtoken');
function authenticateToken(req, res, next) {
- const authHeader = req.headers['authorization'];
- const token = authHeader && authHeader.split(' ')[1];
+ const authHeader = req.headers['authorization'];
+ const token = authHeader && authHeader.split(' ')[1];
- if (!token) {
- return res.status(401).json({ error: 'No token provided.' });
- }
-
- jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => {
- if (err) {
- console.error('Token verification error:', err);
- return res.status(403).json({ error: 'Invalid token.' });
+ if (token == null) {
+ console.error('No token provided');
+ return res.sendStatus(401);
}
- req.user = { userId: decoded.userId }; // Ensure userId is correctly set
- next();
- });
+
+ jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
+ if (err) {
+ console.error('Token verification failed:', err);
+ return res.sendStatus(403);
+ }
+
+ console.log('Authenticated user:', user);
+ req.user = user;
+ next();
+ });
}
module.exports = authenticateToken;
diff --git a/server/functions/api/routes/avatar.js b/server/functions/api/routes/avatar.js
new file mode 100644
index 0000000..4995f11
--- /dev/null
+++ b/server/functions/api/routes/avatar.js
@@ -0,0 +1,60 @@
+const express = require('express');
+const router = express.Router();
+const authenticateToken = require('../middleware/authenticateToken');
+const User = require('../models/User');
+
+// Get current avatar
+router.get('/', authenticateToken, async (req, res) => {
+ try {
+ console.log('Fetching avatar for userId:', req.user.userId);
+ const user = await User.findOne({ userId: req.user.userId }).select('avatar');
+
+ if (!user) {
+ console.error('User not found for userId:', req.user.userId);
+ return res.status(404).json({ error: 'User not found' });
+ }
+
+ console.log('Avatar fetched successfully:', user.avatar);
+ res.json(user.avatar);
+ } catch (error) {
+ console.error('Error fetching avatar:', error);
+ res.status(500).json({ error: 'Internal server error', details: error.message });
+ }
+});
+
+// Update avatar
+router.put('/', authenticateToken, async (req, res) => {
+ try {
+ const { shirtId } = req.body;
+
+ const user = await User.findOne({ userId: req.user.userId });
+ if (!user) {
+ console.error('User not found for userId:', req.user.userId);
+ return res.status(404).json({ error: 'User not found' });
+ }
+
+ if (shirtId) {
+ if (!mongoose.Types.ObjectId.isValid(shirtId)) {
+ console.error('Invalid shirt ID:', shirtId);
+ return res.status(400).json({ error: 'Invalid shirt ID' });
+ }
+
+ // Check if the shirt is in inventory
+ if (!user.inventory.includes(shirtId)) {
+ console.error('Shirt not in user inventory:', shirtId);
+ return res.status(400).json({ error: 'Shirt not in user inventory' });
+ }
+
+ user.avatar.shirtId = shirtId;
+ }
+
+ await user.save();
+ console.log('Avatar updated successfully for userId:', req.user.userId);
+ res.json({ message: 'Avatar updated successfully' });
+ } catch (error) {
+ console.error('Error updating avatar:', error);
+ res.status(500).json({ error: 'Internal server error', details: error.message });
+ }
+});
+
+module.exports = router;
\ No newline at end of file
diff --git a/server/functions/api/routes/pages.js b/server/functions/api/routes/pages.js
index fbe173c..470d005 100644
--- a/server/functions/api/routes/pages.js
+++ b/server/functions/api/routes/pages.js
@@ -70,6 +70,11 @@ router.get('/my/friends', (req, res) =>
sendHtmlFile(res, 'pages/my/friends.html')
);
+// avatar
+router.get('/my/avatar', (req, res) =>
+ sendHtmlFile(res, 'pages/my/avatar.html')
+);
+
router.get('/friends/showfriends', (req, res) =>
sendHtmlFile(res, 'pages/friends/showfriends.html')
);
diff --git a/server/functions/api/routes/shirt.js b/server/functions/api/routes/shirt.js
index 38346ac..b35321d 100644
--- a/server/functions/api/routes/shirt.js
+++ b/server/functions/api/routes/shirt.js
@@ -173,6 +173,8 @@ router.post(
})
.promise();
+ const isForSale = parseInt(price) > 0 ? 1 : 0;
+
const shirt = new Asset({
assetId: shirtassetId,
FileLocation: shirtassetLocation,
@@ -181,9 +183,8 @@ router.post(
Name: filter.clean(title),
Description: filter.clean(description),
ThumbnailLocation: thumbnailUrl,
- IsForSale: 0,
Price: parseInt(price),
- IsForSale: 1, //why is there 2 isforsale?
+ IsForSale: isForSale, //why is there 2 isforsale?
Sales: 0,
IsPublicDomain: 0,
});
@@ -354,15 +355,45 @@ router.get('/user/id/:id', authenticateToken, async (req, res) => {
}
});
+
+
router.get('/user', authenticateToken, async (req, res) => {
try {
- const shirts = await Asset.find({
- creator: req.user.userId,
- AssetType: 'Shirt',
- }).sort({ updatedAt: -1 });
- res.json(shirts);
+ console.log('Fetching shirts for userId:', req.user.userId);
+ const user = await User.findOne({ userId: req.user.userId }).populate('inventory');
+
+ if (!user) {
+ console.error('User not found for userId:', req.user.userId);
+ return res.status(404).json({ error: 'User not found' });
+ }
+
+ // Fetch shirts created by user
+ const createdShirts = await Asset.find({
+ creator: user._id, // Using ObjectId from User model
+ AssetType: 'Shirt',
+ }).populate('creator', 'username');
+
+ console.log('Created shirts found:', createdShirts.length);
+
+ // Fetch shirts owned by user
+ const ownedShirts = await Asset.find({
+ _id: { $in: user.inventory },
+ AssetType: 'Shirt',
+ }).populate('creator', 'username');
+
+ console.log('Owned shirts found:', ownedShirts.length);
+
+ // combine and remove duplicates
+ const allShirts = [...createdShirts, ...ownedShirts];
+ const uniqueShirts = Array.from(
+ new Set(allShirts.map((s) => s._id.toString()))
+ ).map((_id) => allShirts.find((s) => s._id.toString() === _id));
+
+ console.log('Total shirts:', uniqueShirts.length);
+ res.json(uniqueShirts);
} catch (error) {
- res.status(500).json({ error: error.message });
+ console.error('Error fetching user shirts:', error);
+ res.status(500).json({ error: 'Internal server error', details: error.message });
}
});
diff --git a/server/server.js b/server/server.js
index 42fcf8e..873acaa 100644
--- a/server/server.js
+++ b/server/server.js
@@ -28,6 +28,8 @@ const userRoutes = require('./functions/api/routes/user');
const catalogRoutes = require('./functions/api/routes/catalog');
const forumRoutes = require('./functions/api/routes/forum');
+const avatarRoutes = require('./functions/api/routes/avatar');
+
// Init Expressjs
const app = express();
const port = process.env.PORT || 3000;
@@ -180,6 +182,9 @@ app.use('/api', currencyRoutes);
app.use('/api', searchUsersRoutes);
app.use('/api/catalog', catalogRoutes);
+
+app.use('/api/avatar', avatarRoutes);
+
app.use('/api', userRoutes);
app.use('/video', express.static(path.join(__dirname, '../video')));