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:
parent
89acdf6a3c
commit
667a843370
5 changed files with 172 additions and 135 deletions
|
@ -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,24 +70,38 @@ 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];
|
||||||
|
if (!categoryTitle) return;
|
||||||
|
|
||||||
|
const messageObject = {
|
||||||
|
author: {
|
||||||
username: socket.username,
|
username: socket.username,
|
||||||
creatorId: socket.userId,
|
_id: socket.userId
|
||||||
categoryId: categoryId,
|
},
|
||||||
content: content
|
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
|
||||||
|
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);
|
socket.join(v);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
if (messageObject.author.username !== this.loggedInUser.username && messageObject.category._id !== this.selection.category._id) {
|
||||||
this.resetSnackbarButton();
|
this.resetSnackbarButton();
|
||||||
this.notification(`${username}: ${content}`);
|
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) {
|
// Snackbar
|
||||||
const json = await res.json();
|
snackbarButtonAction: function() {
|
||||||
|
this.showSnackbarNotification = false;
|
||||||
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) {
|
snackbarButtonClick: function() {
|
||||||
this.gateway.subscribeToCategoryChat(categoryId);
|
this.snackbarButtonAction();
|
||||||
|
|
||||||
this.selection.category.isChatContext = true;
|
|
||||||
this.selection.category.browsing = true;
|
|
||||||
this.selection.category.title = 'Chat';
|
|
||||||
this.selection.category._id = categoryId;
|
|
||||||
},
|
},
|
||||||
browseCategories: async function() {
|
snackbarEditButton: function(buttonText="Ok", action) {
|
||||||
const res = await fetch(`${window.location.origin}/api/v1/content/category/list?count=50`, {
|
this.snackbarButtonText = buttonText;
|
||||||
method: 'GET',
|
this.snackbarButtonAction = action;
|
||||||
headers: {
|
},
|
||||||
'Accept': 'application/json',
|
resetSnackbarButton: function() {
|
||||||
|
this.snackbarButtonText = 'Ok';
|
||||||
|
this.snackbarButtonAction = () => {
|
||||||
|
this.showSnackbarNotification = false;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
notification: function(text) {
|
||||||
|
this.snackbarNotification = text;
|
||||||
|
this.showSnackbarNotification = true;
|
||||||
},
|
},
|
||||||
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
5
package-lock.json
generated
|
@ -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",
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue