improved a bunch of things

This commit is contained in:
Zoe
2023-01-12 00:12:10 -06:00
parent 21a9b11547
commit 3bad12c646
13 changed files with 252 additions and 102 deletions

View File

@@ -19,11 +19,25 @@
<div> <div>
<div v-for="invite in message.invites"> <div v-for="invite in message.invites">
<div class="w-6/12 bg-[hsl(223,6.9%,19.8%)] p-4 rounded-md shadow-md mr-2"> <div class="w-6/12 bg-[hsl(223,6.9%,19.8%)] p-4 rounded-md shadow-md mr-2">
<p class="text-sm font-semibold">You've been invited</p> <p class="text-sm font-semibold text-zinc-100">You've been invited to join a
server</p>
<span class="text-xl font-bold capitalize">{{ invite.server.name }}</span> <span class="text-xl font-bold capitalize">{{ invite.server.name }}</span>
<div class="flex items-center">
<span
class="before:bg-[hsl(214,9.9%,50.4%)] before:h-2 before:w-2 before:inline-block before:my-auto before:rounded-full before:mr-1"></span>
<span>{{ invite.server.participants.length }} Members</span>
</div>
<div class="flex w-full justify-end"> <div class="flex w-full justify-end">
<button @click="joinServer(invite)" <button @click="joinServer(invite)"
class="font-semibold rounded px-4 py-2 bg-green-700 hover:bg-green-600 transition-colors">Join</button> class="font-semibold rounded px-4 py-2 transition-colors"
:class="(invite.server.participants.find((e) => e.id === user.id)) ? 'bg-green-800 cursor-not-allowed' : 'bg-green-700 hover:bg-green-600'">
<span v-if="invite.server.participants.find((e) => e.id === user.id)">
Joined
</span>
<span v-else>
Join
</span>
</button>
</div> </div>
</div> </div>
</div> </div>
@@ -77,7 +91,8 @@ export default {
user: storeToRefs(useGlobalStore()).user, user: storeToRefs(useGlobalStore()).user,
messageContent: '', messageContent: '',
conversation: this.server.messages as IMessage[], conversation: this.server.messages as IMessage[],
canSendNotifications: false canSendNotifications: false,
servers: storeToRefs(useGlobalStore()).servers
} }
}, },
mounted() { mounted() {
@@ -146,7 +161,7 @@ export default {
const route = useRoute() const route = useRoute()
if (!this.messageContent) return; if (!this.messageContent) return;
const message: IChannel = await $fetch(`/api/channels/sendMessage`, { method: 'post', body: { body: this.messageContent, channelId: route.params.id } }) const message: IMessage = await $fetch(`/api/channels/sendMessage`, { method: 'post', body: { body: this.messageContent, channelId: route.params.id } })
if (!message) return; if (!message) return;
if (this.conversation.includes(message)) return; if (this.conversation.includes(message)) return;
@@ -162,7 +177,7 @@ export default {
async joinServer(invite: IInviteCode) { async joinServer(invite: IInviteCode) {
const { server } = await $fetch('/api/guilds/joinGuild', { method: 'POST', body: { inviteId: invite.id } }) const { server } = await $fetch('/api/guilds/joinGuild', { method: 'POST', body: { inviteId: invite.id } })
if (!server) return; if (!server) return;
this.user.servers?.push(server) this.servers?.push(server)
}, },
scrollToBottom() { scrollToBottom() {
const conversationDiv = document.getElementById('conversation-pane'); const conversationDiv = document.getElementById('conversation-pane');

View File

@@ -19,7 +19,7 @@
</nuxt-link> </nuxt-link>
</div> </div>
<div class="overflow-y-scroll my-2 flex gap-y-2 flex-col"> <div class="overflow-y-scroll my-2 flex gap-y-2 flex-col">
<nuxt-link v-for="server in user.servers" :to="'/channel/' + server.channels[0].id"> <nuxt-link v-for="server in servers" :to="'/channel/' + server.channels[0].id">
<div :key="server.id" <div :key="server.id"
@click="openServer(server.id, 'servers')" @click="openServer(server.id, 'servers')"
class="bg-zinc-600/80 p-3 rounded-full transition-all hover:rounded-2xl ease-in-out hover:bg-zinc-500/60 duration-300 h-[56px] w-[56px]"> class="bg-zinc-600/80 p-3 rounded-full transition-all hover:rounded-2xl ease-in-out hover:bg-zinc-500/60 duration-300 h-[56px] w-[56px]">
@@ -93,6 +93,7 @@ import { IServer } from '~/types';
export default { export default {
data() { data() {
return { return {
servers: storeToRefs(useGlobalStore()).servers,
createServerModelOpen: false, createServerModelOpen: false,
serverName: '' serverName: ''
} }
@@ -109,6 +110,5 @@ export default {
useGlobalStore().setActive(type, id) useGlobalStore().setActive(type, id)
} }
}, },
props: ['user']
} }
</script> </script>

View File

@@ -3,7 +3,7 @@
class="bg-[hsl(223,calc(1*6.9%),19.8%)] min-w-60 w-60 h-screen shadow-sm text-white select-none grid grid-rows-[93.5%_1fr]"> class="bg-[hsl(223,calc(1*6.9%),19.8%)] min-w-60 w-60 h-screen shadow-sm text-white select-none grid grid-rows-[93.5%_1fr]">
<div v-if="!server.id || server.DM == true"> <div v-if="!server.id || server.DM == true">
<div> <div>
<nuxt-link v-for="dm in user.channels" <nuxt-link v-for="dm in dms"
:to="'/channel/@me/' + dm.id"> :to="'/channel/@me/' + dm.id">
<div <div
class="mx-2 my-4 hover:bg-[hsl(223,calc(1*6.9%),25.8%)] px-2 py-2 w-[calc(240px-1rem)] max-h-10 h-10 overflow-ellipsis rounded-md transition-colors"> class="mx-2 my-4 hover:bg-[hsl(223,calc(1*6.9%),25.8%)] px-2 py-2 w-[calc(240px-1rem)] max-h-10 h-10 overflow-ellipsis rounded-md transition-colors">
@@ -17,7 +17,7 @@
<div class="flex p-4 border-b border-zinc-600/80"> <div class="flex p-4 border-b border-zinc-600/80">
<h4 class="text-lg font-semibold grid gap-1 grid-cols-[1fr_28px] w-full"> <h4 class="text-lg font-semibold grid gap-1 grid-cols-[1fr_28px] w-full">
<span>{{ server.name }}</span> <span>{{ server.name }}</span>
<button class="cursor-pointer p-1 hover:backdrop-brightness-110 transition-all"> <button class="cursor-pointer p-1 bg-[hsl(223,calc(1*6.9%),19.8%)] hover:bg-[hsl(223,calc(1*6.9%),26.4%)] transition-all">
<span class="h-fit w-[20px]"> <span class="h-fit w-[20px]">
<svg xmlns="http://www.w3.org/2000/svg" <svg xmlns="http://www.w3.org/2000/svg"
width="20" width="20"
@@ -36,9 +36,9 @@
</div> </div>
<div class="flex gap-y-1.5 px-1.5 mt-2 flex-col"> <div class="flex gap-y-1.5 px-1.5 mt-2 flex-col">
<button @click="createInvite" <button @click="createInvite"
v-if="userIsOwnerOrAdmin">make invite</button> v-if="userIsOwner || userIsAdmin">make invite</button>
<button <button
class="flex text-center hover:bg-zinc-600/70 px-2 py-1.5 w-full transition-colors rounded drop-shadow-sm gap-1/5 cursor-pointer" class="flex text-center hover:bg-[hsl(223,calc(1*6.9%),26.4%)] px-2 py-1.5 w-full transition-colors rounded drop-shadow-sm gap-1/5 cursor-pointer"
v-for="channel in server.channels" v-for="channel in server.channels"
@click="openChannel(channel.id)" @click="openChannel(channel.id)"
:key="channel.id"> :key="channel.id">
@@ -57,9 +57,9 @@
</span> </span>
<span>{{ channel.name }}</span> <span>{{ channel.name }}</span>
</button> </button>
<button v-if="userIsOwnerOrAdmin" <button v-if="userIsOwner || userIsAdmin"
@click="openCreateChannelModel" @click="openCreateChannelModel"
class="flex text-center hover:bg-zinc-600/70 px-2 py-1.5 w-full transition-colors rounded drop-shadow-sm cursor-pointer"> class="flex text-center hover:bg-[hsl(223,calc(1*6.9%),26.4%)] px-2 py-1.5 w-full transition-colors rounded drop-shadow-sm cursor-pointer">
<span> <span>
<svg xmlns="http://www.w3.org/2000/svg" <svg xmlns="http://www.w3.org/2000/svg"
width="20" width="20"
@@ -132,34 +132,42 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { IRole, IServer } from '~/types'; import { useGlobalStore } from '~/stores/store';
import { IChannel, IRole, IServer } from '~/types';
export default { export default {
data() { data() {
return { return {
server: storeToRefs(useGlobalStore()).activeServer,
user: storeToRefs(useGlobalStore()).user,
dms: storeToRefs(useGlobalStore()).dms,
createChannelModelOpen: false, createChannelModelOpen: false,
channelName: '', channelName: '',
userIsOwnerOrAdmin: false userIsOwner: false,
userIsAdmin: false,
} }
}, },
mounted() { async mounted() {
setTimeout(() => { const that = this;
if (!this.server.roles) { var interval = setInterval(function () {
console.log(this.server) // get elem
throw new Error('server must be null'); if (typeof that.server.roles == 'undefined') return;
} clearInterval(interval);
this.userIsOwnerOrAdmin = this.server.roles.find((e: IRole) => e.users.some((el) => el.id === this.user.id)).owner ||
this.server.roles.find((e: IRole) => e.users.some((el) => el.id === this.user.id)).administrator that.userIsOwner = that.server.roles?.find((e: IRole) => e.users.some((el) => el.id === that.user.id))?.owner || false
}) that.userIsAdmin = that.server.roles?.find((e: IRole) => e.users.some((el) => el.id === that.user.id))?.administer || false
}, 10);
}, },
methods: { methods: {
openCreateChannelModel() { openCreateChannelModel() {
this.createChannelModelOpen = true; this.createChannelModelOpen = true;
}, },
async createChannel() { async createChannel() {
const channel = await $fetch(`/api/guilds/${this.server.id}/addChannel`, { method: 'POST', body: { channelName: this.channelName } }) const channel = await $fetch(`/api/guilds/${this.server.id}/addChannel`, { method: 'POST', body: { channelName: this.channelName } }) as IChannel
this.server.channels.push(channel) if (!channel) return;
this.server.channels?.push(channel)
this.createChannelModelOpen = false; this.createChannelModelOpen = false;
}, },
openChannel(id: string) { openChannel(id: string) {
@@ -171,6 +179,5 @@ export default {
const inviteCode = await $fetch(`/api/guilds/${this.server.id}/createInvite`, { method: 'POST' }) const inviteCode = await $fetch(`/api/guilds/${this.server.id}/createInvite`, { method: 'POST' })
}, },
}, },
props: ['server', 'user']
} }
</script> </script>

View File

@@ -2,9 +2,8 @@
<Suspense> <Suspense>
<div v-if="user.id" <div v-if="user.id"
class="flex h-screen max-h-screen text-white"> class="flex h-screen max-h-screen text-white">
<Nav :user="user" /> <Nav />
<Sidebar :server="activeServer" <Sidebar />
:user="user" />
<div class="w-[calc(100vw-88px-240px)] h-full"> <div class="w-[calc(100vw-88px-240px)] h-full">
<slot /> <slot />
</div> </div>
@@ -38,6 +37,10 @@ export default {
if (!user) return; if (!user) return;
userStore.setUser(user) userStore.setUser(user)
const { channels: dms, servers } = await $fetch('/api/user/getServers')
useGlobalStore().servers = servers
useGlobalStore().dms = dms
} }
} }
} }

View File

@@ -7,7 +7,7 @@ const route = useRoute()
const server: IChannel = await $fetch(`/api/channels/${route.params.id}`) const server: IChannel = await $fetch(`/api/channels/${route.params.id}`)
const realServer = useGlobalStore().user.servers?.filter((e) => e.channels.some((el) => el.id == route.params.id))[0] const realServer = useGlobalStore().servers?.filter((e) => e.channels.some((el) => el.id == route.params.id))[0]
if (realServer) { if (realServer) {
useGlobalStore().addServer(realServer); useGlobalStore().addServer(realServer);

View File

@@ -44,7 +44,8 @@ export default defineEventHandler(async (event) => {
server: { server: {
select: { select: {
id: true, id: true,
name: true name: true,
participants: true
} }
} }
} }

View File

@@ -113,14 +113,20 @@ export default defineEventHandler(async (event) => {
invites: { invites: {
select: { select: {
id: true, id: true,
server: true,
expires: true, expires: true,
expiryDate: true, expiryDate: true,
maxUses: true maxUses: true,
server: {
select: {
id: true,
name: true,
participants: true
}
}
} }
} }
} }
}) as IMessage }) as unknown as IMessage
global.io.emit(`message-${channel.id}`, { message }); global.io.emit(`message-${channel.id}`, { message });

View File

@@ -17,47 +17,6 @@ export default defineEventHandler(async (event) => {
select: { select: {
id: true, id: true,
username: true, username: true,
channels: {
select: {
id: true,
name: true,
messages: false,
DM: true,
dmParticipants: true
}
},
servers: {
select: {
id: true,
name: true,
channels: {
select: {
id: true,
DM: true,
name: true
}
},
participants: {
select: {
id: true,
username: true
}
},
roles: {
select: {
id: true,
name: true,
administrator: true,
owner: true,
users: {
select: {
id: true
}
}
}
}
},
},
} }
}) as SafeUser | null; }) as SafeUser | null;

View File

@@ -1,7 +1,7 @@
import bcryptjs from "bcryptjs"; import bcryptjs from "bcryptjs";
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from "uuid";
import { PrismaClient } from '@prisma/client' import { PrismaClient } from '@prisma/client'
import { IUser } from "../../types"; import { IUser, SafeUser } from "../../types";
const prisma = new PrismaClient() const prisma = new PrismaClient()
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
@@ -14,11 +14,62 @@ export default defineEventHandler(async (event) => {
} }
} }
const user = await prisma.user.findFirst({
let user = await prisma.user.findFirst({
where: { where: {
username: body.username username: body.username
} },
}) as IUser select: {
id: true,
username: true,
passwordhash: true,
email: true,
channels: {
select: {
id: true,
name: true,
messages: false,
DM: true,
dmParticipants: true,
serverId: true
}
},
servers: {
select: {
id: true,
name: true,
channels: {
select: {
id: true,
DM: true,
name: true,
serverId: true
}
},
participants: {
select: {
id: true,
username: true
}
},
roles: {
select: {
id: true,
name: true,
administrator: true,
owner: true,
users: {
select: {
id: true
}
}
}
}
},
}
},
}) as unknown
const isCorrect = await bcryptjs.compare(body.password, user.passwordhash) const isCorrect = await bcryptjs.compare(body.password, user.passwordhash)
@@ -38,6 +89,8 @@ export default defineEventHandler(async (event) => {
} }
}) })
user = user as SafeUser
return { return {
token, token,
userId: user.id, userId: user.id,

View File

@@ -41,8 +41,57 @@ export default defineEventHandler(async (event) => {
username: body.username, username: body.username,
passwordhash, passwordhash,
email: body.email email: body.email
} },
}) as SafeUser select: {
id: true,
username: true,
passwordhash: true,
email: true,
channels: {
select: {
id: true,
name: true,
messages: false,
DM: true,
dmParticipants: true,
serverId: true
}
},
servers: {
select: {
id: true,
name: true,
channels: {
select: {
id: true,
DM: true,
name: true,
serverId: true
}
},
participants: {
select: {
id: true,
username: true
}
},
roles: {
select: {
id: true,
name: true,
administrator: true,
owner: true,
users: {
select: {
id: true
}
}
}
}
},
}
},
}) as unknown
const token = uuidv4() const token = uuidv4()

View File

@@ -0,0 +1,65 @@
import { PrismaClient } from '@prisma/client'
import { IServer, IUser } from '~/types'
const prisma = new PrismaClient()
export default defineEventHandler(async (event) => {
if (!event.context.user.authenticated) {
// event.node.res.statusCode = 401;
return {
message: "Unauthenticated"
}
}
const { servers, channels } = await prisma.user.findFirst({
where: {
id: event.context.user.id
},
select: {
channels: {
select: {
id: true,
name: true,
messages: false,
DM: true,
dmParticipants: true
}
},
servers: {
select: {
id: true,
name: true,
channels: {
select: {
id: true,
DM: true,
name: true
}
},
participants: {
select: {
id: true,
username: true
}
},
roles: {
select: {
id: true,
name: true,
administrator: true,
owner: true,
users: {
select: {
id: true
}
}
}
}
},
},
}
}) as IUser | null;
return {
servers, channels
}
})

View File

@@ -4,19 +4,21 @@ import { SafeUser, IServer, IChannel } from "../types";
export const useGlobalStore = defineStore('global', { export const useGlobalStore = defineStore('global', {
state: () => ({ state: () => ({
activeServer: {} as IServer, activeServer: {} as IServer,
user: {} as SafeUser user: {} as SafeUser,
dms: [] as IChannel[],
servers: [] as IServer[]
}), }),
actions: { actions: {
setUser(user: SafeUser) { setUser(user: SafeUser) {
this.user = user; this.user = user;
}, },
addServer(server: IServer) { addServer(server: IServer) {
if (!this.user.servers || this.user.servers.find((e) => e.id === server.id)) return; if (!this.servers || this.servers.find((e) => e.id === server.id)) return;
this.user.servers.push(server) this.servers.push(server)
}, },
addDM(dmChannel: IChannel) { addDM(dmChannel: IChannel) {
if (!this.user.channels || this.user.channels.find((e) => e.id === dmChannel.id)) return; if (!this.channels || this.channels.find((e) => e.id === dmChannel.id)) return;
this.user.channels.push(dmChannel) this.channels.push(dmChannel)
}, },
setActive(type: string, serverId: string) { setActive(type: string, serverId: string) {
if (serverId === '@me') { if (serverId === '@me') {
@@ -24,21 +26,11 @@ export const useGlobalStore = defineStore('global', {
return; return;
} }
console.log(this.activeServer) console.log(this.activeServer)
if (!this.user.channels || !this.user.servers) return;
type = (type === 'dm') ? 'channels' : 'servers' const searchableArray: IChannel[] | IServer[] | undefined = this[type]
if (type !== 'channels' && type !== 'servers') return;
const searchableArray: IChannel[] | IServer[] | undefined = this["user"][type]
if (!searchableArray) return; if (!searchableArray) return;
const activeServer = searchableArray.find((e: IServer | IChannel) => e.id === serverId) this.activeServer = searchableArray.find((e: IServer | IChannel) => e.id === serverId)
console.log(searchableArray, this["user"], activeServer) console.log(this.activeServer, searchableArray.find((e: IServer | IChannel) => e.id === serverId))
if (!activeServer) return;
this.activeServer = activeServer
console.log(this.activeServer)
}, },
}, },
}) })

View File

@@ -14,7 +14,7 @@ export interface IServer {
id: string; id: string;
name: string; name: string;
channels?: Array<IChannel>; channels?: Array<IChannel>;
participants: Array<IUser>; participants: Array<SafeUser>;
roles?: Array<IRole>; roles?: Array<IRole>;
inviteCode?: Array<IInviteCode>; inviteCode?: Array<IInviteCode>;
} }
@@ -25,7 +25,7 @@ export interface IChannel {
server?: IServer; server?: IServer;
messages?: Array<IMessage> messages?: Array<IMessage>
DM: boolean; DM: boolean;
dmParticipants?: Array<IUser>; dmParticipants?: Array<SafeUser>;
serverId: string; serverId: string;
} }