fixed a bunch of bugs, yay!!!
This commit is contained in:
8
components/DropdownItem.vue
Normal file
8
components/DropdownItem.vue
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<template>
|
||||||
|
<li>
|
||||||
|
<button
|
||||||
|
class="w-full cursor-pointer bg-[hsl(225,7.7%,10.2%)] hover:bg-[hsl(225,7.7%,17.4%)] text-left px-3 py-1.5 rounded-md flex items-center">
|
||||||
|
<slot />
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</template>
|
||||||
48
components/DropdownMenu.vue
Normal file
48
components/DropdownMenu.vue
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<template>
|
||||||
|
<Transition name="pop-in">
|
||||||
|
<div ref="dropdown" class="z-[2] absolute m-2 bg-[hsl(225,7.7%,10.2%)] w-[calc(100%-1rem)] p-3 rounded text-left"
|
||||||
|
:class="(inverted) ? 'dropdown-inverse' : 'dropdown'"
|
||||||
|
v-if="opened">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
props: ['opened', 'inverted'],
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.dropdown {
|
||||||
|
transform-origin: top center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-inverse {
|
||||||
|
transform-origin: bottom center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-inverse > ul {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column-reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pop-in-enter-active {
|
||||||
|
animation: pop-in 150ms cubic-bezier(.81, .5, .44, .83);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pop-in-leave-active {
|
||||||
|
animation: pop-in 150ms reverse cubic-bezier(.81, .5, .44, .83);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pop-in {
|
||||||
|
0% {
|
||||||
|
transform: scale(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
53
components/InviteCard.vue
Normal file
53
components/InviteCard.vue
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<template>
|
||||||
|
<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 text-zinc-100">You've been invited to join a
|
||||||
|
server</p>
|
||||||
|
<span class="text-xl font-bold capitalize leading-loose">{{ 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">
|
||||||
|
<button @click="joinServer(invite)"
|
||||||
|
class="font-semibold rounded px-4 py-2 transition-colors"
|
||||||
|
:class="(userInServer) ? 'bg-green-800 cursor-not-allowed' : 'bg-green-700 hover:bg-green-600'">
|
||||||
|
<span v-if="userInServer">
|
||||||
|
Joined
|
||||||
|
</span>
|
||||||
|
<span v-else>
|
||||||
|
Join
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { useGlobalStore } from '~/stores/store'
|
||||||
|
import { IInviteCode, IUser } from '~/types'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: ['invite'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
user: storeToRefs(useGlobalStore()).user,
|
||||||
|
servers: storeToRefs(useGlobalStore()).servers,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
userInServer(): boolean {
|
||||||
|
return !!this.invite.server.participants.find((e: IUser) => e.id === this.user.id)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async joinServer(invite: IInviteCode) {
|
||||||
|
if (this.userInServer) return;
|
||||||
|
const headers = useRequestHeaders(['cookie']) as Record<string, string>;
|
||||||
|
const { server } = await $fetch('/api/guilds/joinGuild', { method: 'POST', body: { inviteId: invite.id }, headers })
|
||||||
|
if (!server) return;
|
||||||
|
this.servers?.push(server)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -1,5 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="h-full bg-[hsl(220,calc(1*7.7%),22.9%)] relative text-white">
|
<div class="h-full bg-[hsl(220,calc(1*7.7%),22.9%)] relative text-white">
|
||||||
|
<div class="bg-[hsl(220,calc(1*7.7%),22.9%)] absolute w-full shadow px-4 py-3 flex items-center z-[1] shadow-zinc-900/50">
|
||||||
|
<span>
|
||||||
|
<svg class="text-zinc-300/80 my-auto" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24">
|
||||||
|
<path fill="currentColor"
|
||||||
|
d="m5.41 21l.71-4h-4l.35-2h4l1.06-6h-4l.35-2h4l.71-4h2l-.71 4h6l.71-4h2l-.71 4h4l-.35 2h-4l-1.06 6h4l-.35 2h-4l-.71 4h-2l.71-4h-6l-.71 4h-2M9.53 9l-1.06 6h6l1.06-6h-6Z" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<span class="text-zinc-100 font-semibold">{{ server.name }}</span>
|
||||||
|
</div>
|
||||||
<div class="w-full h-[calc(100%-60px)] overflow-y-scroll pb-1"
|
<div class="w-full h-[calc(100%-60px)] overflow-y-scroll pb-1"
|
||||||
id="conversation-pane">
|
id="conversation-pane">
|
||||||
<div>
|
<div>
|
||||||
@@ -16,41 +28,18 @@
|
|||||||
</p>
|
</p>
|
||||||
<p class="break-words max-w-full">{{ message.body }}</p>
|
<p class="break-words max-w-full">{{ message.body }}</p>
|
||||||
</div>
|
</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">
|
<InviteCard :invite="invite" />
|
||||||
<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>
|
|
||||||
<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">
|
|
||||||
<button @click="joinServer(invite)"
|
|
||||||
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>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="conversation-input w-[calc(100vw-88px-240px)] h-[61.1px]">
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="conversation-input w-[calc(100vw-88px-240px)]">
|
|
||||||
<form @submit.prevent="sendMessage"
|
<form @submit.prevent="sendMessage"
|
||||||
@keydown.enter.exact.prevent="sendMessage"
|
@keydown.enter.exact.prevent="sendMessage"
|
||||||
class="relative px-4 w-full">
|
class="relative px-4 w-full pt-1.5">
|
||||||
<div id="textbox"
|
<div id="textbox"
|
||||||
class="px-4 rounded-md w-full h-[44px] bg-[hsl(218,calc(1*7.9%),27.3%)] placeholder:text-[hsl(218,calc(1*4.6%),46.9%)] flex flex-row">
|
class="px-4 rounded-md w-full h-[44px] bg-[hsl(218,calc(1*7.9%),27.3%)] placeholder:text-[hsl(218,calc(1*4.6%),46.9%)] flex flex-row">
|
||||||
<textarea type="text"
|
<textarea type="text"
|
||||||
@@ -121,47 +110,22 @@ export default {
|
|||||||
if (!lastElementChild) return;
|
if (!lastElementChild) return;
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (conversationDiv.scrollTop + 11.2 < (conversationDiv.scrollHeight - conversationDiv.clientHeight) - lastElementChild.clientHeight) return;
|
if (conversationDiv.scrollTop + 20 < (conversationDiv.scrollHeight - conversationDiv.clientHeight) - lastElementChild.clientHeight) return;
|
||||||
conversationDiv.scrollTop = conversationDiv.scrollHeight;
|
conversationDiv.scrollTop = conversationDiv.scrollHeight;
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// updated() {
|
unmounted() {
|
||||||
// const route = useRoute()
|
const socket = io();
|
||||||
// const socket = io();
|
socket.removeAllListeners();
|
||||||
|
},
|
||||||
// const conversationDiv = document.getElementById('conversation-pane');
|
|
||||||
// if (!conversationDiv) throw new Error('conversation div not found')
|
|
||||||
// this.scrollToBottom()
|
|
||||||
|
|
||||||
// socket.removeAllListeners('connect')
|
|
||||||
|
|
||||||
// socket.on('connect', () => {
|
|
||||||
// // listen for messages from the server
|
|
||||||
// socket.on(`message-${route.params.id}`, (ev) => {
|
|
||||||
// const { message } = ev
|
|
||||||
// console.log(message.userId, this.user.id, message, this.conversation)
|
|
||||||
// if (message.userId == this.user.id) return;
|
|
||||||
|
|
||||||
// this.conversation.push(message)
|
|
||||||
|
|
||||||
// const lastElementChild = conversationDiv.children[0]?.lastElementChild
|
|
||||||
// if (!lastElementChild) return;
|
|
||||||
|
|
||||||
// setTimeout(() => {
|
|
||||||
// console.log(conversationDiv.scrollTop, conversationDiv.scrollHeight, conversationDiv.clientHeight, lastElementChild.clientHeight, (conversationDiv.scrollHeight - conversationDiv.clientHeight) - lastElementChild.clientHeight)
|
|
||||||
// if (conversationDiv.scrollTop + 11.2 < (conversationDiv.scrollHeight - conversationDiv.clientHeight) - lastElementChild.clientHeight) return;
|
|
||||||
// conversationDiv.scrollTop = conversationDiv.scrollHeight;
|
|
||||||
// })
|
|
||||||
// })
|
|
||||||
// });
|
|
||||||
// },
|
|
||||||
methods: {
|
methods: {
|
||||||
async sendMessage() {
|
async sendMessage() {
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
const headers = useRequestHeaders(['cookie']) as Record<string, string>;
|
||||||
if (!this.messageContent) return;
|
if (!this.messageContent) return;
|
||||||
|
|
||||||
const message: IMessage = 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 }, headers })
|
||||||
|
|
||||||
if (!message) return;
|
if (!message) return;
|
||||||
if (this.conversation.includes(message)) return;
|
if (this.conversation.includes(message)) return;
|
||||||
@@ -174,17 +138,10 @@ export default {
|
|||||||
conversationDiv.scrollTop = conversationDiv.scrollHeight;
|
conversationDiv.scrollTop = conversationDiv.scrollHeight;
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
async joinServer(invite: IInviteCode) {
|
|
||||||
const { server } = await $fetch('/api/guilds/joinGuild', { method: 'POST', body: { inviteId: invite.id } })
|
|
||||||
if (!server) return;
|
|
||||||
this.servers?.push(server)
|
|
||||||
},
|
|
||||||
scrollToBottom() {
|
scrollToBottom() {
|
||||||
const conversationDiv = document.getElementById('conversation-pane');
|
const conversationDiv = document.getElementById('conversation-pane');
|
||||||
if (!conversationDiv) throw new Error('wtf');
|
if (!conversationDiv) throw new Error('wtf');
|
||||||
setTimeout(() => {
|
conversationDiv.scrollTo(0, conversationDiv.scrollHeight);
|
||||||
conversationDiv.scrollTop = conversationDiv.scrollHeight;
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
// resizeTextarea() {
|
// resizeTextarea() {
|
||||||
// const textArea = document.getElementById('messageBox')
|
// const textArea = document.getElementById('messageBox')
|
||||||
@@ -208,7 +165,6 @@ export default {
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
margin-top: 0.5rem;
|
margin-top: 0.5rem;
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
height: 60px;
|
|
||||||
background-color: hsl(220, calc(1 * 7.7%), 22.9%);
|
background-color: hsl(220, calc(1 * 7.7%), 22.9%);
|
||||||
bottom: calc(0px - 0.5rem);
|
bottom: calc(0px - 0.5rem);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,21 +5,36 @@
|
|||||||
<nuxt-link to="/channel/@me">
|
<nuxt-link to="/channel/@me">
|
||||||
<div @click="openServer('@me', 'dms')"
|
<div @click="openServer('@me', 'dms')"
|
||||||
class="bg-zinc-600/80 p-3 rounded-full transition-all hover:rounded-2xl ease-in-out hover:bg-zinc-500/60 duration-300">
|
class="bg-zinc-600/80 p-3 rounded-full transition-all hover:rounded-2xl ease-in-out hover:bg-zinc-500/60 duration-300">
|
||||||
|
<span>
|
||||||
<svg width="32"
|
<svg width="32"
|
||||||
height="32"
|
height="32"
|
||||||
viewBox="0 0 24 24">
|
viewBox="0 0 24 24">
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="fire"
|
||||||
|
x1="-2.778%"
|
||||||
|
x2="100%"
|
||||||
|
y1="24%"
|
||||||
|
y2="48%">
|
||||||
|
<stop offset="0%"
|
||||||
|
stop-color="#ff0c41" />
|
||||||
|
<stop offset="100%"
|
||||||
|
stop-color="#ff6b0c" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
<path fill="none"
|
<path fill="none"
|
||||||
stroke="currentColor"
|
stroke="url(#fire)"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
stroke-width="2"
|
stroke-width="2"
|
||||||
d="M12 12c2-2.96 0-7-1-8c0 3.038-1.773 4.741-3 6c-1.226 1.26-2 3.24-2 5a6 6 0 1 0 12 0c0-1.532-1.056-3.94-2-5c-1.786 3-2.791 3-4 2z" />
|
d="M12 12c2-2.96 0-7-1-8c0 3.038-1.773 4.741-3 6c-1.226 1.26-2 3.24-2 5a6 6 0 1 0 12 0c0-1.532-1.056-3.94-2-5c-1.786 3-2.791 3-4 2z" />
|
||||||
</svg>
|
</svg>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</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 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]">
|
||||||
@@ -63,9 +78,6 @@
|
|||||||
|
|
||||||
<div v-if="createServerModelOpen"
|
<div v-if="createServerModelOpen"
|
||||||
class="absolute z-10 top-0 bottom-0 left-0 right-0">
|
class="absolute z-10 top-0 bottom-0 left-0 right-0">
|
||||||
<div class="bg-zinc-900/80 w-screen h-screen"
|
|
||||||
@click="createServerModelOpen = false">
|
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
class="p-4 z-20 absolute bg-zinc-800 shadow-md rounded-md -translate-x-1/2 -translate-y-1/2 top-1/2 left-1/2 text-white">
|
class="p-4 z-20 absolute bg-zinc-800 shadow-md rounded-md -translate-x-1/2 -translate-y-1/2 top-1/2 left-1/2 text-white">
|
||||||
<h2 class="font-semibold text-xl">
|
<h2 class="font-semibold text-xl">
|
||||||
@@ -83,7 +95,11 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="bg-zinc-900/80 w-screen h-screen"
|
||||||
|
@click="createServerModelOpen = false">
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
@@ -101,12 +117,13 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
async createServer() {
|
async createServer() {
|
||||||
const globalStore = useGlobalStore();
|
const globalStore = useGlobalStore();
|
||||||
const server: IServer = await $fetch('/api/channels/create', { method: 'post', body: { serverName: this.serverName } })
|
const headers = useRequestHeaders(['cookie']) as Record<string, string>;
|
||||||
|
const server: IServer = await $fetch('/api/channels/create', { method: 'post', body: { serverName: this.serverName }, headers })
|
||||||
this.createServerModelOpen = false;
|
this.createServerModelOpen = false;
|
||||||
this.serverName = '';
|
this.serverName = '';
|
||||||
globalStore.addServer(server)
|
globalStore.addServer(server)
|
||||||
},
|
},
|
||||||
openServer(id: string, type: string): void {
|
openServer(id: string, type: "servers" | "dms"): void {
|
||||||
useGlobalStore().setActive(type, id)
|
useGlobalStore().setActive(type, id)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<aside
|
||||||
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] relative z-[2]">
|
||||||
<div v-if="!server.id || server.DM == true">
|
<div v-if="serverType === 'dms' || !server.id">
|
||||||
<div>
|
<div>
|
||||||
<nuxt-link v-for="dm in dms"
|
<nuxt-link v-for="dm in dms"
|
||||||
:to="'/channel/@me/' + dm.id">
|
:to="'/channel/@me/' + dm.id">
|
||||||
@@ -14,11 +14,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="w-full"
|
<div class="w-full"
|
||||||
v-else>
|
v-else>
|
||||||
<div class="flex p-4 border-b border-zinc-600/80">
|
<h4 @click="serverDropdownOpen = !serverDropdownOpen"
|
||||||
<h4 class="text-lg font-semibold grid gap-1 grid-cols-[1fr_28px] w-full">
|
class="py-3 px-4 font-semibold grid gap-1 grid-cols-[1fr_28px] w-full items-center cursor-pointer p-1 bg-[hsl(223,calc(1*6.9%),19.8%)] transition-all"
|
||||||
|
:class="(!serverDropdownOpen) ? 'hover:bg-[hsl(223,calc(1*6.9%),26.4%)]' : 'bg-[hsl(223,calc(1*6.9%),26.4%)]'">
|
||||||
<span>{{ server.name }}</span>
|
<span>{{ server.name }}</span>
|
||||||
<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">
|
<button>
|
||||||
<span class="h-fit w-[20px]">
|
<span v-if="!serverDropdownOpen"
|
||||||
|
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"
|
||||||
height="20"
|
height="20"
|
||||||
@@ -31,19 +33,9 @@
|
|||||||
d="m6 9l6 6l6-6" />
|
d="m6 9l6 6l6-6" />
|
||||||
</svg>
|
</svg>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
<span class="h-fit w-[20px]"
|
||||||
</h4>
|
v-else>
|
||||||
</div>
|
<svg xmlns="http://www.w3.org/2000/svg"
|
||||||
<div class="flex gap-y-1.5 px-1.5 mt-2 flex-col">
|
|
||||||
<button @click="createInvite"
|
|
||||||
v-if="userIsOwner || userIsAdmin">make invite</button>
|
|
||||||
<button
|
|
||||||
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"
|
|
||||||
@click="openChannel(channel.id)"
|
|
||||||
:key="channel.id">
|
|
||||||
<span>
|
|
||||||
<svg class="text-zinc-300 my-auto"
|
|
||||||
width="20"
|
width="20"
|
||||||
height="20"
|
height="20"
|
||||||
viewBox="0 0 24 24">
|
viewBox="0 0 24 24">
|
||||||
@@ -52,16 +44,65 @@
|
|||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
stroke-width="2"
|
stroke-width="2"
|
||||||
d="M5 9h14M5 15h14M11 4L7 20M17 4l-4 16" />
|
d="M18 6L6 18M6 6l12 12" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</h4>
|
||||||
|
<div>
|
||||||
|
<DropdownMenu :opened="serverDropdownOpen">
|
||||||
|
<div>
|
||||||
|
<ul class="flex flex-col gap-y-1">
|
||||||
|
<DropdownItem v-if="userIsOwner || userIsAdmin"
|
||||||
|
@click="createInvite">
|
||||||
|
<span class="mr-1.5 h-fit">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 24 24">
|
||||||
|
<g fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2">
|
||||||
|
<circle cx="9"
|
||||||
|
cy="7"
|
||||||
|
r="4" />
|
||||||
|
<path d="M3 21v-2a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v2m1-10h6m-3-3v6" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
Invite a friend
|
||||||
|
</span>
|
||||||
|
</DropdownItem>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-y-1.5 px-1.5 mt-2 flex-col">
|
||||||
|
<button
|
||||||
|
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 items-center"
|
||||||
|
v-for="channel in server.channels"
|
||||||
|
@click="openChannel(channel.id)"
|
||||||
|
:key="channel.id">
|
||||||
|
<span class="h-fit">
|
||||||
|
<svg class="text-zinc-300/80 my-auto"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 24 24">
|
||||||
|
<path fill="currentColor"
|
||||||
|
d="m5.41 21l.71-4h-4l.35-2h4l1.06-6h-4l.35-2h4l.71-4h2l-.71 4h6l.71-4h2l-.71 4h4l-.35 2h-4l-1.06 6h4l-.35 2h-4l-.71 4h-2l.71-4h-6l-.71 4h-2M9.53 9l-1.06 6h6l1.06-6h-6Z" />
|
||||||
</svg>
|
</svg>
|
||||||
</span>
|
</span>
|
||||||
<span>{{ channel.name }}</span>
|
<span>{{ channel.name }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button v-if="userIsOwner || userIsAdmin"
|
<button v-if="userIsOwner || userIsAdmin"
|
||||||
@click="openCreateChannelModel"
|
@click="openCreateChannelModel"
|
||||||
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">
|
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 items-center">
|
||||||
<span>
|
<span>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg"
|
<svg class="text-zinc-300/80 my-auto" xmlns="http://www.w3.org/2000/svg"
|
||||||
width="20"
|
width="20"
|
||||||
height="20"
|
height="20"
|
||||||
viewBox="0 0 24 24">
|
viewBox="0 0 24 24">
|
||||||
@@ -78,12 +119,44 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="relative">
|
||||||
|
<DropdownMenu class="bottom-full"
|
||||||
|
:inverted="true"
|
||||||
|
:opened="userDropdownOpen">
|
||||||
<div>
|
<div>
|
||||||
|
<ul class="flex flex-col gap-y-1">
|
||||||
|
<DropdownItem v-if="userIsOwner || userIsAdmin"
|
||||||
|
@click="createInvite">
|
||||||
|
<span class="mr-1.5 h-fit">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 24 24">
|
||||||
|
<g fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2">
|
||||||
|
<circle cx="9"
|
||||||
|
cy="7"
|
||||||
|
r="4" />
|
||||||
|
<path d="M3 21v-2a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v2m1-10h6m-3-3v6" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
Invite a friend
|
||||||
|
</span>
|
||||||
|
</DropdownItem>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</DropdownMenu>
|
||||||
<div class="bg-[hsl(220,calc(1*6.8%),17.3%)] h-full p-3">
|
<div class="bg-[hsl(220,calc(1*6.8%),17.3%)] h-full p-3">
|
||||||
<div class="grid grid-cols-[32px_1fr_32px] gap-x-2 items-center">
|
<div class="grid grid-cols-[32px_1fr_32px] gap-x-2 items-center">
|
||||||
<span class="bg-[hsl(220,calc(1*6.8%),22.6%)] w-[32px] h-[32px] rounded-full"></span>
|
<span class="bg-[hsl(220,calc(1*6.8%),22.6%)] w-[32px] h-[32px] rounded-full"></span>
|
||||||
<span class="h-fit w-fit overflow-ellipsis">{{ user.username }}</span>
|
<span class="h-fit w-fit overflow-ellipsis">{{ user.username }}</span>
|
||||||
<span class="text-zinc-300 hover:bg-[hsl(220,calc(1*6.8%),14.3%)] p-1 transition-colors">
|
<button @click="userDropdownOpen = !userDropdownOpen"
|
||||||
|
class="text-zinc-300 hover:bg-[hsl(220,calc(1*6.8%),14.3%)] p-1 transition-colors">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg"
|
<svg xmlns="http://www.w3.org/2000/svg"
|
||||||
width="24"
|
width="24"
|
||||||
height="24"
|
height="24"
|
||||||
@@ -100,11 +173,11 @@
|
|||||||
r="3" />
|
r="3" />
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
</span>
|
</button>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</aside>
|
||||||
|
|
||||||
<div v-if="createChannelModelOpen"
|
<div v-if="createChannelModelOpen"
|
||||||
class="absolute z-10 top-0 bottom-0 left-0 right-0">
|
class="absolute z-10 top-0 bottom-0 left-0 right-0">
|
||||||
@@ -139,31 +212,30 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
server: storeToRefs(useGlobalStore()).activeServer,
|
server: storeToRefs(useGlobalStore()).activeServer,
|
||||||
|
serverType: storeToRefs(useGlobalStore()).activeServerType,
|
||||||
user: storeToRefs(useGlobalStore()).user,
|
user: storeToRefs(useGlobalStore()).user,
|
||||||
dms: storeToRefs(useGlobalStore()).dms,
|
dms: storeToRefs(useGlobalStore()).dms,
|
||||||
createChannelModelOpen: false,
|
createChannelModelOpen: false,
|
||||||
|
serverDropdownOpen: false,
|
||||||
|
userDropdownOpen: false,
|
||||||
channelName: '',
|
channelName: '',
|
||||||
userIsOwner: false,
|
|
||||||
userIsAdmin: false,
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async mounted() {
|
computed: {
|
||||||
const that = this;
|
userIsOwner() {
|
||||||
var interval = setInterval(function () {
|
return this.server && this.serverType === "servers" && this.server.roles?.find((e: IRole) => e.users.some((el) => el.id === this.user.id))?.owner
|
||||||
// get elem
|
},
|
||||||
if (typeof that.server.roles == 'undefined') return;
|
userIsAdmin() {
|
||||||
clearInterval(interval);
|
return this.server && this.serverType === "servers" && this.server.roles?.find((e: IRole) => e.users.some((el) => el.id === this.user.id))?.administer
|
||||||
|
}
|
||||||
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 } }) as IChannel
|
const headers = useRequestHeaders(['cookie']) as Record<string, string>;
|
||||||
|
const channel = await $fetch(`/api/guilds/${this.server.id}/addChannel`, { method: 'POST', body: { channelName: this.channelName }, headers }) as IChannel
|
||||||
|
|
||||||
if (!channel) return;
|
if (!channel) return;
|
||||||
|
|
||||||
@@ -176,7 +248,8 @@ export default {
|
|||||||
router.push({ params: { id } })
|
router.push({ params: { id } })
|
||||||
},
|
},
|
||||||
async createInvite() {
|
async createInvite() {
|
||||||
const inviteCode = await $fetch(`/api/guilds/${this.server.id}/createInvite`, { method: 'POST' })
|
const headers = useRequestHeaders(['cookie']) as Record<string, string>
|
||||||
|
const inviteCode = await $fetch(`/api/guilds/${this.server.id}/createInvite`, { method: 'POST', headers })
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<Suspense>
|
<Suspense>
|
||||||
<div v-if="user.id"
|
<div class="flex h-screen max-h-screen text-white">
|
||||||
class="flex h-screen max-h-screen text-white">
|
|
||||||
<Nav />
|
<Nav />
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
<div class="w-[calc(100vw-88px-240px)] h-full">
|
<div class="w-[calc(100vw-88px-240px)] h-full">
|
||||||
@@ -23,24 +22,36 @@ import { SafeUser } from '~/types'
|
|||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
activeServer: storeToRefs(useGlobalStore()).activeServer,
|
user: storeToRefs(useGlobalStore()).user,
|
||||||
user: storeToRefs(useGlobalStore()).user
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async setup() {
|
async setup() {
|
||||||
|
const globalStore = useGlobalStore()
|
||||||
const userStore = useGlobalStore()
|
|
||||||
const sessionToken = useCookie('sessionToken')
|
const sessionToken = useCookie('sessionToken')
|
||||||
if (userStore.user.id === undefined && sessionToken.value) {
|
if (globalStore.user.id === undefined && sessionToken.value) {
|
||||||
const user: SafeUser = await $fetch('/api/getCurrentUser')
|
const route = useRoute()
|
||||||
|
const headers = useRequestHeaders(['cookie']) as Record<string, string>
|
||||||
|
const [user, { dms, servers }] = await Promise.all([
|
||||||
|
$fetch('/api/getCurrentUser', { headers }) as unknown as SafeUser,
|
||||||
|
$fetch('/api/user/getServers', { headers })
|
||||||
|
])
|
||||||
|
|
||||||
if (!user) return;
|
if (!user || !servers || !dms) return;
|
||||||
|
|
||||||
userStore.setUser(user)
|
globalStore.setUser(user)
|
||||||
const { channels: dms, servers } = await $fetch('/api/user/getServers')
|
|
||||||
|
|
||||||
useGlobalStore().servers = servers
|
globalStore.setServers(servers)
|
||||||
useGlobalStore().dms = dms
|
globalStore.setDms(dms)
|
||||||
|
console.log('params', route.params.id)
|
||||||
|
if (route.params.id && typeof route.params.id === 'string') {
|
||||||
|
globalStore.setActive(route.path.includes('@me') ? 'dms' : 'servers', route.params.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
const server = globalStore.activeServer
|
||||||
|
|
||||||
|
return {
|
||||||
|
server
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,4 +36,8 @@ export default {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
|
typescript: {
|
||||||
|
strict: true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,16 +2,6 @@
|
|||||||
<MessagePane :server="server" />
|
<MessagePane :server="server" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script async setup lang="ts">
|
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
const server: IChannel = await $fetch(`/api/channels/${route.params.id}`)
|
|
||||||
if (server) {
|
|
||||||
useGlobalStore().addDM(server);
|
|
||||||
useGlobalStore().setActive('dms', server.id);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { useGlobalStore } from '~/stores/store'
|
import { useGlobalStore } from '~/stores/store'
|
||||||
import { IChannel } from '~/types'
|
import { IChannel } from '~/types'
|
||||||
@@ -21,8 +11,24 @@ definePageMeta({
|
|||||||
})
|
})
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
async setup() {
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
const headers = useRequestHeaders(['cookie']) as Record<string, string>
|
||||||
|
const server: IChannel = await $fetch(`/api/channels/${route.params.id}`, { headers })
|
||||||
|
if (!server) throw new Error('could not find the dm')
|
||||||
|
useGlobalStore().addDM(server);
|
||||||
|
if (typeof route.params.id !== 'string') throw new Error('route.params.id must be a string, but got an array presumably?')
|
||||||
|
useGlobalStore().setActive('dms', route.params.id);
|
||||||
|
|
||||||
|
return {
|
||||||
|
server
|
||||||
|
}
|
||||||
|
},
|
||||||
async updated() {
|
async updated() {
|
||||||
if (!useGlobalStore().activeServer == this.server) useGlobalStore().setActive('dms', this.server.id)
|
const route = useRoute()
|
||||||
|
if (typeof route.params.id !== 'string') throw new Error('route.params.id must be a string, but got an array presumably?')
|
||||||
|
if (useGlobalStore().activeServer !== this.server) useGlobalStore().setActive('dms', route.params.id)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -19,9 +19,14 @@ export default {
|
|||||||
userId: ''
|
userId: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
console.log('mounted')
|
||||||
|
useGlobalStore().setActive('dms', '@me')
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async startDM() {
|
async startDM() {
|
||||||
const server: IChannel = await $fetch('/api/channels/createDM', { method: 'post', body: { partnerId: this.userId } })
|
const headers = useRequestHeaders(['cookie']) as Record<string, string>
|
||||||
|
const server: IChannel = await $fetch('/api/channels/createDM', { method: 'post', body: { partnerId: this.userId }, headers })
|
||||||
|
|
||||||
useGlobalStore().addDM(server)
|
useGlobalStore().addDM(server)
|
||||||
useRouter().push({ path: '/channel/@me/' + server.id })
|
useRouter().push({ path: '/channel/@me/' + server.id })
|
||||||
|
|||||||
@@ -2,19 +2,6 @@
|
|||||||
<MessagePane :server="server" />
|
<MessagePane :server="server" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script async setup lang="ts">
|
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
const server: IChannel = await $fetch(`/api/channels/${route.params.id}`)
|
|
||||||
|
|
||||||
const realServer = useGlobalStore().servers?.filter((e) => e.channels.some((el) => el.id == route.params.id))[0]
|
|
||||||
|
|
||||||
if (realServer) {
|
|
||||||
useGlobalStore().addServer(realServer);
|
|
||||||
useGlobalStore().setActive('servers', realServer.id)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { useGlobalStore } from '~/stores/store'
|
import { useGlobalStore } from '~/stores/store'
|
||||||
import { IChannel } from '~/types'
|
import { IChannel } from '~/types'
|
||||||
@@ -24,12 +11,32 @@ definePageMeta({
|
|||||||
})
|
})
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
async setup() {
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
const headers = useRequestHeaders(['cookie']) as Record<string, string>
|
||||||
|
const server: IChannel = await $fetch(`/api/channels/${route.params.id}`, { headers })
|
||||||
|
|
||||||
|
const realServer = useGlobalStore().servers?.find((e) => e.channels.some((el) => el.id == route.params.id))
|
||||||
|
|
||||||
|
if (!realServer) throw new Error('realServer not found, this means that the channel is serverless but not a dm????');
|
||||||
|
useGlobalStore().addServer(realServer);
|
||||||
|
if (typeof route.params.id !== 'string') throw new Error('route.params.id must be a string, but got an array presumiably?')
|
||||||
|
useGlobalStore().setActive('servers', route.params.id)
|
||||||
|
|
||||||
|
return {
|
||||||
|
server
|
||||||
|
}
|
||||||
|
},
|
||||||
async updated() {
|
async updated() {
|
||||||
|
const route = useRoute()
|
||||||
|
const headers = useRequestHeaders(['cookie']) as Record<string, string>;
|
||||||
if (!this.server) return;
|
if (!this.server) return;
|
||||||
|
|
||||||
this.server = await $fetch(`/api/channels/${route.params.id}`);
|
this.server = await $fetch(`/api/channels/${route.params.id}`, { headers });
|
||||||
|
|
||||||
if (!useGlobalStore().activeServer == this.server.id) useGlobalStore().setActive('servers', this.server.id)
|
if (typeof route.params.id !== 'string') throw new Error('route.params.id must be a string, but got an array presumiably?')
|
||||||
|
if (useGlobalStore().activeServer.id !== this.server.id) useGlobalStore().setActive('servers', route.params.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -37,12 +37,14 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async signup() {
|
async signup() {
|
||||||
|
const headers = useRequestHeaders(['cookie'])
|
||||||
if (!this.username || !this.password) return;
|
if (!this.username || !this.password) return;
|
||||||
const user = await $fetch('/api/login', {
|
const user = await $fetch('/api/login', {
|
||||||
method: 'post', body: {
|
method: 'post', body: {
|
||||||
username: this.username,
|
username: this.username,
|
||||||
password: this.password
|
password: this.password
|
||||||
}
|
},
|
||||||
|
headers
|
||||||
}) as { userId: string; token: string; user: SafeUser; }
|
}) as { userId: string; token: string; user: SafeUser; }
|
||||||
|
|
||||||
const userId = useCookie('userId')
|
const userId = useCookie('userId')
|
||||||
|
|||||||
@@ -48,13 +48,15 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async signup() {
|
async signup() {
|
||||||
|
const headers = useRequestHeaders(['cookie']) as Record<string, string>
|
||||||
if (!this.username || !this.password || !this.email) return;
|
if (!this.username || !this.password || !this.email) return;
|
||||||
const user = await $fetch('/api/signup', {
|
const user = await $fetch('/api/signup', {
|
||||||
method: 'post', body: {
|
method: 'post', body: {
|
||||||
username: this.username,
|
username: this.username,
|
||||||
email: this.email,
|
email: this.email,
|
||||||
password: this.password
|
password: this.password
|
||||||
}
|
},
|
||||||
|
headers
|
||||||
}) as { userId: string; token: string; user: SafeUser; }
|
}) as { userId: string; token: string; user: SafeUser; }
|
||||||
|
|
||||||
const userId = useCookie('userId')
|
const userId = useCookie('userId')
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ const prisma = new PrismaClient()
|
|||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
if (!event.context.user.authenticated) {
|
if (!event.context.user.authenticated) {
|
||||||
|
event.node.res.statusCode = 401;
|
||||||
return {
|
return {
|
||||||
message: 'You must be logged in to view a channel.'
|
message: 'You must be logged in to view a channel.'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ const prisma = new PrismaClient()
|
|||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
if (!event.context.user.authenticated) {
|
if (!event.context.user.authenticated) {
|
||||||
// event.node.res.statusCode = 401;
|
event.node.res.statusCode = 401;
|
||||||
return {
|
return {
|
||||||
message: "Unauthenticated"
|
message: "Unauthenticated"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ const prisma = new PrismaClient()
|
|||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
if (!event.context.user.authenticated) {
|
if (!event.context.user.authenticated) {
|
||||||
|
event.node.res.statusCode = 401;
|
||||||
return {
|
return {
|
||||||
message: 'You must be logged in to view a channel.'
|
message: 'You must be logged in to view a channel.'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ const prisma = new PrismaClient()
|
|||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
if (!event.context.user.authenticated) {
|
if (!event.context.user.authenticated) {
|
||||||
|
event.node.res.statusCode = 401;
|
||||||
return {
|
return {
|
||||||
message: 'You must be logged in to view a channel.'
|
message: 'You must be logged in to view a channel.'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,12 @@ import { PrismaClient } from '@prisma/client'
|
|||||||
const prisma = new PrismaClient()
|
const prisma = new PrismaClient()
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
if (!event.context.user.authenticated) return {
|
if (!event.context.user.authenticated) {
|
||||||
|
event.node.res.statusCode = 401;
|
||||||
|
return {
|
||||||
message: 'You must be logged in to view a channel.'
|
message: 'You must be logged in to view a channel.'
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!event.context.params.id) {
|
if (!event.context.params.id) {
|
||||||
event.node.res.statusCode = 400;
|
event.node.res.statusCode = 400;
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
import { IServer } from '~/types'
|
import { IInviteCode, IServer } from '~/types'
|
||||||
import { PrismaClient } from '@prisma/client'
|
import { PrismaClient } from '@prisma/client'
|
||||||
const prisma = new PrismaClient()
|
const prisma = new PrismaClient()
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
if (!event.context.user.authenticated) return {
|
if (!event.context.user.authenticated) {
|
||||||
|
event.node.res.statusCode = 401;
|
||||||
|
return {
|
||||||
message: 'You must be logged in to view a channel.'
|
message: 'You must be logged in to view a channel.'
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const { inviteId } = await readBody(event);
|
const { inviteId } = await readBody(event);
|
||||||
|
|
||||||
@@ -20,10 +23,24 @@ export default defineEventHandler(async (event) => {
|
|||||||
where: {
|
where: {
|
||||||
id: inviteId
|
id: inviteId
|
||||||
},
|
},
|
||||||
include: {
|
select: {
|
||||||
server: true
|
id: true,
|
||||||
|
server: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
participants: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
expires: true,
|
||||||
|
expiryDate: true,
|
||||||
|
maxUses: true
|
||||||
|
}
|
||||||
|
}) as IInviteCode | null;
|
||||||
|
|
||||||
if (!invite) {
|
if (!invite) {
|
||||||
event.node.res.statusCode = 404;
|
event.node.res.statusCode = 404;
|
||||||
@@ -32,6 +49,15 @@ export default defineEventHandler(async (event) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const userInServer = invite.server.participants.find((e) => e.id === event.context.user.id);
|
||||||
|
|
||||||
|
if (userInServer) {
|
||||||
|
event.node.res.statusCode = 409;
|
||||||
|
return {
|
||||||
|
message: `You are already in that server.`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: check if invite is valid
|
// TODO: check if invite is valid
|
||||||
|
|
||||||
const server = await prisma.server.update({
|
const server = await prisma.server.update({
|
||||||
@@ -50,7 +76,7 @@ export default defineEventHandler(async (event) => {
|
|||||||
channels: true,
|
channels: true,
|
||||||
roles: true
|
roles: true
|
||||||
}
|
}
|
||||||
}) as IServer
|
}) as unknown as IServer
|
||||||
|
|
||||||
if (!server) {
|
if (!server) {
|
||||||
event.node.res.statusCode = 404;
|
event.node.res.statusCode = 404;
|
||||||
|
|||||||
@@ -1,30 +1,23 @@
|
|||||||
import { PrismaClient } from '@prisma/client'
|
import { PrismaClient } from '@prisma/client'
|
||||||
import { IServer, IUser } from '~/types'
|
import { IChannel, IServer, IUser } from '~/types'
|
||||||
const prisma = new PrismaClient()
|
const prisma = new PrismaClient()
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
if (!event.context.user.authenticated) {
|
if (!event.context.user.authenticated) {
|
||||||
// event.node.res.statusCode = 401;
|
event.node.res.statusCode = 401;
|
||||||
return {
|
return {
|
||||||
message: "Unauthenticated"
|
message: "Unauthenticated"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { servers, channels } = await prisma.user.findFirst({
|
const servers = await prisma.server.findMany({
|
||||||
where: {
|
where: {
|
||||||
|
participants: {
|
||||||
|
some: {
|
||||||
id: event.context.user.id
|
id: event.context.user.id
|
||||||
},
|
}
|
||||||
select: {
|
|
||||||
channels: {
|
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
name: true,
|
|
||||||
messages: false,
|
|
||||||
DM: true,
|
|
||||||
dmParticipants: true
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
servers: {
|
|
||||||
select: {
|
select: {
|
||||||
id: true,
|
id: true,
|
||||||
name: true,
|
name: true,
|
||||||
@@ -54,12 +47,33 @@ export default defineEventHandler(async (event) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}) as IUser | null;
|
}) as unknown as IServer[] | null;
|
||||||
|
|
||||||
|
const dms = await prisma.channel.findMany({
|
||||||
|
where: {
|
||||||
|
DM: true,
|
||||||
|
dmParticipants: {
|
||||||
|
some: {
|
||||||
|
id: event.context.user.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
messages: false,
|
||||||
|
DM: true,
|
||||||
|
dmParticipants: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
username: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}) as IChannel[] | null;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
servers, channels
|
servers, dms
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import { Ref } from "vue";
|
|
||||||
import { SafeUser, IServer, IChannel } from "../types";
|
import { SafeUser, IServer, IChannel } from "../types";
|
||||||
|
|
||||||
export const useGlobalStore = defineStore('global', {
|
export const useGlobalStore = defineStore('global', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
activeServer: {} as IServer,
|
activeServer: {} as IServer | IChannel,
|
||||||
|
activeServerType: '' as "dms" | "servers" | undefined,
|
||||||
user: {} as SafeUser,
|
user: {} as SafeUser,
|
||||||
dms: [] as IChannel[],
|
dms: [] as IChannel[],
|
||||||
servers: [] as IServer[]
|
servers: [] as IServer[]
|
||||||
@@ -17,20 +17,38 @@ export const useGlobalStore = defineStore('global', {
|
|||||||
this.servers.push(server)
|
this.servers.push(server)
|
||||||
},
|
},
|
||||||
addDM(dmChannel: IChannel) {
|
addDM(dmChannel: IChannel) {
|
||||||
if (!this.channels || this.channels.find((e) => e.id === dmChannel.id)) return;
|
if (!this.dms || this.dms.find((e) => e.id === dmChannel.id)) return;
|
||||||
this.channels.push(dmChannel)
|
this.dms.push(dmChannel)
|
||||||
},
|
},
|
||||||
setActive(type: string, serverId: string) {
|
setServers(servers: Array<IServer>) {
|
||||||
if (serverId === '@me') {
|
this.servers = servers
|
||||||
this.activeServer = {} as IServer
|
},
|
||||||
|
setDms(dms: Array<IChannel>) {
|
||||||
|
this.dms = dms
|
||||||
|
},
|
||||||
|
setActive(type: "servers" | "dms", channelId: string) {
|
||||||
|
if (channelId === '@me') {
|
||||||
|
this.activeServer = {} as IServer | IChannel
|
||||||
|
this.activeServerType = 'dms'
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log(this.activeServer)
|
|
||||||
|
this.activeServerType = type
|
||||||
|
|
||||||
const searchableArray: IChannel[] | IServer[] | undefined = this[type]
|
const searchableArray: IChannel[] | IServer[] | undefined = this[type]
|
||||||
if (!searchableArray) return;
|
if (!searchableArray) return;
|
||||||
this.activeServer = searchableArray.find((e: IServer | IChannel) => e.id === serverId)
|
let activeServerIndex: number;
|
||||||
console.log(this.activeServer, searchableArray.find((e: IServer | IChannel) => e.id === serverId))
|
if (type === 'servers') {
|
||||||
|
activeServerIndex = searchableArray.findIndex((e) => {
|
||||||
|
return e.channels.some((channel: IChannel) => channel.id === channelId)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
activeServerIndex = searchableArray.findIndex((e) => {
|
||||||
|
return e.id === channelId
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.activeServer = this.servers[activeServerIndex]
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,30 +1,3 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
|
||||||
"target": "ES2020",
|
|
||||||
"lib": ["ESNext", "ESNext.AsyncIterable", "DOM"],
|
|
||||||
"allowJs": true,
|
|
||||||
"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,
|
|
||||||
"baseUrl": ".",
|
|
||||||
"paths": {
|
|
||||||
"~/*": [
|
|
||||||
"./*"
|
|
||||||
],
|
|
||||||
"@/*": [
|
|
||||||
"./*"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"extends": "./.nuxt/tsconfig.json"
|
"extends": "./.nuxt/tsconfig.json"
|
||||||
}
|
}
|
||||||
30
tsconfig.json.bak
Normal file
30
tsconfig.json.bak
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"lib": ["ESNext", "ESNext.AsyncIterable", "DOM"],
|
||||||
|
"allowJs": true,
|
||||||
|
"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,
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"~/*": [
|
||||||
|
"./*"
|
||||||
|
],
|
||||||
|
"@/*": [
|
||||||
|
"./*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"extends": "./.nuxt/tsconfig.json"
|
||||||
|
}
|
||||||
@@ -13,16 +13,16 @@ export type SafeUser = Omit<Omit<IUser, 'passwordhash'>, 'email'>
|
|||||||
export interface IServer {
|
export interface IServer {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
channels?: Array<IChannel>;
|
channels: Array<IChannel>;
|
||||||
participants: Array<SafeUser>;
|
participants: Array<SafeUser>;
|
||||||
roles?: Array<IRole>;
|
roles: Array<IRole>;
|
||||||
inviteCode?: Array<IInviteCode>;
|
inviteCode?: Array<IInviteCode>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IChannel {
|
export interface IChannel {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
server?: IServer;
|
server: IServer;
|
||||||
messages?: Array<IMessage>
|
messages?: Array<IMessage>
|
||||||
DM: boolean;
|
DM: boolean;
|
||||||
dmParticipants?: Array<SafeUser>;
|
dmParticipants?: Array<SafeUser>;
|
||||||
|
|||||||
Reference in New Issue
Block a user