improve message object structure, rearrange methods in app.js just for the code to look better and make sure the message list updates when a new message is sent

This commit is contained in:
hippoz 2020-11-17 15:27:23 +02:00
parent 89acdf6a3c
commit 667a843370
5 changed files with 172 additions and 135 deletions

View file

@ -1,9 +1,11 @@
const User = require('../../../models/User'); const User = require('../../../models/User');
const secret = require('../../../secret'); const secret = require('../../../secret');
const config = require('../../../config'); const config = require('../../../config');
const Category = require('../../../models/Category');
const jwt = require('jsonwebtoken'); const jwt = require('jsonwebtoken');
const siolib = require('socket.io') const siolib = require('socket.io');
const uuid = require('uuid');
class GatewayServer { class GatewayServer {
constructor(httpServer) { constructor(httpServer) {
@ -68,23 +70,37 @@ GatewayServer.prototype.eventSetup = function() {
console.log(`[*] [gateway] [handshake] Got yoo from ${socket.username}, connection is finally completed!`); console.log(`[*] [gateway] [handshake] Got yoo from ${socket.username}, connection is finally completed!`);
socket.isConnected = true; socket.isConnected = true;
socket.on('message', ({ categoryId, content }) => { socket.on('message', ({ category, content }) => {
// TODO: URGENT: Check if the category exists and if the user has access to it (access coming soon) // TODO: When/if category permissions are added, check if the user has permissions for that category
const msgConfig = { const categoryTitle = socket.joinedCategories[category._id];
username: socket.username, if (!categoryTitle) return;
creatorId: socket.userId,
categoryId: categoryId, const messageObject = {
content: content author: {
username: socket.username,
_id: socket.userId
},
category: {
title: categoryTitle,
_id: category._id
},
content: content,
_id: uuid.v4()
}; };
socket.to(categoryId).emit('message', msgConfig); socket.to(category._id).emit('message', messageObject);
socket.emit('message', msgConfig); socket.emit('message', messageObject);
}); });
socket.on('subscribe', async (categories) => { socket.on('subscribe', async (categories) => {
for (let v of categories) { for (let v of categories) {
// TODO: URGENT: Check if the category exists and if the user has access to it (access coming soon) // TODO: When/if category permissions are added, check if the user has permissions for that category
socket.join(v); const category = await Category.findById(v);
if (category && category.title && category._id) {
if (!socket.joinedCategories) socket.joinedCategories = {};
socket.joinedCategories[v] = category.title;
socket.join(v);
}
} }
}); });
}); });

View file

@ -109,10 +109,10 @@
<md-button v-for="button in cardButtons" v-bind:key="button.text" @click="button.click(post)">{{ button.text }}</md-button> <md-button v-for="button in cardButtons" v-bind:key="button.text" @click="button.click(post)">{{ button.text }}</md-button>
</md-card-actions> </md-card-actions>
</md-card> </md-card>
<div v-for="post,k in messages[selection.category._id]" v-if="selection.category.isChatContext" :key="post.content + post.creatorId + k"> <div v-for="post,k in messages[selection.category._id]" v-if="selection.category.isChatContext" :key="post._id + post.author._id">
<md-card> <md-card>
<md-card-header> <md-card-header>
<a class="md-dense cursor md-title" v-on:click="viewProfile(post.creatorId)"><span>{{ post.username }}</span></a> <a class="md-dense cursor md-title" v-on:click="viewProfile(post.author._id)"><span>{{ post.author.username }}</span></a>
</md-card-header> </md-card-header>
<md-card-content v-html="post.content"></md-card-content> <md-card-content v-html="post.content"></md-card-content>

View file

@ -100,7 +100,12 @@ GatewayConnection.prototype.connect = function(token) {
GatewayConnection.prototype.sendMessage = function(categoryId, content) { GatewayConnection.prototype.sendMessage = function(categoryId, content) {
if (!this.isConnected) return; if (!this.isConnected) return;
this.socket.emit('message', { categoryId, content }); this.socket.emit('message', {
category: {
_id: categoryId
},
content
});
}; };
GatewayConnection.prototype.subscribeToCategoryChat = function(categoryId) { GatewayConnection.prototype.subscribeToCategoryChat = function(categoryId) {
@ -193,29 +198,7 @@ const app = new Vue({
} }
}, },
methods: { methods: {
navigateToAccountManager() { // Gateway and chat
window.location.href = `${window.location.origin}/auth.html`;
},
snackbarButtonAction: function() {
this.showSnackbarNotification = false;
},
snackbarButtonClick: function() {
this.snackbarButtonAction();
},
snackbarEditButton: function(buttonText="Ok", action) {
this.snackbarButtonText = buttonText;
this.snackbarButtonAction = action;
},
resetSnackbarButton: function() {
this.snackbarButtonText = 'Ok';
this.snackbarButtonAction = () => {
this.showSnackbarNotification = false;
};
},
notification: function(text) {
this.snackbarNotification = text;
this.showSnackbarNotification = true;
},
performGatewayConnection: function() { performGatewayConnection: function() {
// TODO: again, the thing im doing with the token is not very secure, since its being sent by the current user info endpoint and is also being send through query parameters // TODO: again, the thing im doing with the token is not very secure, since its being sent by the current user info endpoint and is also being send through query parameters
this.gateway.onDisconnect = (e) => { this.gateway.onDisconnect = (e) => {
@ -223,21 +206,99 @@ const app = new Vue({
this.notification('ERROR: You have been disconnected from the gateway. Realtime features such as chat will not work and unexpected errors may occur.'); this.notification('ERROR: You have been disconnected from the gateway. Realtime features such as chat will not work and unexpected errors may occur.');
}; };
this.gateway.onConnect = () => { this.gateway.onConnect = () => {
this.gateway.socket.on('message', ({ username, categoryId, content, creatorId }) => { this.gateway.socket.on('message', this.processMessage);
this.showMessageNotification(categoryId, username, creatorId, content);
})
}; };
this.gateway.connect(this.loggedInUser.token); this.gateway.connect(this.loggedInUser.token);
}, },
showMessageNotification: async function(categoryId, username, creatorId, content) { processMessage: async function(messageObject) {
if (!this.messages[categoryId]) this.messages[categoryId] = [] if (!this.messages[messageObject.category._id]) this.$set(this.messages, messageObject.category._id, []);
this.messages[categoryId].push({ username, content, creatorId }); this.messages[messageObject.category._id].push(messageObject);
this.resetSnackbarButton(); if (messageObject.author.username !== this.loggedInUser.username && messageObject.category._id !== this.selection.category._id) {
this.notification(`${username}: ${content}`); this.resetSnackbarButton();
this.notification(`${messageObject.author.username}: ${messageObject.content}`);
}
}, },
button: function(text, click) { openChatForCategory: async function(categoryId) {
this.cardButtons.push({ text, click }); this.gateway.subscribeToCategoryChat(categoryId);
this.selection.category.isChatContext = true;
this.selection.category.browsing = true;
this.selection.category.title = 'Chat';
this.selection.category._id = categoryId;
},
// Category and post browsing
browseCategories: async function() {
const res = await fetch(`${window.location.origin}/api/v1/content/category/list?count=50`, {
method: 'GET',
headers: {
'Accept': 'application/json',
},
credentials: 'include'
});
if (res.status === 200) {
const json = await res.json();
this.selection.category.title = 'categories';
this.selection.category.browsing = true;
this.selection.category.isCategory = false;
this.selection.category.isChatContext = false;
this.selection.posts = [];
this.cardButtons = [];
this.button('Chat', (post) => {
if (post._id) {
this.openChatForCategory(post._id);
this.gateway.sendMessage(post._id, 'yoooo');
}
});
this.button('View', (post) => {
this.browse(post);
});
for (let i = 0; i < json.categories.length; i++) {
const v = json.categories[i];
this.selection.posts.push({ title: v.title, body: '', _id: v._id, creator: v.creator });
}
} else {
this.resetSnackbarButton();
this.notification('Failed to fetch category list');
}
},
browse: async function(category) {
const { _id, title } = category;
const res = await fetch(`${window.location.origin}/api/v1/content/category/${_id}/info`, {
method: 'GET',
headers: {
'Accept': 'application/json',
},
credentials: 'include'
});
if (res.status === 200) {
const json = await res.json();
this.selection.category.title = title;
this.selection.category._id = _id;
this.selection.category.browsing = true;
this.selection.category.isCategory = true;
this.selection.category.isChatContext = false;
this.selection.posts = [];
this.cardButtons = [];
for (let i = 0; i < json.category.posts.length; i++) {
const v = json.category.posts[i];
this.selection.posts.push({ title: v.title, body: v.body, _id: v._id, creator: v.creator });
}
} else {
this.resetSnackbarButton();
this.notification('Failed to fetch category');
}
}, },
refresh: function() { refresh: function() {
if (this.selection.category.title === 'categories' && this.selection.category.isCategory === false) { if (this.selection.category.title === 'categories' && this.selection.category.isCategory === false) {
@ -246,6 +307,20 @@ const app = new Vue({
this.browse(this.selection.category); this.browse(this.selection.category);
} }
}, },
button: function(text, click) {
this.cardButtons.push({ text, click });
},
stopBrowsing: function() {
this.selection.category = {
title: '',
browsing: false,
_id: undefined,
isCategory: false,
isChatContext: false
};
this.selection.posts = [];
this.cardButtons = [];
},
viewProfile: async function(id) { viewProfile: async function(id) {
// TODO: this just returns the username for now // TODO: this just returns the username for now
const res = await fetch(`${window.location.origin}/api/v1/users/user/${id}/info`, { const res = await fetch(`${window.location.origin}/api/v1/users/user/${id}/info`, {
@ -267,16 +342,8 @@ const app = new Vue({
this.notification('Failed to fetch user data'); this.notification('Failed to fetch user data');
} }
}, },
stopBrowsing: function() {
this.selection.category = { // Content creation
title: '',
browsing: false,
_id: undefined,
isCategory: false
};
this.selection.posts = [];
this.cardButtons = [];
},
showCreatePostDialog: function() { showCreatePostDialog: function() {
if (!this.selection.category.isCategory) { if (!this.selection.category.isCategory) {
this.resetSnackbarButton(); this.resetSnackbarButton();
@ -373,84 +440,32 @@ const app = new Vue({
return; return;
} }
}, },
browse: async function(category) {
const { _id, title } = category;
const res = await fetch(`${window.location.origin}/api/v1/content/category/${_id}/info`, { // Navigation
method: 'GET', navigateToAccountManager() {
headers: { window.location.href = `${window.location.origin}/auth.html`;
'Accept': 'application/json',
},
credentials: 'include'
});
if (res.status === 200) {
const json = await res.json();
this.selection.category.title = title;
this.selection.category._id = _id;
this.selection.category.browsing = true;
this.selection.category.isCategory = true;
this.selection.category.isChatContext = false;
this.selection.posts = [];
this.cardButtons = [];
for (let i = 0; i < json.category.posts.length; i++) {
const v = json.category.posts[i];
this.selection.posts.push({ title: v.title, body: v.body, _id: v._id, creator: v.creator });
}
} else {
this.resetSnackbarButton();
this.notification('Failed to fetch category');
}
}, },
openChatForCategory: async function(categoryId) {
this.gateway.subscribeToCategoryChat(categoryId); // Snackbar
snackbarButtonAction: function() {
this.selection.category.isChatContext = true; this.showSnackbarNotification = false;
this.selection.category.browsing = true; },
this.selection.category.title = 'Chat'; snackbarButtonClick: function() {
this.selection.category._id = categoryId; this.snackbarButtonAction();
},
snackbarEditButton: function(buttonText="Ok", action) {
this.snackbarButtonText = buttonText;
this.snackbarButtonAction = action;
},
resetSnackbarButton: function() {
this.snackbarButtonText = 'Ok';
this.snackbarButtonAction = () => {
this.showSnackbarNotification = false;
};
},
notification: function(text) {
this.snackbarNotification = text;
this.showSnackbarNotification = true;
}, },
browseCategories: async function() {
const res = await fetch(`${window.location.origin}/api/v1/content/category/list?count=50`, {
method: 'GET',
headers: {
'Accept': 'application/json',
},
credentials: 'include'
});
if (res.status === 200) {
const json = await res.json();
this.selection.category.title = 'categories';
this.selection.category.browsing = true;
this.selection.category.isCategory = false;
this.selection.category.isChatContext = false;
this.selection.posts = [];
this.cardButtons = [];
this.button('Chat', (post) => {
if (post._id) {
this.openChatForCategory(post._id);
this.gateway.sendMessage(post._id, 'yoooo');
}
});
this.button('View', (post) => {
this.browse(post);
});
for (let i = 0; i < json.categories.length; i++) {
const v = json.categories[i];
this.selection.posts.push({ title: v.title, body: '', _id: v._id, creator: v.creator });
}
} else {
this.resetSnackbarButton();
this.notification('Failed to fetch category list');
}
}
} }
}); });

5
package-lock.json generated
View file

@ -1202,6 +1202,11 @@
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
}, },
"uuid": {
"version": "8.3.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz",
"integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg=="
},
"validator": { "validator": {
"version": "13.1.1", "version": "13.1.1",
"resolved": "https://registry.npmjs.org/validator/-/validator-13.1.1.tgz", "resolved": "https://registry.npmjs.org/validator/-/validator-13.1.1.tgz",

View file

@ -18,6 +18,7 @@
"express-validator": "^6.6.1", "express-validator": "^6.6.1",
"jsonwebtoken": "^8.5.1", "jsonwebtoken": "^8.5.1",
"mongoose": "^5.10.0", "mongoose": "^5.10.0",
"socket.io": "^3.0.1" "socket.io": "^3.0.1",
"uuid": "^8.3.1"
} }
} }