From 7f8709516d82cb65ed62b69a59463e13ecfa0dc5 Mon Sep 17 00:00:00 2001 From: Torsten Schulz Date: Sun, 27 Oct 2024 13:14:05 +0100 Subject: [PATCH] Friendship management added --- .../controllers/socialnetworkController.js | 41 ++++++- backend/models/associations.js | 6 + backend/models/community/friendship.js | 37 ++++++ backend/models/index.js | 2 + backend/routers/socialnetworkRouter.js | 45 ++++---- backend/services/socialnetworkService.js | 106 +++++++++++++++++- .../public/images/icons/cancel-friendship.png | Bin 0 -> 11858 bytes .../images/icons/request-friendship.png | Bin 0 -> 11130 bytes frontend/src/App.vue | 8 +- frontend/src/components/DialogWidget.vue | 18 ++- .../socialnetwork/UserProfileDialog.vue | 105 ++++++++++++++++- .../src/dialogues/standard/MessageDialog.vue | 52 +++++++++ .../src/i18n/locales/de/socialnetwork.json | 17 +++ 13 files changed, 406 insertions(+), 31 deletions(-) create mode 100644 backend/models/community/friendship.js create mode 100644 frontend/public/images/icons/cancel-friendship.png create mode 100644 frontend/public/images/icons/request-friendship.png create mode 100644 frontend/src/dialogues/standard/MessageDialog.vue diff --git a/backend/controllers/socialnetworkController.js b/backend/controllers/socialnetworkController.js index 0f79d4c..29ca110 100644 --- a/backend/controllers/socialnetworkController.js +++ b/backend/controllers/socialnetworkController.js @@ -23,6 +23,9 @@ class SocialNetworkController { this.updateDiaryEntry = this.updateDiaryEntry.bind(this); this.deleteDiaryEntry = this.deleteDiaryEntry.bind(this); this.getDiaryEntries = this.getDiaryEntries.bind(this); + this.addFriend = this.addFriend.bind(this); + this.removeFriend = this.removeFriend.bind(this); + this.acceptFriendship = this.acceptFriendship.bind(this); } async userSearch(req, res) { @@ -278,7 +281,7 @@ class SocialNetworkController { async getDiaryEntries(req, res) { try { - const { userid: userId} = req.headers; + const { userid: userId } = req.headers; const { page } = req.params; const entries = await this.socialNetworkService.getDiaryEntries(userId, page); res.status(200).json(entries); @@ -287,6 +290,42 @@ class SocialNetworkController { res.status(500).json({ error: error.message }); } } + + async addFriend(req, res) { + try { + const { userid: hashedUserid } = req.headers; + const { friendUserid } = req.body; + await this.socialNetworkService.addFriend(hashedUserid, friendUserid); + res.status(201).json({ message: 'added' }); + } catch (error) { + console.error('Error in addFriend:', error); + res.status(500).json({ error: error.message }); + } + } + + async removeFriend(req, res) { + try { + const { userid: hashedUserid } = req.headers; + const { friendUserid } = req.params; + await this.socialNetworkService.removeFriend(hashedUserid, friendUserid); + res.status(200).json({ message: 'removed' }); + } catch (error) { + console.error('Error in removeFriend:', error); + res.status(500).json({ error: error.message }); + } + } + + async acceptFriendship(req, res) { + try { + const { userid: hashedUserid } = req.headers; + const { friendUserid } = req.params; + await this.socialNetworkService.acceptFriendship(hashedUserid, friendUserid); + res.status(200).json({ message: 'accepted' }); + } catch (error) { + console.error('Error in acceptFriendship:', error); + res.status(500).json({ error: error.message }); + } + } } export default SocialNetworkController; diff --git a/backend/models/associations.js b/backend/models/associations.js index 5508905..edfc167 100644 --- a/backend/models/associations.js +++ b/backend/models/associations.js @@ -27,6 +27,7 @@ import TitleHistory from './forum/title_history.js'; import ForumPermission from './forum/forum_permission.js'; import ForumUserPermission from './forum/forum_user_permission.js'; import ForumForumPermission from './forum/forum_forum_permission.js'; +import Friendship from './community/friendship.js'; export default function setupAssociations() { // UserParam related associations @@ -156,4 +157,9 @@ export default function setupAssociations() { ForumPermission.hasMany(ForumUserPermission, { foreignKey: 'permissionId' }); ForumUserPermission.belongsTo(ForumPermission, { foreignKey: 'permissionId' }); + + Friendship.belongsTo(User, { foreignKey: 'user1Id', as: 'friendSender' }); + Friendship.belongsTo(User, { foreignKey: 'user2Id', as: 'friendReceiver' }); + User.hasMany(Friendship, { foreignKey: 'user1Id', as: 'friendSender' }); + User.hasMany(Friendship, { foreignKey: 'user2Id', as: 'friendReceiver' }); } diff --git a/backend/models/community/friendship.js b/backend/models/community/friendship.js new file mode 100644 index 0000000..50e50fa --- /dev/null +++ b/backend/models/community/friendship.js @@ -0,0 +1,37 @@ +import { sequelize } from '../../utils/sequelize.js'; +import { DataTypes } from 'sequelize'; + +const Friendship = sequelize.define('friendship', { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true + }, + user1Id: { + type: DataTypes.INTEGER, + allowNull: false + }, + user2Id: { + type: DataTypes.INTEGER, + allowNull: false + }, + accepted: { + type: DataTypes.BOOLEAN, + defaultValue: false + }, + denied: { + type: DataTypes.BOOLEAN, + defaultValue: false + }, + withdrawn: { + type: DataTypes.BOOLEAN, + defaultValue: false + } +}, { + tableName: 'friendship', + schema: 'community', + underscored: true, + timestamps: true, +}); + +export default Friendship; diff --git a/backend/models/index.js b/backend/models/index.js index 68a4df4..ee27131 100644 --- a/backend/models/index.js +++ b/backend/models/index.js @@ -31,6 +31,7 @@ import Message from './forum/message.js'; import MessageHistory from './forum/message_history.js'; import MessageImage from './forum/message_image.js'; import ForumForumPermission from './forum/forum_forum_permission.js'; +import Friendship from './community/friendship.js'; const models = { SettingsType, @@ -66,6 +67,7 @@ const models = { Message, MessageHistory, MessageImage, + Friendship, }; export default models; diff --git a/backend/routers/socialnetworkRouter.js b/backend/routers/socialnetworkRouter.js index 017b35d..ba8d3de 100644 --- a/backend/routers/socialnetworkRouter.js +++ b/backend/routers/socialnetworkRouter.js @@ -7,25 +7,30 @@ const upload = multer(); const router = express.Router(); const socialNetworkController = new SocialNetworkController(); -router.post('/usersearch', authenticate, socialNetworkController.userSearch); -router.get('/profile/main/:userId', authenticate, socialNetworkController.profile); -router.post('/folders/:folderId', authenticate, socialNetworkController.createFolder); -router.get('/folders', authenticate, socialNetworkController.getFolders); -router.get('/folder/:folderId', authenticate, socialNetworkController.getFolderImageList); -router.post('/images', authenticate, upload.single('image'), socialNetworkController.uploadImage); -router.get('/images/:imageId', authenticate, socialNetworkController.getImage); -router.put('/images/:imageId', authenticate, socialNetworkController.changeImage); -router.get('/imagevisibilities', authenticate, socialNetworkController.getImageVisibilityTypes); -router.get('/image/:hash', authenticate, socialNetworkController.getImageByHash); -router.get('/profile/images/folders/:username', authenticate, socialNetworkController.getFoldersByUsername); -router.delete('/folders/:folderId', authenticate, socialNetworkController.deleteFolder); -router.post('/guestbook/entries', authenticate, upload.single('image'), socialNetworkController.createGuestbookEntry); -router.get('/guestbook/entries/:username/:page', authenticate, socialNetworkController.getGuestbookEntries); -router.delete('/guestbook/entries/:entryId', authenticate, socialNetworkController.deleteGuestbookEntry); -router.get('/guestbook/image/:guestbookUserName/:entryId', authenticate, socialNetworkController.getGuestbookImage); -router.post('/diary', authenticate, socialNetworkController.createDiaryEntry); -router.put('/diary/:diaryEntryId', authenticate, socialNetworkController.updateDiaryEntry); -router.delete('/diary/:entryId', authenticate, socialNetworkController.deleteDiaryEntry); -router.get('/diary/:page', authenticate, socialNetworkController.getDiaryEntries); +router.use(authenticate); + +router.post('/usersearch', socialNetworkController.userSearch); +router.get('/profile/main/:userId', socialNetworkController.profile); +router.post('/folders/:folderId', socialNetworkController.createFolder); +router.get('/folders', socialNetworkController.getFolders); +router.get('/folder/:folderId', socialNetworkController.getFolderImageList); +router.post('/images', upload.single('image'), socialNetworkController.uploadImage); +router.get('/images/:imageId', socialNetworkController.getImage); +router.put('/images/:imageId', socialNetworkController.changeImage); +router.get('/imagevisibilities', socialNetworkController.getImageVisibilityTypes); +router.get('/image/:hash', socialNetworkController.getImageByHash); +router.get('/profile/images/folders/:username', socialNetworkController.getFoldersByUsername); +router.delete('/folders/:folderId', socialNetworkController.deleteFolder); +router.post('/guestbook/entries', upload.single('image'), socialNetworkController.createGuestbookEntry); +router.get('/guestbook/entries/:username/:page', socialNetworkController.getGuestbookEntries); +router.delete('/guestbook/entries/:entryId', socialNetworkController.deleteGuestbookEntry); +router.get('/guestbook/image/:guestbookUserName/:entryId', socialNetworkController.getGuestbookImage); +router.post('/diary', socialNetworkController.createDiaryEntry); +router.put('/diary/:diaryEntryId', socialNetworkController.updateDiaryEntry); +router.delete('/diary/:entryId', socialNetworkController.deleteDiaryEntry); +router.get('/diary/:page', socialNetworkController.getDiaryEntries); +router.post('/friend', socialNetworkController.addFriend); +router.delete('/friend/:friendUserId', socialNetworkController.removeFriend); +router.put('/friend/:friendUserId', socialNetworkController.acceptFriendship); export default router; diff --git a/backend/services/socialnetworkService.js b/backend/services/socialnetworkService.js index c795f75..d6d8827 100644 --- a/backend/services/socialnetworkService.js +++ b/backend/services/socialnetworkService.js @@ -23,6 +23,7 @@ import { JSDOM } from 'jsdom'; import DOMPurify from 'dompurify'; import sharp from 'sharp'; import Diary from '../models/community/diary.js'; +import Friendship from '../models/community/friendship.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -347,7 +348,15 @@ class SocialNetworkService extends BaseService { { model: UserParamType, as: 'paramType' }, { model: UserParamVisibility, as: 'param_visibilities', include: [{ model: UserParamVisibilityType, as: 'visibility_type' }] } ], - order: [['order_id', 'asc']] + order: [['order_id', 'asc']], + }, + { + model: Friendship, + as: 'friendSender', + }, + { + model: Friendship, + as: 'friendReceiver', } ] }); @@ -370,9 +379,26 @@ class SocialNetworkService extends BaseService { }; } } + let friendship = null; + if (user.friendSender && user.friendSender.length > 0) { + friendship = { + isSender: true, + accepted: user.friendSender[0].dataValues.accepted, + denied: user.friendSender[0].dataValues.denied, + withdrawn: user.friendSender[0].dataValues.withdrawn, + } + } else if (user.friendReceiver && user.friendReceiver.length > 0) { + friendship = { + isSender: false, + accepted: user.friendReceiver[0].dataValues.accepted, + denied: user.friendReceiver[0].dataValues.denied, + withdrawn: user.friendReceiver[0].dataValues.withdrawn, + } + } return { username: user.username, registrationDate: user.registrationDate, + friendship: friendship, params: userParams }; } @@ -707,5 +733,83 @@ class SocialNetworkService extends BaseService { }); return { entries: entries.rows, totalPages: Math.ceil(entries.count / 20) }; } + + async addFriend(hashedUserid, friendUserid) { + const requestingUserId = await this.checkUserAccess(hashedUserid); + const friend = await this.loadUserByHash(friendUserid); + if (!friend) { + throw new Error('notfound'); + } + const friendship = await Friendship.findOne({ + where: { + [Op.or]: [ + { user1Id: requestingUserId, user2Id: friend.id }, + { user1Id: friend.id, user2Id: requestingUserId } + ] + } + }); + if (friendship) { + if (friendship.withdrawn) { + friendship.withdrawn = false; + + } else { + throw new Error('alreadyexists'); + } + } else { + await Friendship.create({ user1Id: requestingUserId, user2Id: friend.id }); + } + return { accepted: false, withdrawn: false, denied: false }; + } + + async removeFriend(hashedUserid, friendUserid) { + const requestingUserId = await this.checkUserAccess(hashedUserid); + const friend = await this.loadUserByHash(friendUserid); + if (!friend) { + throw new Error('notfound'); + } + const friendship = await Friendship.findOne({ + where: { + [Op.or]: [ + { user1Id: requestingUserId, user2Id: friend.id }, + { user1Id: friend.id, user2Id: requestingUserId } + ] + } + }); + if (!friendship) { + throw new Error('notfound'); + } + if (friendship.user1Id === requestingUserId) { + friendship.update({ withdrawn: true }) + } else { + friendship.update({ denied: true }); + } + return true; + } + + async acceptFriendship(hashedUserid, friendUserid) { + const requestingUserId = await this.checkUserAccess(hashedUserid); + const friend = await this.loadUserByHash(friendUserid); + if (!friend) { + throw new Error('notfound'); + } + const friendship = await Friendship.findOne({ + where: { + [Op.or]: [ + { user1Id: requestingUserId, user2Id: friend.id }, + { user1Id: friend.id, user2Id: requestingUserId } + ] + } + }); + if (!friendship) { + throw new Error('notfound'); + } + if (friendship.user1Id === requestingUserId && friendship.withdrawn) { + friendship.update({ withdrawn: false }); + } else if (friendship.user2Id === requestingUserId && friendship.denied) { + friendship.update({ denied: false, accepted: true }); + } else { + throw new Error('notfound'); + } + } } export default SocialNetworkService; diff --git a/frontend/public/images/icons/cancel-friendship.png b/frontend/public/images/icons/cancel-friendship.png new file mode 100644 index 0000000000000000000000000000000000000000..e10a26838d4a482e44537faa34b0579fa6d07ea5 GIT binary patch literal 11858 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 ogTzPP7^T*Bx!_N(r - + - + + + - diff --git a/frontend/src/dialogues/standard/MessageDialog.vue b/frontend/src/dialogues/standard/MessageDialog.vue new file mode 100644 index 0000000..ff9457c --- /dev/null +++ b/frontend/src/dialogues/standard/MessageDialog.vue @@ -0,0 +1,52 @@ + + + + + diff --git a/frontend/src/i18n/locales/de/socialnetwork.json b/frontend/src/i18n/locales/de/socialnetwork.json index b32677e..3edea90 100644 --- a/frontend/src/i18n/locales/de/socialnetwork.json +++ b/frontend/src/i18n/locales/de/socialnetwork.json @@ -231,6 +231,23 @@ "page": "Seite <> von <>" }, "createNewMesssage": "Antwort senden" + }, + "friendship": { + "error": { + "alreadyexists": "Die Freundschaftsanfrage existiert bereits" + }, + "state": { + "none": "Nicht befreundet", + "waiting": "Freundschaftsanfrage gesendet, aber nicht beantwortet", + "open": "Freundschaft wurde angefragt", + "denied": "Freundschaftsanfrage abgelehnt", + "withdrawn": "Freundschaftsanfrage zurückgezogen", + "accepted": "Befreundet" + }, + "added": "Du hast eine Freundschaftsanfrage gestellt.", + "withdrawn": "Du hast Deine Freundschaftsanfrage zurückgezogen.", + "denied": "Du hast die Freundschaftsanfrage abgelehnt.", + "accepted": "Die Freundschaft wurde geschlossen." } } } \ No newline at end of file