From 1cba5ea335b2d4ba72a970a41b9f4b07322351e3 Mon Sep 17 00:00:00 2001 From: juls0730 Date: Sun, 15 Jan 2023 23:54:38 -0600 Subject: [PATCH] reactions and a bunch of bug fixes --- components/Message.vue | 88 +++++ components/MessagePane.vue | 210 ++++++----- components/Nav.vue | 2 +- components/Sidebar.vue | 7 +- layouts/default.vue | 2 +- nuxt.config.ts | 3 +- package-lock.json | 343 ++++++++++++++++++ package.json | 4 + pages/channel/@me/[id].vue | 18 +- pages/channel/@me/index.vue | 2 +- pages/channel/[id].vue | 86 +++-- pages/signup.vue | 16 +- prisma/dev.db | 0 prisma/schema.prisma | 16 + server/api/channels/[id]/index.get.ts | 65 +++- .../[messageId]/reactions/[name].post.ts | 173 +++++++++ server/api/guilds/[id]/addChannel.post.ts | 61 ++++ server/api/guilds/[id]/index.get.ts | 61 +++- server/api/login.post.ts | 2 - server/api/signup.post.ts | 15 + server/api/user/getServers.get.ts | 64 +++- server/middleware/socket.ts | 2 - stores/store.ts | 84 +++-- tsconfig.json | 23 ++ types/index.ts | 19 +- 25 files changed, 1170 insertions(+), 196 deletions(-) create mode 100644 components/Message.vue delete mode 100644 prisma/dev.db create mode 100644 server/api/channels/[id]/messages/[messageId]/reactions/[name].post.ts diff --git a/components/Message.vue b/components/Message.vue new file mode 100644 index 0000000..64afbe2 --- /dev/null +++ b/components/Message.vue @@ -0,0 +1,88 @@ + + + \ No newline at end of file diff --git a/components/MessagePane.vue b/components/MessagePane.vue index 34b765c..43876dd 100644 --- a/components/MessagePane.vue +++ b/components/MessagePane.vue @@ -36,39 +36,23 @@ {{ - server.dmParticipants.find((e: IUser) => e.id !== - user.id).username + server.dmParticipants?.find((e: SafeUser) => e.id !== user.id)?.username }}
-
+

No messages yet

-
-
-
-

- {{ message.creator.username }} -

-

{{ message.body }}

-
-
- -
-
+ v-for="(message, i) in server.messages" + class="relative"> +
+
@@ -135,26 +119,25 @@ \ No newline at end of file diff --git a/pages/channel/@me/index.vue b/pages/channel/@me/index.vue index ce10c29..8151e25 100644 --- a/pages/channel/@me/index.vue +++ b/pages/channel/@me/index.vue @@ -20,7 +20,7 @@ export default { } }, mounted() { - useGlobalStore().setActive('dms', '@me') + useGlobalStore().setActiveServer('dms', '@me') }, methods: { async startDM() { diff --git a/pages/channel/[id].vue b/pages/channel/[id].vue index 012f18d..4528076 100644 --- a/pages/channel/[id].vue +++ b/pages/channel/[id].vue @@ -3,6 +3,7 @@ \ No newline at end of file diff --git a/pages/signup.vue b/pages/signup.vue index daf4fb2..dca4be4 100644 --- a/pages/signup.vue +++ b/pages/signup.vue @@ -63,15 +63,19 @@ export default { const token = useCookie('sessionToken') token.value = user.token - const headers = { Cookie: `sessionToken=${token.value}`} - const { servers, dms } = await $fetch('/api/user/getServers', { headers }) + setTimeout(async () => { + const headers = { Cookie: `sessionToken=${token.value}` } + const { servers, dms } = await $fetch('/api/user/getServers', { headers }) - globalStore.setServers(servers) - globalStore.setDms(dms) + if (!servers || !dms) return; - useGlobalStore().setUser(user.user) + globalStore.setServers(servers) + globalStore.setDms(dms) - navigateTo('/channel/@me') + globalStore.setUser(user.user) + + navigateTo('/channel/@me') + }) } } } diff --git a/prisma/dev.db b/prisma/dev.db deleted file mode 100644 index e69de29..0000000 diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 14edb61..7857486 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -17,6 +17,9 @@ model User { session Session[] channels Channel[] roles Role[] + createdAt DateTime @default(now()) + Reaction Reaction? @relation(fields: [reactionId], references: [id]) + reactionId String? } model Server { @@ -26,6 +29,7 @@ model Server { channels Channel[] roles Role[] InviteCode InviteCode[] + createdAt DateTime @default(now()) } model Role { @@ -56,6 +60,9 @@ model Message { userId String channelId String invites InviteCode[] + reactions Reaction[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt } model InviteCode { @@ -80,3 +87,12 @@ model ExpiredSession { id String @id @default(cuid()) token String } + +model Reaction { + id String @id @default(cuid()) + emoji Json + count Int + users User[] + Message Message? @relation(fields: [messageId], references: [id]) + messageId String? +} diff --git a/server/api/channels/[id]/index.get.ts b/server/api/channels/[id]/index.get.ts index e766317..c8637d3 100644 --- a/server/api/channels/[id]/index.get.ts +++ b/server/api/channels/[id]/index.get.ts @@ -33,7 +33,55 @@ export default defineEventHandler(async (event) => { id: true, username: true } - } + }, + channels: { + select: { + id: true, + DM: true, + name: true, + messages: { + select: { + id: true, + body: true, + creator: { + select: { + id: true, + username: true + } + }, + invites: { + select: { + id: true, + server: { + select: { + id: true, + name: true, + participants: { + select: { + id: true + } + } + } + } + } + }, + reactions: { + select: { + id: true, + emoji: true, + count: true, + users: { + select: { + id: true, + username: true + } + } + } + } + } + } + } + }, } }, messages: { @@ -61,6 +109,19 @@ export default defineEventHandler(async (event) => { } } } + }, + reactions: { + select: { + id: true, + emoji: true, + count: true, + users: { + select: { + id: true, + username: true + } + } + } } } }, @@ -93,6 +154,8 @@ export default defineEventHandler(async (event) => { } }) as IServer | null; + if (!server) return; + const userInServer: Array | undefined = server?.participants.filter((e: SafeUser) => e.id === event.context.user.id) if (!userInServer) { diff --git a/server/api/channels/[id]/messages/[messageId]/reactions/[name].post.ts b/server/api/channels/[id]/messages/[messageId]/reactions/[name].post.ts new file mode 100644 index 0000000..80c15cd --- /dev/null +++ b/server/api/channels/[id]/messages/[messageId]/reactions/[name].post.ts @@ -0,0 +1,173 @@ +import { IChannel, IServer, SafeUser } from '~/types' +import { PrismaClient } from '@prisma/client' +import { node } from 'unenv' +const prisma = new PrismaClient() + +export default defineEventHandler(async (event) => { + if (!event.context.user.authenticated) { + event.node.res.statusCode = 401; + return { + message: 'You must be logged in to send a message.' + } + } + + const emoji = decodeURIComponent(event.context.params.name) + + if (emoji.length !== 2) { + event.node.res.statusCode = 400; + return { + message: 'reaction is not an emoji or more than one emoji.' + } + } + + const first = emoji.charCodeAt(0); + const second = emoji.charCodeAt(1); + + if (!((first >= 0xD800 && first <= 0xDBFF) && (second >= 0xDC00 && second <= 0xDFFF))) { + event.node.res.statusCode = 400; + return { + message: 'reaction is not an emoji or more than one emoji.' + } + } + + const messageSelect = { + id: true, + body: true, + creator: { + select: { + id: true, + username: true + } + }, + invites: { + select: { + id: true, + server: { + select: { + id: true, + name: true, + participants: { + select: { + id: true + } + } + } + } + } + }, + reactions: { + select: { + id: true, + emoji: true, + count: true, + users: { + select: { + id: true, + username: true + } + } + } + } + } + + const message = await prisma.message.findFirst({ + where: { + id: event.context.params.messageId + }, + select: messageSelect + }) + + if (!message.id) { + event.node.res.statusCode = 404; + return { + message: `message with id "${event.context.params.messageId}" not found.` + } + } + + const reactionInMessage = message.reactions.find((e) => e.emoji.name === emoji) + + let count; + + if (reactionInMessage?.count) { + count = reactionInMessage.count + 1; + } else { + count = 1; + } + + if (reactionInMessage && reactionInMessage.users.find((e) => e.id === event.context.user.id)) { + // remove reaction + await prisma.reaction.update({ + where: { + id: reactionInMessage.id + }, + data: { + count: reactionInMessage.count - 1, + users: { + disconnect: [{ id: event.context.user.id }] + } + } + }) + + const updatedMessage = await prisma.message.findFirst({ + where: { + id: event.context.params.messageId + }, + select: messageSelect + }) + + global.io.emit(`message-${event.context.params.id}`, { message: updatedMessage }); + + return { message: updatedMessage } + } + + let reaction; + if (reactionInMessage) { + // reaction already exists, so up the count by one and add the user to the users who have reacted + reaction = await prisma.reaction.update({ + where: { + id: reactionInMessage.id + }, + data: { + count, + users: { + connect: [{ + id: event.context.user.id, + }] + }, + } + }) + } else { + reaction = await prisma.reaction.create({ + data: { + emoji: { + name: emoji, + id: null + }, + count: count, + users: { + connect: [{ + id: event.context.user.id, + }] + }, + Message: { + connect: { + id: message.id, + } + } + } + }) + } + + if (!reaction.messageId) return; + + const updatedMessage = await prisma.message.findFirst({ + where: { + id: reaction.messageId, + }, + select: messageSelect + }) + + global.io.emit(`message-${event.context.params.id}`, { message: updatedMessage }); + + return { message: updatedMessage } +}) \ No newline at end of file diff --git a/server/api/guilds/[id]/addChannel.post.ts b/server/api/guilds/[id]/addChannel.post.ts index 52a8b74..55de722 100644 --- a/server/api/guilds/[id]/addChannel.post.ts +++ b/server/api/guilds/[id]/addChannel.post.ts @@ -61,8 +61,69 @@ export default defineEventHandler(async (event) => { id: server.id } } + }, + select: { + id: true, + name: true, + server: { + select: { + id: true, + name: true, + participants: { + select: { + id: true, + username: true + } + }, + channels: { + select: { + id: true, + DM: true, + name: true + } + }, + } + }, + messages: { + select: { + id: true, + body: true, + creator: { + select: { + id: true, + username: true + } + }, + invites: { + select: { + id: true, + server: { + select: { + id: true, + name: true, + participants: { + select: { + id: true + } + } + } + } + } + } + } + }, + DM: true, + dmParticipants: { + select: { + id: true, + username: true + } + }, + serverId: true, } }) as IChannel + global.io.emit(`addChannel-${server.id}`, channel) + return channel }) \ No newline at end of file diff --git a/server/api/guilds/[id]/index.get.ts b/server/api/guilds/[id]/index.get.ts index ed13d5a..ae00597 100644 --- a/server/api/guilds/[id]/index.get.ts +++ b/server/api/guilds/[id]/index.get.ts @@ -21,10 +21,63 @@ export default defineEventHandler(async (event) => { where: { id: event.context.params.id }, - include: { - participants: true, - channels: true, - roles: true + select: { + id: true, + name: true, + participants: { + select: { + id: true, + username: true + } + }, + channels: { + select: { + id: true, + DM: true, + name: true, + messages: { + select: { + id: true, + body: true, + creator: { + select: { + id: true, + username: true + } + }, + invites: { + select: { + id: true, + server: { + select: { + id: true, + name: true, + participants: { + select: { + id: true + } + } + } + } + } + }, + reactions: { + select: { + id: true, + emoji: true, + count: true, + users: { + select: { + id: true, + username: true + } + } + } + } + } + } + } + }, } }) as IServer | null; diff --git a/server/api/login.post.ts b/server/api/login.post.ts index ea3805e..abb95cd 100644 --- a/server/api/login.post.ts +++ b/server/api/login.post.ts @@ -14,8 +14,6 @@ export default defineEventHandler(async (event) => { } } - - let user = await prisma.user.findFirst({ where: { username: body.username diff --git a/server/api/signup.post.ts b/server/api/signup.post.ts index 178b121..c3f57a6 100644 --- a/server/api/signup.post.ts +++ b/server/api/signup.post.ts @@ -45,6 +45,21 @@ export default defineEventHandler(async (event) => { select: { id: true, username: true, + servers: { + participants: { + select: { + id: true, + username: true + } + }, + channels: { + select: { + id: true, + DM: true, + name: true, + } + }, + } }, }) as unknown as IUser diff --git a/server/api/user/getServers.get.ts b/server/api/user/getServers.get.ts index ca9b914..364c425 100644 --- a/server/api/user/getServers.get.ts +++ b/server/api/user/getServers.get.ts @@ -25,8 +25,68 @@ export default defineEventHandler(async (event) => { select: { id: true, DM: true, - name: true - } + name: true, + server: { + select: { + id: true, + name: true, + participants: { + select: { + id: true, + username: true + } + }, + channels: { + select: { + id: true, + DM: true, + name: true, + messages: { + select: { + id: true, + body: true, + creator: { + select: { + id: true, + username: true + } + }, + invites: { + select: { + id: true, + server: { + select: { + id: true, + name: true, + participants: { + select: { + id: true + } + } + } + } + } + }, + reactions: { + select: { + id: true, + emoji: true, + count: true, + users: { + select: { + id: true, + username: true + } + } + } + } + } + } + } + }, + } + }, + }, }, participants: { select: { diff --git a/server/middleware/socket.ts b/server/middleware/socket.ts index c353e5d..36a30b8 100644 --- a/server/middleware/socket.ts +++ b/server/middleware/socket.ts @@ -17,7 +17,6 @@ export default defineEventHandler(({ node }) => { return; } - console.time() const { user } = await prisma.session.findFirst({ where: { token @@ -87,7 +86,6 @@ export default defineEventHandler(({ node }) => { } } }) as { user: IUser } | null; - console.timeEnd(); if (!user) { return; diff --git a/stores/store.ts b/stores/store.ts index 9f30082..c8745d4 100644 --- a/stores/store.ts +++ b/stores/store.ts @@ -1,8 +1,11 @@ +import { channel } from "diagnostics_channel"; +import { serve } from "esbuild"; import { Socket } from "socket.io-client"; -import { SafeUser, IServer, IChannel } from "../types"; +import { SafeUser, IServer, IChannel, IMessage } from "../types"; export const useGlobalStore = defineStore('global', { state: () => ({ + activeChannel: {} as IChannel, activeServer: {} as IServer | IChannel, activeServerType: '' as "dms" | "servers" | undefined, user: {} as SafeUser, @@ -11,27 +14,7 @@ export const useGlobalStore = defineStore('global', { socket: null as unknown }), actions: { - setUser(user: SafeUser) { - this.user = user; - }, - addServer(server: IServer) { - if (!this.servers || this.servers.find((e) => e.id === server.id)) return; - this.servers.push(server) - }, - addDM(dmChannel: IChannel) { - if (!this.dms || this.dms.find((e) => e.id === dmChannel.id)) return; - this.dms.push(dmChannel) - }, - setServers(servers: Array) { - this.servers = servers - }, - setDms(dms: Array) { - this.dms = dms - }, - setSocket(socket: Socket) { - this.socket = socket - }, - setActive(type: "servers" | "dms", channelId: string) { + setActiveServer(type: "servers" | "dms", channelId: string) { if (channelId === '@me') { this.activeServer = {} as IServer | IChannel this.activeServerType = 'dms' @@ -42,24 +25,71 @@ export const useGlobalStore = defineStore('global', { const searchableArray: IChannel[] | IServer[] | undefined = this[type] if (!searchableArray) return; - let activeServerIndex: number; + let activeServer: number; if (type === 'servers') { - activeServerIndex = searchableArray.findIndex((e) => { + activeServer = searchableArray.find((e) => { return e.channels.some((channel: IChannel) => channel.id === channelId) }) } else { - activeServerIndex = searchableArray.findIndex((e) => { + activeServer = searchableArray.find((e) => { return e.id === channelId }) } - this.activeServer = this.servers[activeServerIndex] + this.activeServer = activeServer + }, + setActiveChannel(channel: IChannel) { + this.activeChannel = channel; + }, + updateServer(channelId: string, server: IServer) { + const serverIndex = this.servers.findIndex(s => s.channels.some((c) => c.id === channelId)) + this.servers[serverIndex] = server + }, + setServers(servers: Array) { + this.servers = servers + }, + addChannel(serverId: string, channel: IChannel) { + const serverIndex = this.servers.findIndex(s => s.id === serverId) + const server = this.servers[serverIndex] + if (serverIndex < 0 || !server) return; + if (server.channels.find((c) => c.id === channel.id)) return; + server.channels.push(channel) + }, + addDM(dmChannel: IChannel) { + if (this.dms.find((e) => e.id === dmChannel.id)) { + const index = this.dms.findIndex((e) => e.id === dmChannel.id) + this.dms[index] = dmChannel + return; + } + this.dms.push(dmChannel) + }, + addServer(server: IServer) { + if (this.servers.find((e) => e.id === server.id)) { + const index = this.servers.findIndex((e) => e.id === server.id) + this.servers[index] = server + return; + } + this.servers.push(server) + }, + setDms(dms: Array) { + this.dms = dms + }, + setSocket(socket: Socket) { + this.socket = socket + }, + setUser(user: SafeUser) { + this.user = user; + }, + updateMessage(messageId: string, message: IMessage) { + const messageIndex = this.activeChannel.messages.findIndex((e) => e.id === messageId) + if (messageIndex < 0) return; + this.activeChannel.messages[messageIndex] = message }, logout() { this.dms = [] this.servers = [] this.socket = null - this.activeServer = {} as IServer | IChannel + this.activeServer = {} as IChannel } }, }) diff --git a/tsconfig.json b/tsconfig.json index 1211d5a..91700dc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,3 +1,26 @@ { + "compilerOptions": { + "target": "ES2020", + "lib": [ + "ESNext", + "ESNext.AsyncIterable", + "DOM" + ], + "sourceMap": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "module": "ESNext", + "moduleResolution": "Node", + "resolveJsonModule": true, + "isolatedModules": true, + "incremental": true, + "jsx": "preserve", + "noUncheckedIndexedAccess": true, + "noImplicitAny": true, + "allowJs": true + }, "extends": "./.nuxt/tsconfig.json" } \ No newline at end of file diff --git a/types/index.ts b/types/index.ts index 613d817..9c8eddf 100644 --- a/types/index.ts +++ b/types/index.ts @@ -6,6 +6,7 @@ export interface IUser { servers?: Array; channels?: Array; roles?: Array; + createdAt: Date; } export type SafeUser = Omit, 'email'> @@ -23,7 +24,7 @@ export interface IChannel { id: string; name: string; server: IServer; - messages?: Array + messages: Array DM: boolean; dmParticipants?: Array; serverId: string; @@ -37,6 +38,9 @@ export interface IMessage { userId: string; channelId: string; invites?: IInviteCode[]; + reactions?: IReaction[]; + createdAt: Date; + updatedAt: Date; } export interface IInviteCode { @@ -58,4 +62,17 @@ export interface IRole { users: IUser[]; server?: IServer; serverId?: string; +} + +export interface IReaction { + id: string; + emoji: { + name: string; + id?: string; + }; + count: number; + previousCount?: number; + users: IUser[]; + Message: IMessage; + messageId: string; } \ No newline at end of file