replace all mentions of category with channel for better consistency
This commit is contained in:
parent
e7da5de255
commit
943ed64735
11 changed files with 175 additions and 175 deletions
|
@ -1,5 +1,5 @@
|
|||
# Brainlet
|
||||
Brainlet is a simple chat app. Each category has a text channel associated with it, all messages sent in the text channel are temporary, while in the category itself all posts are permanent.
|
||||
Brainlet is a simple chat app. Each channel has a text channel associated with it, all messages sent in the text channel are temporary, while in the channel itself all posts are permanent.
|
||||
|
||||
# Resources
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const User = require("../../models/User");
|
||||
const Category = require("../../models/Category");
|
||||
const Channel = require("../../models/Channel");
|
||||
const Post = require("../../models/Post");
|
||||
const config = require("../../config");
|
||||
const { authenticateEndpoint } = require("./../../common/auth/authfunctions");
|
||||
|
@ -17,7 +17,7 @@ const createLimiter = rateLimit({
|
|||
|
||||
mongoose.connect(config.mongoUrl, {useNewUrlParser: true, useUnifiedTopology: true});
|
||||
|
||||
app.post("/category/create", [
|
||||
app.post("/channel/create", [
|
||||
createLimiter,
|
||||
body("title").not().isEmpty().trim().isLength({ min: 3, max: 32 }).escape()
|
||||
], authenticateEndpoint(async (req, res, user) => {
|
||||
|
@ -28,7 +28,7 @@ app.post("/category/create", [
|
|||
}
|
||||
|
||||
const title = req.body.title;
|
||||
const category = await Category.create({
|
||||
const channel = await Channel.create({
|
||||
title: title,
|
||||
creator: user._id,
|
||||
posts: []
|
||||
|
@ -37,13 +37,13 @@ app.post("/category/create", [
|
|||
res.status(200).json({
|
||||
error: false,
|
||||
message: "SUCCESS_CATEGORY_CREATED",
|
||||
category: category.getPublicObject()
|
||||
channel: channel.getPublicObject()
|
||||
});
|
||||
}, undefined, config.roleMap.USER));
|
||||
|
||||
app.post("/post/create", [
|
||||
createLimiter,
|
||||
body("category").not().isEmpty().trim().escape().isLength({ min: 24, max: 24 }),
|
||||
body("channel").not().isEmpty().trim().escape().isLength({ min: 24, max: 24 }),
|
||||
body("title").not().isEmpty().trim().isLength({ min: 3, max: 32 }).escape(),
|
||||
body("body").not().isEmpty().trim().isLength({ min: 3, max: 1000 }).escape(),
|
||||
], authenticateEndpoint(async (req, res, user) => {
|
||||
|
@ -53,7 +53,7 @@ app.post("/post/create", [
|
|||
return;
|
||||
}
|
||||
|
||||
const category = req.body.category;
|
||||
const channel = req.body.channel;
|
||||
const title = req.body.title;
|
||||
const content = req.body.body;
|
||||
|
||||
|
@ -61,10 +61,10 @@ app.post("/post/create", [
|
|||
post.title = title;
|
||||
post.body = content;
|
||||
post.creator = user._id;
|
||||
post.category = category;
|
||||
post.channel = channel;
|
||||
|
||||
const r = await Category.updateOne({
|
||||
_id: category
|
||||
const r = await Channel.updateOne({
|
||||
_id: channel
|
||||
}, {
|
||||
$push: { posts: post }
|
||||
});
|
||||
|
@ -86,8 +86,8 @@ app.post("/post/create", [
|
|||
});
|
||||
}, undefined, config.roleMap.USER));
|
||||
|
||||
app.get("/category/:category/info", [
|
||||
param("category").not().isEmpty().trim().escape().isLength({ min: 24, max: 24 })
|
||||
app.get("/channel/:channel/info", [
|
||||
param("channel").not().isEmpty().trim().escape().isLength({ min: 24, max: 24 })
|
||||
], authenticateEndpoint(async (req, res) => {
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
|
@ -95,13 +95,13 @@ app.get("/category/:category/info", [
|
|||
return;
|
||||
}
|
||||
|
||||
const categoryId = req.params.category;
|
||||
const category = await Category.findById(categoryId).populate("posts.creator", User.getPulicFields());
|
||||
const channelId = req.params.channel;
|
||||
const channel = await Channel.findById(channelId).populate("posts.creator", User.getPulicFields());
|
||||
|
||||
// TODO: Implement subscribing to a channel and stuff
|
||||
const users = await User.find().sort({ _id: -1 }).limit(50).select(User.getPulicFields());
|
||||
|
||||
if (!category) {
|
||||
if (!channel) {
|
||||
res.status(404).json({
|
||||
error: true,
|
||||
message: "ERROR_CATEGORY_NOT_FOUND"
|
||||
|
@ -112,7 +112,7 @@ app.get("/category/:category/info", [
|
|||
res.status(200).json({
|
||||
error: false,
|
||||
message: "SUCCESS_CATEGORY_DATA_FETCHED",
|
||||
category: category.getPublicObject(),
|
||||
channel: channel.getPublicObject(),
|
||||
userInfo: {
|
||||
userListLimit: 50,
|
||||
users: users
|
||||
|
@ -120,19 +120,19 @@ app.get("/category/:category/info", [
|
|||
});
|
||||
}, undefined, config.roleMap.USER));
|
||||
|
||||
app.get("/category/list", authenticateEndpoint(async (req, res) => {
|
||||
app.get("/channel/list", authenticateEndpoint(async (req, res) => {
|
||||
let count = parseInt(req.query.count);
|
||||
if (!Number.isInteger(count)) {
|
||||
count = 10;
|
||||
}
|
||||
|
||||
// TODO: This is probably not efficient
|
||||
const categories = await Category.find().lean().sort({ _id: -1 }).limit(count).select("-posts -__v").populate("creator", User.getPulicFields());
|
||||
const channels = await Channel.find().lean().sort({ _id: -1 }).limit(count).select("-posts -__v").populate("creator", User.getPulicFields());
|
||||
|
||||
res.status(200).json({
|
||||
error: false,
|
||||
message: "SUCCESS_CATEGORY_LIST_FETCHED",
|
||||
categories
|
||||
channels
|
||||
});
|
||||
}, undefined, config.roleMap.USER));
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const User = require("../../../models/User");
|
||||
const secret = require("../../../secret");
|
||||
const config = require("../../../config");
|
||||
const Category = require("../../../models/Category");
|
||||
const Channel = require("../../../models/Channel");
|
||||
const RateLimiter = require("../../../common/util/ratelimiter");
|
||||
|
||||
const jwt = require("jsonwebtoken");
|
||||
|
@ -23,15 +23,15 @@ class GatewayServer {
|
|||
}
|
||||
}
|
||||
|
||||
GatewayServer.prototype._sendSystemMessage = function(socket, message, category) {
|
||||
GatewayServer.prototype._sendSystemMessage = function(socket, message, channel) {
|
||||
const messageObject = {
|
||||
author: {
|
||||
username: "__SYSTEM",
|
||||
_id: "5fc69864f15a7c5e504c9a1f"
|
||||
},
|
||||
category: {
|
||||
title: category.title,
|
||||
_id: category._id
|
||||
channel: {
|
||||
title: channel.title,
|
||||
_id: channel._id
|
||||
},
|
||||
content: message,
|
||||
_id: uuid.v4()
|
||||
|
@ -53,7 +53,7 @@ GatewayServer.prototype._processCommand = async function(socket, message) {
|
|||
|
||||
switch (command) {
|
||||
case "INVALID_COMMAND": {
|
||||
this._sendSystemMessage(socket, "Invalid command.", message.category);
|
||||
this._sendSystemMessage(socket, "Invalid command.", message.channel);
|
||||
break;
|
||||
}
|
||||
case "admin/fr": {
|
||||
|
@ -61,33 +61,33 @@ GatewayServer.prototype._processCommand = async function(socket, message) {
|
|||
if (socket.user.permissionLevel >= config.roleMap.ADMIN) {
|
||||
this._gateway.emit("refreshClient", { reason: fullCommand[1] || "REFRESH" });
|
||||
} else {
|
||||
this._sendSystemMessage(socket, "how about no", message.category);
|
||||
this._sendSystemMessage(socket, "how about no", message.channel);
|
||||
}
|
||||
} else {
|
||||
this._sendSystemMessage(socket, "Invalid number of arguments.", message.category);
|
||||
this._sendSystemMessage(socket, "Invalid number of arguments.", message.channel);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "admin/fru": {
|
||||
if (args === 1) {
|
||||
if (socket.user.permissionLevel >= config.roleMap.ADMIN) {
|
||||
const user = await this._findSocketInRoom(message.category._id, fullCommand[1]);
|
||||
const user = await this._findSocketInRoom(message.channel._id, fullCommand[1]);
|
||||
if (!user) {
|
||||
this._sendSystemMessage(socket, "User not found.", message.category);
|
||||
this._sendSystemMessage(socket, "User not found.", message.channel);
|
||||
break;
|
||||
}
|
||||
|
||||
this._gateway.in(user.user.sid).emit("refreshClient", { reason: "REFRESH" });
|
||||
} else {
|
||||
this._sendSystemMessage(socket, "how about no", message.category);
|
||||
this._sendSystemMessage(socket, "how about no", message.channel);
|
||||
}
|
||||
} else {
|
||||
this._sendSystemMessage(socket, "Invalid number of arguments.", message.category);
|
||||
this._sendSystemMessage(socket, "Invalid number of arguments.", message.channel);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
this._sendSystemMessage(socket, "That command does not exist.", message.category);
|
||||
this._sendSystemMessage(socket, "That command does not exist.", message.channel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -170,8 +170,8 @@ GatewayServer.prototype.eventSetup = function() {
|
|||
console.log(`[*] [gateway] [handshake] Got yoo from ${socket.user.username}, connection is finally completed!`);
|
||||
socket.isConnected = true;
|
||||
|
||||
socket.on("message", async ({ category, content, nickAuthor, destUser }) => {
|
||||
if (!category || !content || !socket.joinedCategories || !socket.isConnected || !socket.user || !(typeof content === "string") || !(typeof category._id === "string")) return;
|
||||
socket.on("message", async ({ channel, content, nickAuthor, destUser }) => {
|
||||
if (!channel || !content || !socket.joinedChannels || !socket.isConnected || !socket.user || !(typeof content === "string") || !(typeof channel._id === "string")) return;
|
||||
content = content.trim();
|
||||
if (!content || content === "" || content === " " || content.length >= 2000) return;
|
||||
if (!this.rateLimiter.consoom(socket.user.token)) { // TODO: maybe user ip instead of token?
|
||||
|
@ -179,9 +179,9 @@ GatewayServer.prototype.eventSetup = function() {
|
|||
return;
|
||||
}
|
||||
|
||||
// TODO: When/if category permissions are added, check if the user has permissions for that category
|
||||
const categoryTitle = socket.joinedCategories[category._id];
|
||||
if (!categoryTitle || !(typeof categoryTitle === "string")) return;
|
||||
// TODO: When/if channel permissions are added, check if the user has permissions for that channel
|
||||
const channelTitle = socket.joinedChannels[channel._id];
|
||||
if (!channelTitle || !(typeof channelTitle === "string")) return;
|
||||
|
||||
let messageObject = {
|
||||
author: {
|
||||
|
@ -189,9 +189,9 @@ GatewayServer.prototype.eventSetup = function() {
|
|||
_id: socket.user._id,
|
||||
color: socket.user.color
|
||||
},
|
||||
category: {
|
||||
title: categoryTitle,
|
||||
_id: category._id
|
||||
channel: {
|
||||
title: channelTitle,
|
||||
_id: channel._id
|
||||
},
|
||||
content: content,
|
||||
_id: uuid.v4()
|
||||
|
@ -214,32 +214,32 @@ GatewayServer.prototype.eventSetup = function() {
|
|||
}
|
||||
|
||||
if (destUser && destUser._id && (typeof destUser._id) === "string") {
|
||||
const user = await this._findSocketInRoom(messageObject.category._id, destUser._id);
|
||||
const user = await this._findSocketInRoom(messageObject.channel._id, destUser._id);
|
||||
if (!user) return;
|
||||
|
||||
this._gateway.in(user.user.sid).emit("message", messageObject);
|
||||
return;
|
||||
}
|
||||
|
||||
this._gateway.in(category._id).emit("message", messageObject);
|
||||
this._gateway.in(channel._id).emit("message", messageObject);
|
||||
});
|
||||
|
||||
socket.on("subscribe", async (categories) => {
|
||||
if ( !socket.isConnected || !socket.user || !categories || !Array.isArray(categories) || categories === []) return;
|
||||
socket.on("subscribe", async (channels) => {
|
||||
if ( !socket.isConnected || !socket.user || !channels || !Array.isArray(channels) || channels === []) return;
|
||||
try {
|
||||
for (const v of categories) {
|
||||
for (const v of channels) {
|
||||
if (!v && !(typeof v === "string")) continue;
|
||||
// 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 = {};
|
||||
if (socket.joinedCategories[v]) continue;
|
||||
socket.joinedCategories[v] = category.title;
|
||||
// TODO: When/if channel permissions are added, check if the user has permissions for that channel
|
||||
const channel = await Channel.findById(v);
|
||||
if (channel && channel.title && channel._id) {
|
||||
if (!socket.joinedChannels) socket.joinedChannels = {};
|
||||
if (socket.joinedChannels[v]) continue;
|
||||
socket.joinedChannels[v] = channel.title;
|
||||
await socket.join(v);
|
||||
|
||||
console.log(`[*] [gateway] User ${socket.user.username} subscribed to room ${v} (${category.title}), sending updated user list to all members of that room...`);
|
||||
console.log(`[*] [gateway] User ${socket.user.username} subscribed to room ${v} (${channel.title}), sending updated user list to all members of that room...`);
|
||||
|
||||
const upd = await this._generateClientListUpdateObject(v, category.title);
|
||||
const upd = await this._generateClientListUpdateObject(v, channel.title);
|
||||
this._gateway.in(v).emit("clientListUpdate", upd);
|
||||
}
|
||||
}
|
||||
|
@ -255,10 +255,10 @@ GatewayServer.prototype.eventSetup = function() {
|
|||
// Socket io automatically adds a user to a room with their own id
|
||||
if (room === socket.id) return;
|
||||
|
||||
const categoryTitle = socket.joinedCategories[room] || "UNKNOWN";
|
||||
const channelTitle = socket.joinedChannels[room] || "UNKNOWN";
|
||||
await socket.leave(room);
|
||||
|
||||
const upd = await this._generateClientListUpdateObject(room, categoryTitle);
|
||||
const upd = await this._generateClientListUpdateObject(room, channelTitle);
|
||||
socket.in(room).emit("clientListUpdate", upd);
|
||||
});
|
||||
});
|
||||
|
@ -309,11 +309,11 @@ GatewayServer.prototype._findSocketInRoom = async function(room, userid) {
|
|||
return updatedClientList[0] || undefined;
|
||||
};
|
||||
|
||||
GatewayServer.prototype._generateClientListUpdateObject = async function(room, categoryTitle="UNKNOWN") {
|
||||
GatewayServer.prototype._generateClientListUpdateObject = async function(room, channelTitle="UNKNOWN") {
|
||||
const clientList = await this._getSocketsInRoom(room);
|
||||
return {
|
||||
category: {
|
||||
title: categoryTitle,
|
||||
channel: {
|
||||
title: channelTitle,
|
||||
_id: room
|
||||
},
|
||||
clientList
|
||||
|
|
|
@ -3,7 +3,7 @@ const EventEmitter = require("events");
|
|||
const uuid = require("uuid");
|
||||
|
||||
const User = require("../../../models/User");
|
||||
const Category = require("../../../models/Category");
|
||||
const Channel = require("../../../models/Channel");
|
||||
const { parseMessage, opcodeSeparator, getOpcodeByName } = require("./messageparser");
|
||||
const { checkToken } = require("../../../common/auth/authfunctions");
|
||||
|
||||
|
@ -57,7 +57,7 @@ class GatewayServer extends EventEmitter {
|
|||
// The user is now successfully authenticated, send the YOO_ACK packet
|
||||
// TODO: This is probably not efficient
|
||||
|
||||
let channels = await Category.find().lean().sort({ _id: -1 }).limit(50).select("-posts -__v").populate("creator", User.getPulicFields(true));
|
||||
let channels = await Channel.find().lean().sort({ _id: -1 }).limit(50).select("-posts -__v").populate("creator", User.getPulicFields(true));
|
||||
if (!channels) channels = [];
|
||||
channels = channels.map(x => ({ ...x, _id: x._id.toString() }));
|
||||
|
||||
|
|
|
@ -77,24 +77,24 @@
|
|||
</md-dialog-content>
|
||||
</md-dialog>
|
||||
|
||||
<md-dialog id="create-category-dialog" :md-active.sync="dialog.show.createCategory">
|
||||
<md-dialog-title>Create category</md-dialog-title>
|
||||
<md-dialog id="create-channel-dialog" :md-active.sync="dialog.show.createChannel">
|
||||
<md-dialog-title>Create channel</md-dialog-title>
|
||||
|
||||
<md-dialog-content>
|
||||
<md-field>
|
||||
<label>Title</label>
|
||||
<md-input v-model="dialog.text.createCategory.title"></md-input>
|
||||
<md-input v-model="dialog.text.createChannel.title"></md-input>
|
||||
</md-field>
|
||||
|
||||
<md-dialog-actions>
|
||||
<md-button @click="dialog.show.createCategory = false">Close</md-button>
|
||||
<md-button class="md-primary" @click="createCategory()">Create</md-button>
|
||||
<md-button @click="dialog.show.createChannel = false">Close</md-button>
|
||||
<md-button class="md-primary" @click="createChannel()">Create</md-button>
|
||||
</md-dialog-actions>
|
||||
</md-dialog-content>
|
||||
</md-dialog>
|
||||
|
||||
<md-dialog id="create-post-dialog" :md-active.sync="dialog.show.createPost">
|
||||
<md-dialog-title>Create post for <strong>{{ selection.category.title }}</strong></md-dialog-title>
|
||||
<md-dialog-title>Create post for <strong>{{ selection.channel.title }}</strong></md-dialog-title>
|
||||
|
||||
<md-dialog-content>
|
||||
<md-field>
|
||||
|
@ -137,19 +137,19 @@
|
|||
</md-menu>
|
||||
</md-toolbar>
|
||||
|
||||
<md-toolbar v-show="selection.category.browsing" class="md-dense" md-elevation="5">
|
||||
<h3 v-if="selection.category.isCategory && !selection.category.isChatContext" class="md-title" style="flex: 1">Browsing category: {{ selection.category.title }}</h3>
|
||||
<h3 v-if="!selection.category.isCategory && !selection.category.isChatContext" class="md-title" style="flex: 1">Browsing {{ selection.category.title }}</h3>
|
||||
<h3 v-if="!selection.category.isCategory && selection.category.isChatContext" class="md-title" style="flex: 1">
|
||||
Browsing {{ selection.category.title }} with
|
||||
<a v-for="user in userLists[selection.category._id]" class="md-dense cursor" v-on:click="viewProfile(user.user._id)" v-bind:style="{ 'color': user.user.color }">{{ user.user.username }} </a>
|
||||
<md-toolbar v-show="selection.channel.browsing" class="md-dense" md-elevation="5">
|
||||
<h3 v-if="selection.channel.isChannel && !selection.channel.isChatContext" class="md-title" style="flex: 1">Browsing channel: {{ selection.channel.title }}</h3>
|
||||
<h3 v-if="!selection.channel.isChannel && !selection.channel.isChatContext" class="md-title" style="flex: 1">Browsing {{ selection.channel.title }}</h3>
|
||||
<h3 v-if="!selection.channel.isChannel && selection.channel.isChatContext" class="md-title" style="flex: 1">
|
||||
Browsing {{ selection.channel.title }} with
|
||||
<a v-for="user in userLists[selection.channel._id]" class="md-dense cursor" v-on:click="viewProfile(user.user._id)" v-bind:style="{ 'color': user.user.color }">{{ user.user.username }} </a>
|
||||
</h3>
|
||||
<md-button @click="browseCategories()" v-if="selection.category.isCategory || selection.category.isChatContext"><md-icon>arrow_back</md-icon></md-button>
|
||||
<md-button @click="refresh()" v-if="!selection.category.isChatContext"><md-icon>refresh</md-icon></md-button>
|
||||
<md-button @click="browseChannels()" v-if="selection.channel.isChannel || selection.channel.isChatContext"><md-icon>arrow_back</md-icon></md-button>
|
||||
<md-button @click="refresh()" v-if="!selection.channel.isChatContext"><md-icon>refresh</md-icon></md-button>
|
||||
</md-toolbar>
|
||||
|
||||
<div id="posts-container" class="posts-container" v-if="selection.category.browsing">
|
||||
<md-card v-for="post in selection.posts" v-bind:key="post._id" v-if="!selection.category.isChatContext">
|
||||
<div id="posts-container" class="posts-container" v-if="selection.channel.browsing">
|
||||
<md-card v-for="post in selection.posts" v-bind:key="post._id" v-if="!selection.channel.isChatContext">
|
||||
<md-card-header>
|
||||
<div class="md-title" v-html="post.title"></div>
|
||||
<span>by <a class="md-dense cursor" v-on:click="viewProfile(post.creator._id)" v-bind:style="{ 'color': post.creator.color}">{{ post.creator.username }}</a></span>
|
||||
|
@ -161,7 +161,7 @@
|
|||
<md-button v-for="button in cardButtons" v-bind:key="button.text" @click="button.click(post)"><md-icon>{{ button.text }}</md-icon></md-button>
|
||||
</md-card-actions>
|
||||
</md-card>
|
||||
<div v-for="post,k in messages[selection.category._id]" v-if="selection.category.isChatContext" :key="post._id + post.author._id">
|
||||
<div v-for="post,k in messages[selection.channel._id]" v-if="selection.channel.isChatContext" :key="post._id + post.author._id">
|
||||
<md-card class="message-card">
|
||||
<md-card-header>
|
||||
<a v-if="!post.nickAuthor" class="md-dense cursor md-title" v-on:click="viewProfile(post.author._id)" v-bind:style="{ 'color': post.author.color}"><span>{{ post.author.username }}</span></a>
|
||||
|
@ -174,26 +174,26 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<md-speed-dial class="md-fixed md-bottom-right" md-direction="top" md-elevation="5" style="z-index: 4000;" v-show="!selection.category.isChatContext">
|
||||
<md-speed-dial class="md-fixed md-bottom-right" md-direction="top" md-elevation="5" style="z-index: 4000;" v-show="!selection.channel.isChatContext">
|
||||
<md-speed-dial-target>
|
||||
<md-icon class="md-morph-initial">add</md-icon>
|
||||
<md-icon class="md-morph-final">edit</md-icon>
|
||||
</md-speed-dial-target>
|
||||
|
||||
<md-speed-dial-content>
|
||||
<md-button v-show="selection.category.isCategory" class="md-icon-button" @click="showCreatePostDialog()">
|
||||
<md-button v-show="selection.channel.isChannel" class="md-icon-button" @click="showCreatePostDialog()">
|
||||
<md-icon>add</md-icon>
|
||||
<md-tooltip md-direction="left">Create a new post</md-tooltip>
|
||||
</md-button>
|
||||
|
||||
<md-button class="md-icon-button" @click="dialog.show.createCategory = true">
|
||||
<md-button class="md-icon-button" @click="dialog.show.createChannel = true">
|
||||
<md-icon>category</md-icon>
|
||||
<md-tooltip md-direction="left">Create a new category</md-tooltip>
|
||||
<md-tooltip md-direction="left">Create a new channel</md-tooltip>
|
||||
</md-button>
|
||||
</md-speed-dial-content>
|
||||
</md-speed-dial>
|
||||
|
||||
<md-field md-inline class="chat-bar" v-show="selection.category.isChatContext">
|
||||
<md-field md-inline class="chat-bar" v-show="selection.channel.isChatContext">
|
||||
<label>Write something interesting, go on!</label>
|
||||
<md-input v-model="message.typed" v-on:keyup.enter="sendCurrentMessage()"></md-input>
|
||||
</md-field>
|
||||
|
|
|
@ -10,8 +10,8 @@ const getCreatePostError = (json) => {
|
|||
case 'body': {
|
||||
return 'Invalid content. Must be between 3 and 1000 characters';
|
||||
}
|
||||
case 'category': {
|
||||
return 'Invalid category. Something went wrong.';
|
||||
case 'channel': {
|
||||
return 'Invalid channel. Something went wrong.';
|
||||
}
|
||||
default: {
|
||||
return 'Invalid value sent to server. Something went wrong.';
|
||||
|
@ -20,7 +20,7 @@ const getCreatePostError = (json) => {
|
|||
}
|
||||
|
||||
case 'ERROR_CATEGORY_NOT_FOUND': {
|
||||
return 'The category you tried to post to no longer exists.';
|
||||
return 'The channel you tried to post to no longer exists.';
|
||||
}
|
||||
|
||||
case 'ERROR_ACCESS_DENIED': {
|
||||
|
@ -33,7 +33,7 @@ const getCreatePostError = (json) => {
|
|||
}
|
||||
}
|
||||
|
||||
const getCreateCategoryError = (json) => {
|
||||
const getCreateChannelError = (json) => {
|
||||
switch (json.message) {
|
||||
case 'ERROR_REQUEST_INVALID_DATA': {
|
||||
switch (json.errors[0].param) {
|
||||
|
@ -109,22 +109,22 @@ GatewayConnection.prototype.connect = function(token) {
|
|||
});
|
||||
};
|
||||
|
||||
GatewayConnection.prototype.sendMessage = function(categoryId, content) {
|
||||
GatewayConnection.prototype.sendMessage = function(channelId, content) {
|
||||
if (!this.isConnected) return 1;
|
||||
if (content.length >= 2000) return 1;
|
||||
|
||||
this.socket.emit('message', {
|
||||
category: {
|
||||
_id: categoryId
|
||||
channel: {
|
||||
_id: channelId
|
||||
},
|
||||
content
|
||||
});
|
||||
};
|
||||
|
||||
GatewayConnection.prototype.subscribeToCategoryChat = function(categoryId) {
|
||||
GatewayConnection.prototype.subscribeToChannelChat = function(channelId) {
|
||||
if (!this.isConnected) return;
|
||||
|
||||
const request = [categoryId];
|
||||
const request = [channelId];
|
||||
|
||||
console.log('[*] [gateway] Subscribing to channel(s)', request);
|
||||
|
||||
|
@ -143,11 +143,11 @@ const app = new Vue({
|
|||
showApp: false,
|
||||
menuVisible: false,
|
||||
selection: {
|
||||
category: {
|
||||
channel: {
|
||||
title: '',
|
||||
browsing: false,
|
||||
_id: undefined,
|
||||
isCategory: false,
|
||||
isChannel: false,
|
||||
isChatContext: false
|
||||
},
|
||||
posts: []
|
||||
|
@ -156,7 +156,7 @@ const app = new Vue({
|
|||
dialog: {
|
||||
show: {
|
||||
createPost: false,
|
||||
createCategory: false,
|
||||
createChannel: false,
|
||||
debug: false
|
||||
},
|
||||
text: {
|
||||
|
@ -164,7 +164,7 @@ const app = new Vue({
|
|||
title: '',
|
||||
body: ''
|
||||
},
|
||||
createCategory: {
|
||||
createChannel: {
|
||||
title: ''
|
||||
}
|
||||
}
|
||||
|
@ -201,7 +201,7 @@ const app = new Vue({
|
|||
this.loggedInUser = json.user;
|
||||
this.showApp = true;
|
||||
this.performGatewayConnection();
|
||||
this.browseCategories();
|
||||
this.browseChannels();
|
||||
Notification.requestPermission();
|
||||
} else {
|
||||
this.showApp = false;
|
||||
|
@ -259,9 +259,9 @@ const app = new Vue({
|
|||
});
|
||||
});
|
||||
},
|
||||
shouldMergeMessage: function(messageObject, categoryMessageList) {
|
||||
const lastMessageIndex = categoryMessageList.length-1;
|
||||
const lastMessage = categoryMessageList[lastMessageIndex];
|
||||
shouldMergeMessage: function(messageObject, channelMessageList) {
|
||||
const lastMessageIndex = channelMessageList.length-1;
|
||||
const lastMessage = channelMessageList[lastMessageIndex];
|
||||
|
||||
if (!lastMessage) return;
|
||||
if (lastMessage.author._id === messageObject.author._id) {
|
||||
|
@ -277,32 +277,32 @@ const app = new Vue({
|
|||
return false;
|
||||
},
|
||||
processMessage: async function(messageObject) {
|
||||
if (!this.messages[messageObject.category._id]) this.$set(this.messages, messageObject.category._id, []);
|
||||
const categoryMessageList = this.messages[messageObject.category._id];
|
||||
const lastMessageIndex = categoryMessageList.length-1;
|
||||
if (!this.messages[messageObject.channel._id]) this.$set(this.messages, messageObject.channel._id, []);
|
||||
const channelMessageList = this.messages[messageObject.channel._id];
|
||||
const lastMessageIndex = channelMessageList.length-1;
|
||||
|
||||
if (this.shouldMergeMessage(messageObject, categoryMessageList)) {
|
||||
categoryMessageList[lastMessageIndex].content += `\n${messageObject.content}`;
|
||||
if (this.shouldMergeMessage(messageObject, channelMessageList)) {
|
||||
channelMessageList[lastMessageIndex].content += `\n${messageObject.content}`;
|
||||
} else {
|
||||
this.messages[messageObject.category._id].push(messageObject);
|
||||
this.messages[messageObject.channel._id].push(messageObject);
|
||||
}
|
||||
|
||||
if (messageObject.category._id === this.selection.category._id) {
|
||||
if (messageObject.channel._id === this.selection.channel._id) {
|
||||
this.$nextTick(() => {
|
||||
// TODO: When the user presses back, actually undo this scroll cause its annoying to scroll back up in the category list
|
||||
// TODO: When the user presses back, actually undo this scroll cause its annoying to scroll back up in the channel list
|
||||
const container = this.$el.querySelector('#posts-container');
|
||||
container.scrollTop = container.scrollHeight;
|
||||
});
|
||||
}
|
||||
|
||||
if (messageObject.author.username !== this.loggedInUser.username && messageObject.category._id !== this.selection.category._id) {
|
||||
this.okNotification(`${messageObject.category.title}/${messageObject.author.username}: ${messageObject.content}`);
|
||||
if (messageObject.author.username !== this.loggedInUser.username && messageObject.channel._id !== this.selection.channel._id) {
|
||||
this.okNotification(`${messageObject.channel.title}/${messageObject.author.username}: ${messageObject.content}`);
|
||||
}
|
||||
|
||||
if (messageObject.author.username !== this.loggedInUser.username) {
|
||||
if (Notification.permission === 'granted') {
|
||||
try {
|
||||
new Notification(`${messageObject.category.title}/${messageObject.author.username}`, {
|
||||
new Notification(`${messageObject.channel.title}/${messageObject.author.username}`, {
|
||||
body: messageObject.content
|
||||
});
|
||||
} catch(e) {
|
||||
|
@ -313,20 +313,20 @@ const app = new Vue({
|
|||
}
|
||||
},
|
||||
processUserListUpdate: async function(e) {
|
||||
const { category, clientList } = e;
|
||||
if (!this.userLists[category._id]) this.$set(this.userLists, category._id, []);
|
||||
this.userLists[category._id] = clientList;
|
||||
const { channel, clientList } = e;
|
||||
if (!this.userLists[channel._id]) this.$set(this.userLists, channel._id, []);
|
||||
this.userLists[channel._id] = clientList;
|
||||
},
|
||||
openChatForCategory: async function(categoryId) {
|
||||
this.gateway.subscribeToCategoryChat(categoryId);
|
||||
openChatForChannel: async function(channelId) {
|
||||
this.gateway.subscribeToChannelChat(channelId);
|
||||
|
||||
this.selection.category.isChatContext = true;
|
||||
this.selection.category.browsing = true;
|
||||
this.selection.category.title = 'Chat';
|
||||
this.selection.category._id = categoryId;
|
||||
this.selection.channel.isChatContext = true;
|
||||
this.selection.channel.browsing = true;
|
||||
this.selection.channel.title = 'Chat';
|
||||
this.selection.channel._id = channelId;
|
||||
},
|
||||
sendCurrentMessage: async function() {
|
||||
const status = await this.gateway.sendMessage(this.selection.category._id, this.message.typed);
|
||||
const status = await this.gateway.sendMessage(this.selection.channel._id, this.message.typed);
|
||||
if (status === 1) {
|
||||
this.okNotification('Failed to send message!');
|
||||
return;
|
||||
|
@ -343,9 +343,9 @@ const app = new Vue({
|
|||
console.log('[DEBUG DUMP] [loggedInUser] (this contains sensitive information about the current logged in user, do not leak it to other people lol)', this.loggedInUser);
|
||||
},
|
||||
|
||||
// Category and post browsing
|
||||
browseCategories: async function() {
|
||||
const res = await fetch(`${window.location.origin}/api/v1/content/category/list?count=50`, {
|
||||
// Channel and post browsing
|
||||
browseChannels: async function() {
|
||||
const res = await fetch(`${window.location.origin}/api/v1/content/channel/list?count=50`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
|
@ -356,36 +356,36 @@ const app = new Vue({
|
|||
if (res.ok) {
|
||||
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.category._id = '__CATEGORY_LIST';
|
||||
this.selection.channel.title = 'channels';
|
||||
this.selection.channel.browsing = true;
|
||||
this.selection.channel.isChannel = false;
|
||||
this.selection.channel.isChatContext = false;
|
||||
this.selection.channel._id = '__CATEGORY_LIST';
|
||||
this.selection.posts = [];
|
||||
|
||||
this.cardButtons = [];
|
||||
|
||||
this.button('chat', (post) => {
|
||||
if (post._id) {
|
||||
this.openChatForCategory(post._id);
|
||||
this.openChatForChannel(post._id);
|
||||
}
|
||||
});
|
||||
this.button('topic', (post) => {
|
||||
this.browse(post);
|
||||
});
|
||||
|
||||
for (let i = 0; i < json.categories.length; i++) {
|
||||
const v = json.categories[i];
|
||||
for (let i = 0; i < json.channels.length; i++) {
|
||||
const v = json.channels[i];
|
||||
this.selection.posts.push({ title: v.title, body: '', _id: v._id, creator: v.creator });
|
||||
}
|
||||
} else {
|
||||
this.okNotification('Failed to fetch category list');
|
||||
this.okNotification('Failed to fetch channel list');
|
||||
}
|
||||
},
|
||||
browse: async function(category) {
|
||||
const { _id, title } = category;
|
||||
browse: async function(channel) {
|
||||
const { _id, title } = channel;
|
||||
|
||||
const res = await fetch(`${window.location.origin}/api/v1/content/category/${_id}/info`, {
|
||||
const res = await fetch(`${window.location.origin}/api/v1/content/channel/${_id}/info`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
|
@ -396,39 +396,39 @@ const app = new Vue({
|
|||
if (res.ok) {
|
||||
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.channel.title = title;
|
||||
this.selection.channel._id = _id;
|
||||
this.selection.channel.browsing = true;
|
||||
this.selection.channel.isChannel = true;
|
||||
this.selection.channel.isChatContext = false;
|
||||
this.selection.posts = [];
|
||||
|
||||
this.cardButtons = [];
|
||||
|
||||
for (let i = 0; i < json.category.posts.length; i++) {
|
||||
const v = json.category.posts[i];
|
||||
for (let i = 0; i < json.channel.posts.length; i++) {
|
||||
const v = json.channel.posts[i];
|
||||
this.selection.posts.push({ title: v.title, body: v.body, _id: v._id, creator: v.creator });
|
||||
}
|
||||
} else {
|
||||
this.okNotification('Failed to fetch category');
|
||||
this.okNotification('Failed to fetch channel');
|
||||
}
|
||||
},
|
||||
refresh: function() {
|
||||
if (this.selection.category.title === 'categories' && this.selection.category.isCategory === false) {
|
||||
this.browseCategories();
|
||||
if (this.selection.channel.title === 'channels' && this.selection.channel.isChannel === false) {
|
||||
this.browseChannels();
|
||||
} else {
|
||||
this.browse(this.selection.category);
|
||||
this.browse(this.selection.channel);
|
||||
}
|
||||
},
|
||||
button: function(text, click) {
|
||||
this.cardButtons.push({ text, click });
|
||||
},
|
||||
stopBrowsing: function() {
|
||||
this.selection.category = {
|
||||
this.selection.channel = {
|
||||
title: '',
|
||||
browsing: false,
|
||||
_id: undefined,
|
||||
isCategory: false,
|
||||
isChannel: false,
|
||||
isChatContext: false
|
||||
};
|
||||
this.selection.posts = [];
|
||||
|
@ -457,20 +457,20 @@ const app = new Vue({
|
|||
|
||||
// Content creation
|
||||
showCreatePostDialog: function() {
|
||||
if (!this.selection.category.isCategory) {
|
||||
this.okNotification('You are not in a category');
|
||||
if (!this.selection.channel.isChannel) {
|
||||
this.okNotification('You are not in a channel');
|
||||
return;
|
||||
}
|
||||
|
||||
this.dialog.show.createPost = true;
|
||||
},
|
||||
createPost: async function() {
|
||||
if (!this.selection.category.isCategory) {
|
||||
this.okNotification('You are not in a category');
|
||||
if (!this.selection.channel.isChannel) {
|
||||
this.okNotification('You are not in a channel');
|
||||
return;
|
||||
}
|
||||
|
||||
const category = this.selection.category;
|
||||
const channel = this.selection.channel;
|
||||
const input = this.dialog.text.createPost;
|
||||
|
||||
const res = await fetch(`${window.location.origin}/api/v1/content/post/create`, {
|
||||
|
@ -481,7 +481,7 @@ const app = new Vue({
|
|||
},
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({
|
||||
category: category._id,
|
||||
channel: channel._id,
|
||||
title: input.title,
|
||||
body: input.body
|
||||
})
|
||||
|
@ -490,7 +490,7 @@ const app = new Vue({
|
|||
if (res.ok) {
|
||||
this.okNotification('Successfully created post');
|
||||
this.dialog.show.createPost = false;
|
||||
this.browse(this.selection.category);
|
||||
this.browse(this.selection.channel);
|
||||
return;
|
||||
} else {
|
||||
if (res.status === 401 || res.status === 403) {
|
||||
|
@ -507,10 +507,10 @@ const app = new Vue({
|
|||
return;
|
||||
}
|
||||
},
|
||||
createCategory: async function() {
|
||||
const input = this.dialog.text.createCategory;
|
||||
createChannel: async function() {
|
||||
const input = this.dialog.text.createChannel;
|
||||
|
||||
const res = await fetch(`${window.location.origin}/api/v1/content/category/create`, {
|
||||
const res = await fetch(`${window.location.origin}/api/v1/content/channel/create`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
|
@ -523,9 +523,9 @@ const app = new Vue({
|
|||
});
|
||||
|
||||
if (res.ok) {
|
||||
this.okNotification('Successfully created category');
|
||||
this.dialog.show.createCategory = false;
|
||||
this.browseCategories();
|
||||
this.okNotification('Successfully created channel');
|
||||
this.dialog.show.createChannel = false;
|
||||
this.browseChannels();
|
||||
return;
|
||||
} else {
|
||||
if (res.status === 401 || res.status === 403) {
|
||||
|
@ -538,7 +538,7 @@ const app = new Vue({
|
|||
}
|
||||
|
||||
const json = await res.json();
|
||||
this.okNotification(getCreateCategoryError(json));
|
||||
this.okNotification(getCreateChannelError(json));
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
|
|
@ -2,13 +2,13 @@ const mongoose = require("mongoose");
|
|||
const Post = require("./Post");
|
||||
const User = require("./User");
|
||||
|
||||
const categorySchema = new mongoose.Schema({
|
||||
const channelSchema = new mongoose.Schema({
|
||||
title: String,
|
||||
creator: {type: mongoose.Schema.Types.ObjectId, ref: "User"},
|
||||
posts: [Post.schema]
|
||||
});
|
||||
|
||||
categorySchema.method("getPublicObject", function() {
|
||||
channelSchema.method("getPublicObject", function() {
|
||||
return {
|
||||
title: this.title,
|
||||
creator: this.populate("creator", User.getPulicFields()).creator,
|
||||
|
@ -17,6 +17,6 @@ categorySchema.method("getPublicObject", function() {
|
|||
};
|
||||
});
|
||||
|
||||
const Category = mongoose.model("Category", categorySchema);
|
||||
const Channel = mongoose.model("Channel", channelSchema);
|
||||
|
||||
module.exports = Category;
|
||||
module.exports = Channel;
|
|
@ -4,7 +4,7 @@ const Post = mongoose.model("Post", {
|
|||
title: String,
|
||||
body: String,
|
||||
creator: {type: mongoose.Schema.Types.ObjectId, ref: "User"},
|
||||
categoryId: {type: mongoose.Schema.Types.ObjectId, ref: "Category"}
|
||||
channelId: {type: mongoose.Schema.Types.ObjectId, ref: "Channel"}
|
||||
});
|
||||
|
||||
module.exports = Post;
|
|
@ -50,7 +50,7 @@ const main = async () => {
|
|||
|
||||
discord.login(secret.discord.token);
|
||||
|
||||
const category = client.gateway.subscribeToCategoryChat(LISTEN_ON);
|
||||
const channel = client.gateway.subscribeToChannelChat(LISTEN_ON);
|
||||
|
||||
discord.on('message', (e) => {
|
||||
if (e.webhookID) return;
|
||||
|
|
|
@ -59,13 +59,13 @@ GatewayConnection.prototype.connect = function(token) {
|
|||
this.socket.on('clientListUpdate', (e) => this.emit('clientListUpdate', e));
|
||||
};
|
||||
|
||||
GatewayConnection.prototype.sendMessage = function(categoryId, content, { nickAuthor, destUser }) {
|
||||
GatewayConnection.prototype.sendMessage = function(channelId, content, { nickAuthor, destUser }) {
|
||||
if (!this.isConnected) return 1;
|
||||
if (content.length >= 2000) return 1;
|
||||
|
||||
this.socket.emit('message', {
|
||||
category: {
|
||||
_id: categoryId
|
||||
channel: {
|
||||
_id: channelId
|
||||
},
|
||||
nickAuthor,
|
||||
destUser,
|
||||
|
@ -73,16 +73,16 @@ GatewayConnection.prototype.sendMessage = function(categoryId, content, { nickAu
|
|||
});
|
||||
};
|
||||
|
||||
GatewayConnection.prototype.subscribeToCategoryChat = function(categoryId) {
|
||||
GatewayConnection.prototype.subscribeToChannelChat = function(channelId) {
|
||||
if (!this.isConnected) return;
|
||||
|
||||
const request = [categoryId];
|
||||
const request = [channelId];
|
||||
|
||||
console.log('[*] [gateway] Subscribing to channel(s)', request);
|
||||
|
||||
this.socket.emit('subscribe', request);
|
||||
|
||||
return categoryId;
|
||||
return channelId;
|
||||
};
|
||||
|
||||
module.exports = GatewayConnection;
|
|
@ -23,7 +23,7 @@ Packets can also have JSON as a payload:
|
|||
|
||||
## Instructions
|
||||
|
||||
The terms "channel" and "category" are used interchangeably.
|
||||
The terms "channel" and "channel" are used interchangeably.
|
||||
|
||||
## 0:HELLO
|
||||
*Part of handshake, Server to client*
|
||||
|
|
Reference in a new issue