Skip to content

Commit

Permalink
Able to create reminder on web (#3)
Browse files Browse the repository at this point in the history
* Add check for user in guild after voting and return it in response

* Add set reminder route for servers

* Rename Reminder to VoteReminder, remove date field from newReminder object

* Add can_set_reminder field to get server route

* Remove date field from new vote reminder object

* Refactor voteServer to return response data

* Add createReminder function

* Add create reminder button and functionality to Actions component
  • Loading branch information
chimpdev authored Mar 29, 2024
1 parent 736cf2f commit 324482b
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,19 @@ import Script from 'next/script';
import { useEffect, useRef, useState } from 'react';
import { toast } from 'sonner';
import voteServer from '@/lib/request/servers/voteServer';
import createReminder from '@/lib/request/servers/createReminder';
import Countdown from '@/app/components/Countdown';
import Tooltip from '@/app/components/Tooltip';
import { FaRegBell, FaBell } from 'react-icons/fa';

export default function Actions({ server }) {
const [serverVotes, setServerVotes] = useState(server.votes);
const [voteTimeout, setVoteTimeout] = useState(server.vote_timeout);
const [canSetReminder, setCanSetReminder] = useState(server.can_set_reminder);
const loggedIn = useAuthStore(state => state.loggedIn);
const [showCaptcha, setShowCaptcha] = useState(false);
const [loading, setLoading] = useState(false);
const [createReminderLoading, setCreateReminderLoading] = useState(false);

const formatter = new Intl.NumberFormat('en-US', {
notation: 'compact',
Expand All @@ -48,10 +52,11 @@ export default function Actions({ server }) {

toast.promise(voteServer(server.id, response), {
loading: `Voting ${server.name}..`,
success: () => {
success: data => {
setLoading(false);
setServerVotes(serverVotes + (server.badges.includes('Premium') ? 2 : 1));
setVoteTimeout({ createdAt: new Date().getTime() + 86400000 });
setCanSetReminder(data.inGuild);

return `Successfully voted for ${server.name}!`;
},
Expand All @@ -68,6 +73,24 @@ export default function Actions({ server }) {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [showCaptcha]);

function setReminder() {
setCreateReminderLoading(true);

toast.promise(createReminder(server.id), {
loading: `Creating reminder for ${server.name}..`,
success: () => {
setCreateReminderLoading(false);
setCanSetReminder(false);

return `I will remind you to vote for ${server.name} in 24 hours! Please keep your DMs open and don't leave the server.`;
},
error: error => {
setCreateReminderLoading(false);
return error;
}
});
}

return (
<div>
<motion.h2
Expand Down Expand Up @@ -99,6 +122,35 @@ export default function Actions({ server }) {
)}
</AnimatePresence>

{canSetReminder && (
<motion.button
className={cn(
'flex items-center justify-between w-full px-3 py-2 text-sm font-semibold text-white bg-black rounded-lg group gap-x-2 hover:bg-black/70 dark:bg-white dark:text-black dark:hover:bg-white/70',
createReminderLoading && 'cursor-default !opacity-70 hover:bg-black dark:hover:bg-white'
)}
initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3, type: 'spring', stiffness: 100, damping: 10 }}
onClick={() => {
if (!loggedIn) return toast.error('You need to be logged in to set a reminder for a server.');

setReminder();
}}
>
<div className='flex gap-x-1.5 items-center'>
{createReminderLoading && <TbLoader className='animate-spin' />}
Create Reminder
</div>

<div className='flex items-center font-bold gap-x-1'>
<div className='relative'>
<FaBell className='absolute transition-transform opacity-0 group-hover:opacity-100 group-hover:scale-[1.2]' />
<FaRegBell className='opacity-100 transition-[transform] group-hover:opacity-0' />
</div>
</div>
</motion.button>
)}

<motion.button
className={cn(
'flex items-center justify-between w-full px-3 py-2 text-sm font-semibold text-white bg-black rounded-lg group gap-x-2 hover:bg-black/70 dark:bg-white dark:text-black dark:hover:bg-white/70',
Expand Down
16 changes: 16 additions & 0 deletions client/lib/request/servers/createReminder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import config from '@/config';
import axios from 'axios';

export default function createReminder(id) {
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve, reject) => {
const url = `${config.api.url}/servers/${id}/reminder`;

try {
await axios.post(url, {}, { withCredentials: true });
resolve();
} catch (error) {
reject(error instanceof axios.AxiosError ? (error.response?.data?.error || error.message) : error.message);
}
});
}
4 changes: 2 additions & 2 deletions client/lib/request/servers/voteServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ export default function voteServer(id, captchaResponse) {
const url = `${config.api.url}/servers/${id}/vote`;

try {
await axios.post(url, { captchaResponse }, { withCredentials: true });
resolve();
const response = await axios.post(url, { captchaResponse }, { withCredentials: true });
resolve(response.data);
} catch (error) {
reject(error instanceof axios.AxiosError ? (error.response?.data?.error || error.message) : error.message);
}
Expand Down
3 changes: 1 addition & 2 deletions server/src/bot/commands/vote.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,7 @@ module.exports = {
},
guild: {
id: interaction.guild.id
},
date: Date.now() + 86400000
}
}).save();

return reminderCollected.update({ components: [], content: 'You will be reminded in 24 hours.' });
Expand Down
11 changes: 9 additions & 2 deletions server/src/routes/servers/[id]/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const Server = require('@/schemas/Server');
const Premium = require('@/schemas/Premium');
const VoteTimeout = require('@/schemas/Server/Vote/Timeout');
const VoiceActivity = require('@/schemas/Server/VoiceActivity');
const VoteReminder = require('@/schemas/Server/Vote/Reminder');
const Review = require('@/schemas/Server/Review');
const inviteLinkValidation = require('@/validations/servers/inviteLink');
const updatePanelMessage = require('@/utils/servers/updatePanelMessage');
Expand Down Expand Up @@ -57,6 +58,11 @@ module.exports = {
)
};

const voteTimeout = await VoteTimeout.findOne({ 'user.id': request.user?.id, 'guild.id': id });
const reminder = await VoteReminder.findOne({ 'user.id': request.user?.id, 'guild.id': id });
const memberInGuild = guild.members.cache.get(request.user?.id) || await guild.members.fetch(request.user?.id).catch(() => false);
const tenMinutesPassedAfterVote = voteTimeout && Date.now() - voteTimeout.createdAt.getTime() > 600000;

return response.json({
...await server.toPubliclySafe(),
name: guild.name,
Expand All @@ -68,12 +74,13 @@ module.exports = {
vanity_url: guild.vanityURLCode ? `https://discord.com/invite/${guild.vanityURLCode}` : null,
boost_level: guild.premiumTier,
total_boosts: guild.premiumSubscriptionCount,
vote_timeout: request.user ? (await VoteTimeout.findOne({ 'user.id': request.user.id, 'guild.id': id }) || null) : null,
vote_timeout: request.user ? (voteTimeout || null) : null,
badges,
voiceActivity: voiceActivity ? voiceActivity.data : null,
reviews,
has_reviewed: request.user ? !!reviews.find(review => review.user.id === request.user.id) : null,
permissions
permissions,
can_set_reminder: !!(request.user && !reminder && voteTimeout && memberInGuild && !tenMinutesPassedAfterVote)
});
}
],
Expand Down
47 changes: 47 additions & 0 deletions server/src/routes/servers/[id]/reminder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
const checkAuthentication = require('@/utils/middlewares/checkAuthentication');
const useRateLimiter = require('@/utils/useRateLimiter');
const { param, matchedData } = require('express-validator');
const Server = require('@/schemas/Server');
const VoteTimeout = require('@/schemas/Server/Vote/Timeout');
const VoteReminder = require('@/schemas/Server/Vote/Reminder');
const bodyParser = require('body-parser');

module.exports = {
post: [
useRateLimiter({ maxRequests: 5, perMinutes: 1 }),
bodyParser.json(),
checkAuthentication,
param('id'),
async (request, response) => {
const { id } = matchedData(request);

const guild = client.guilds.cache.get(id);
if (!guild) return response.sendError('Guild not found.', 404);

const server = await Server.findOne({ id });
if (!server) return response.sendError('Server not found.', 404);

const timeout = await VoteTimeout.findOne({ 'user.id': request.user.id, 'guild.id': id });
if (!timeout) return response.sendError('You can\'t set a reminder for a server you haven\'t voted for.', 400);

const reminder = await VoteReminder.findOne({ 'user.id': request.user.id, 'guild.id': id });
if (reminder) return response.sendError('You already set a reminder for this server.', 400);

const newReminder = new VoteReminder({
user: {
id: request.user.id
},
guild: {
id
}
});

const validationErrors = newReminder.validateSync();
if (validationErrors) return response.sendError('An unknown error occurred.', 400);

await newReminder.save();

return response.sendStatus(204).end();
}
]
};
5 changes: 4 additions & 1 deletion server/src/routes/servers/[id]/vote.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ module.exports = {
if (timeout) return response.sendError(`You can vote again in ${Math.floor((timeout.createdAt.getTime() + 86400000 - Date.now()) / 3600000)} hours, ${Math.floor((timeout.createdAt.getTime() + 86400000 - Date.now()) / 60000) % 60} minutes.`, 400);

return incrementVote(id, request.user.id)
.then(() => response.status(204).end())
.then(async () => {
const userInGuild = guild.members.cache.get(request.user.id) || await guild.members.fetch(request.user.id).catch(() => false);
return response.json({ inGuild: !!userInGuild });
})
.catch(error => response.sendError(error.message, 400));
}
]
Expand Down

0 comments on commit 324482b

Please sign in to comment.