I'm trying to make a social media app, so I wrote a route that allows users to like posts, but If someone already liked it and pressed "like" again - It should remove the like, right? The point is - I can't use $pull since it uses findByIdAndUpdate, which can only be used if likes have their own separated model, but I don't have it because I think that overworks db a bit.
I use for to cycle trough an array and scan an Id of an Object and validate if it matches an Id of the logged user
const post = await Post.findById(request.params.post_id)
for (let i = 0; i < post.likes.length; i++) {
if (post.likes[i].userId === request.user.id) {
// Remove post.likes.[i] from the database completely
}
}
Here is how post looks like in Mongo documents:
{ "userId": "627eae4ed5b20f818d7a8142", "title": "Simple Post Title", "body": "Whatever",
"likes": [{ "userId": "627eae4ed5b20f818d7a8142", "likedAt": "14:58:20" }], }
If there is something else I need to provide - don't hesitate to ask.
Nevermind guys! I did it, I only needed to update the Post collection, nothing more!
for (let i = 0; i < post.likes.length; i++) {
if (post.likes[i].userId === request.user.id) {
Post
.findByIdAndUpdate(post._id, {
$pull: { likes: post.likes[i] }
}, {
new: true,
runValidators: true
})
.then((updated_post) => {
response.status(200).json({ success: true, updated_post: updated_post })
})
.catch(() => {
return next(
new ErrorResponse(`An error occurred while updating the post`, 400)
)
})
}
}
It does gives me an error, but I think I'll manage
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
Related
I'm wondering if there's anything I'm missing with how I'm trying to import my MongoDB data into a ReactJS array. It's had me stuck for the past day or so and I can't figure it out for the life of me. Here's what I'm doing so far to try to bring it in
(The data being imported is a MongoDB collection with two entries, code and name)
// find the course object with the id route parameter
let course:(Course | undefined) = courses.find(item => item._id === id);
let courseArray:any[] = [];
const checkCheckboxes = () => {
if (course !== undefined) {
for (let i:number = 0;i < courses.length;i++) {
courseArray.push({course.code + course.name});
}
}
}
Courses collection in question:
// drop collection if already exists
db.courses.drop();
// insert new documents into collection
db.courses.insert([
{
"code":"WEBD1000",
"name":"Website Development"
},
{
"code":"PROG2700",
"name":"Client Side Programming"
},
{
"code":"INET2005",
"name":"Web Application Programming I"
},
{
"code":"WEBD3000",
"name":"Web Application Programming II"
},
{
"code":"PROG3017",
"name":"Full Stack Programming"
},
{
"code":"APPD1001",
"name":"User Interface Design & Development"
},
{
"code":"WEBD3027",
"name":"Developing for Content Management Systems"
},
{
"code":"PROG1400",
"name":"Intro to Object Oriented Programming"
},
{
"code":"PROG1700",
"name":"Logic and Programming"
}
]);
Any ideas?
Maybe try this:
let course:(Course | undefined) = courses.findOne({_id: ID_GOES_HERE});
Yes I know TLDR but I would appreciate the help
Ok I have this wall of code below I use for applications
It uses a button to start the application (asking questions)
and after the application is filled in it sends that application to a specified applicationChannelId
is there any way I could add buttons to the application that was sent to the applicationChannelId to accept or deny members?
Accept would add a role by id and sends a message to the original applicant
//Not the code I tried using just an example for what the Accept button would do
//
let teamRole = message.guild.roles.cache.find(role => role.id == "761996603434598460")
member.roles.add(teamRole)
member.send("You have been accepted into the team")
Deny would send a message to the original applicant
I have tried doing this for the past few days but just can't get it to work it either does nothing or breaks everything
I have removed some info from the code below to make it shorter
Code for my button I already use to start the application for an applicant
client.on("message", async (message) => {
const reqEmbed = {
color: 0xed5181,
title: 'Blind Spot Team Requirements',
url: 'link',
author: {
name: '',
icon_url: '',
url: '',
},
description: '',
thumbnail: {
url: '',
},
fields: [{
"name": `Read through these Requirements`,
"value": `- requirments will go here`,
}, ],
image: {
url: '',
},
footer: {
text: 'Blind Spot est 2019',
icon_url: 'link',
},
};
//
// Don't reply to bots
let admins = ['741483726688747541', '741483726688747541'];
if (message.content.startsWith(`#blindspot`)) {
message.delete();
const amount = message.content.split(" ")[1];
if (!admins.includes(message.author.id)) {
message.reply("You do not have permission to do that!");
return;
}
// Perform raw API request and send a message with a button,
// since it isn't supported natively in discord.js v12
client.api.channels(message.channel.id).messages.post({
data: {
embeds: [reqEmbed],
components: [{
type: 1,
components: [{
type: 2,
style: 4,
label: "Apply",
// Our button id, we can use that later to identify,
// that the user has clicked this specific button
custom_id: "send_application"
}]
}]
}
});
}
});
Rest of code that handles questions and sends application to applicationChannelId when complete
// Channel id where the application will be sent
const applicationChannelId = "652099170835890177";
// Our questions the bot will ask the user
const questions = ["These are the questions but have deleted them to make this shorter",];
// Function that will ask a GuildMember a question and returns a reply
async function askQuestion(member, question) {
const message = await member.send(question);
const reply = await message.channel.awaitMessages((m) => {
return m.author.id === member.id;
}, {
time: 5 * 60000,
max: 1
});
return reply.first();
}
client.ws.on("INTERACTION_CREATE", async (interaction) => {
// If component type is a button
if (interaction.data.component_type === 2) {
const guildId = interaction.guild_id;
const userId = interaction.member.user.id;
const buttonId = interaction.data.custom_id;
const member = client.guilds.resolve(guildId).member(userId);
if (buttonId == "send_application") {
// Reply to an interaction, so we don't get "This interaction failed" error
client.api.interactions(interaction.id, interaction.token).callback.post({
data: {
type: 4,
data: {
content: "I have started the application process in your DM's.",
flags: 64 // make the message ephemeral
}
}
});
try {
// Create our application, we will fill it later
const application = new MessageEmbed()
.setTitle("New Application")
.setDescription(`This application was submitted by ${member}/${member.user.tag}`)
.setFooter("If the #Username doesn't appear please go to general chat and scroll through the member list")
.setColor("#ED4245");
const cancel = () => member.send("Your application has been canceled.\n**If you would like to start your application again Click on the Apply button in** <#657393981851697153>");
// Ask the user if he wants to continue
const reply = await askQuestion(member, "Please fill in this form so we can proceed with your tryout.\n" + "**Type `yes` to continue or type `cancel` to cancel.**");
// If not cancel the process
if (reply.content.toLowerCase() != "yes") {
cancel();
return;
}
// Ask the user questions one by one and add them to application
for (const question of questions) {
const reply = await askQuestion(member, question);
// The user can cancel the process anytime he wants
if (reply.content.toLowerCase() == "cancel") {
cancel();
return;
}
application.addField(question, reply);
}
await askQuestion(member, "Would you like to submit your Application?\n" + "**Type `yes` to get your Application submitted and reviewed by Staff Members.**");
// If not cancel the process
if (reply.content.toLowerCase() != "yes") {
cancel();
return;
}
// Send the filled application to the application channel
client.channels.cache.get(applicationChannelId).send(application);
} catch {
// If the user took too long to respond an error will be thrown,
// we can handle that case here.
member.send("You took too long to respond or Something went wrong, Please contact a Staff member\n" + "The process was canceled.");
}
}
}
});
at this point I just feel like not even doing this and keep it as is because its driving me insane
You very well can! just send it as you send your normal buttons!
const {
MessageButton,
MessageActionRow
} = require("discord.js"),
const denybtn = new MessageButton()
.setStyle('DANGER')
.setEmoji('❌')
.setCustomId('deny')
const acceptbtn = new MessageButton()
.setStyle('SUCCESS')
.setEmoji('✔')
.setCustomId('accept')
client.channels.cache.get(applicationChannelId).send({
embeds: [application],
components: [new MessageActionRow().addComponents["acceptbtn", "denybtn"]]
});
const collector = msg.createMessageComponentCollector({
time: 3600000,
errors: ["time"],
});
await collector.on("collect", async (r) => {
if (r.user.id !== message.author.id)
return r.reply({
content: "You may not accept/ deny this application",
ephemeral: true,
});
if (r.customId === "acceptbtn") {
let teamRole = message.guild.roles.cache.find(role => role.id == "761996603434598460")
member.roles.add(teamRole)
member.send("You have been accepted into the team")
}
if (r.customId === "denybtn") {
member.send("You have been rejected");
}
});
NOTE: Please be mindful that since your question lacks the functions / method definitions you are using I have used discord.js v13's methods, you may update your discord.js version and the code will work as intended ALTHOUGH the component collector's functions have been directly copy pasted from your question and in some instances such as member#send member is not defined, so please take this as an example and I urge you to write the code instead of copy pasting directly!
I would like that my bot detect when a user is joining again, back, to the server. I have made a JSON file containing the ID of the users that left the server. If the new member's ID is included in the JSON file with IDs, then it (the bot) should give a special welcome message.
The code shown is not working, meaning it is not really taking any ID from the JSON file. I guess that it needs a bucle with the lenght of the JSON's content to compare the Member ID with Badmember.USERS_LEFT_ID[].
JSON file: user_left.json
{
"USERS_LEFT_ID" : [
"1",
"2",
"3",
"4",
]
}
JS File: guildMemberAdd.js
const Badmember = require('users_left.json')
exports.run = async(client, newmember) => {
const channel = newmember.guild.channels.cache.get(Wlc_ch);
if (newmember.user.id == Badmember.USERS_LEFT_ID) {
channel.send('Special message!')
} else {
console.log(newmember);
console.log(newmember.user.id + ' has joined');
}
}
To check if the user is in the ID list, need to somehow iterate through the array. One option is to use the built-in includes() method.
const Badmember = require('users_left.json')
exports.run = async(client, newmember) => {
const channel = newmember.guild.channels.cache.get(Wlc_ch);
if (Badmember.USERS_LEFT_ID.includes(newmember.user.id)) {
channel.send('Special message!')
} else {
console.log(newmember);
console.log(newmember.user.id + ' has joined');
}
}
Check out MDN docs for more details on Array.prototype.includes(): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes
Basically, this is my code
applySearchFilter(filterValue: string) {
this.values = filterValue.toString().toLowerCase();
this.dataSource.filter = this.values;
}
with "this.dataSource.filter" I can filter any data out of it, but I want to add an exception to keyId.... See the object below.
my object looks something like this.
{
keyId: "00000000-0000-0000-0000-000000010001",
name: "Test 10",
location: "New York"
}
When I input "10", it filters everything in the dataSource that has a 10. So, not only the name has "10" in it, but the keyId too. I took an hour to realize what this kept happening... and it is because of keyId xD
Allthough I get the whole object with keyId included, keyId is not shown in frontend and should not be search/filter-able. Even if it does not show keyId in frontend, he searches for the object that has that keyid
I am not sure how to fix it right now, since something like this.dataSource.data.name or so does not work...
I'd appreciate if you gals and girls would give me some tips.
Oh and here is where I get my data to that get filtered.
public getObject(): void {
this.service.getObjectMethod().then(data=> {
this.dataSource.data = data;
}, (err: Error) => { console.log(err.message); });
}
i solved this issue using.
public filterData(): void {
if (this.inputValues.trim().length !== 0) {
this.filteredValues = [];
this.dataSource.data.filter(data => {
if (data.name.toLowerCase().includes((this.inputValues).toLowerCase()) ||
data.opensCategory.toLowerCase().includes((this.inputValues).toLowerCase())) {
return true && this.filteredValues.push(data);
}
});
this.dataSource.data = this.filteredValues;
}
}
Use the following code to search.
applySearchFilter(filterValue: string) {
this.dataSource.filterPredicate = function (data: any, filterValue: string) {
return data.name.trim().toLocaleLowerCase().indexOf(filterValue.trim().toLocaleLowerCase()) >= 0;
};
}
In my users profile collection I have array with image objects in it.
A user can have a max of 3 images in their profile collection. If the user has 3, throw an error that the maximum has been reached. The user has the option to remove an image themselves in the frontend.
I thought the solution would be to check the length of the array with $size. if it's less then 3, insert the image, else throw error.
I'm using the tomi:upload-jquery package.
client:
Template.uploadImage.helpers({
uploadUserData: function() {
return Meteor.user();
},
finishUpload: function() {
return {
finished: function(index, fileInfo, context) {
Meteor.call('insert.profileImage', fileInfo, function(error, userId) {
if (error) {
// todo: display modal with error
return console.log(error.reason);
} else {
// console.log('success ' +userId);
// console.log('success ' + fileInfo);
}
});
}
};
}
});
The method (server) I use:
'insert.profileImage': function(postImage) {
check(postImage, Object);
// check array profile.images max 3
Meteor.users.update(this.userId, {
$push: {
'profile.images': postImage
}
});
},
You may do it with a function using the $where operator:
'insert.profileImage': function(postImage) {
var updateResults;
check(postImage, Object);
updateResults = Meteor.users.update(
{
_id : this.userId,
$where : 'this.profile.images.length < 3' //'this' is the tested doc
},
{
$push: {
'profile.images': postImage
}
});
if(updateResults === 0) {
throw new Meteor.Error('too-many-profile-images',
'A user can only have up to 3 images on his/her profile');
}
},
The Mongo docs warns about potential performance issues (if you run a JavaScript function on all documents of the store, you're in for bad surprises) but since we also search by _id I guess it should be fine.
This way, the update just doesn't run if the user has too many images. You can also check the number of affected document (the return value of the update) to know if something happened. If nothing (returns 0) happened, there's not many possibilities: The user has too many images.
Use the $exists operator to check the existence of all documents that have at least a fourth profile image array element (index position 3) with the dot notation. For example you could use it to check whether the size of the profile.image array is greater than 3 with the find() method as follows:
var hasSizeGreaterThanThree = Meteor.users.find(
{
'_id': this.userId,
'profile.image.3': { '$exists': true }
}).count() > 0;
So you could use that in your code as:
'insert.profileImage': function(postImage) {
check(postImage, Object);
// check array profile.images max 3
var hasSizeGreaterThanThree = Meteor.users.find(
{
'_id': this.userId,
'profile.image.3': { '$exists': true }
}).count() > 0;
if (!hasSizeGreaterThanThree){
Meteor.users.update(this.userId, {
$push: {
'profile.images': postImage
}
});
}
},