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>
|
||||
<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"
|
||||
id="conversation-pane">
|
||||
<div>
|
||||
@@ -16,41 +28,18 @@
|
||||
</p>
|
||||
<p class="break-words max-w-full">{{ message.body }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<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">
|
||||
<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 v-for="invite in message.invites">
|
||||
<InviteCard :invite="invite" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="conversation-input w-[calc(100vw-88px-240px)]">
|
||||
<div class="conversation-input w-[calc(100vw-88px-240px)] h-[61.1px]">
|
||||
<form @submit.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"
|
||||
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"
|
||||
@@ -121,47 +110,22 @@ export default {
|
||||
if (!lastElementChild) return;
|
||||
|
||||
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;
|
||||
})
|
||||
});
|
||||
},
|
||||
// updated() {
|
||||
// const route = useRoute()
|
||||
// const socket = io();
|
||||
|
||||
// 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;
|
||||
// })
|
||||
// })
|
||||
// });
|
||||
// },
|
||||
unmounted() {
|
||||
const socket = io();
|
||||
socket.removeAllListeners();
|
||||
},
|
||||
methods: {
|
||||
async sendMessage() {
|
||||
const route = useRoute()
|
||||
const headers = useRequestHeaders(['cookie']) as Record<string, string>;
|
||||
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 (this.conversation.includes(message)) return;
|
||||
@@ -174,17 +138,10 @@ export default {
|
||||
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() {
|
||||
const conversationDiv = document.getElementById('conversation-pane');
|
||||
if (!conversationDiv) throw new Error('wtf');
|
||||
setTimeout(() => {
|
||||
conversationDiv.scrollTop = conversationDiv.scrollHeight;
|
||||
})
|
||||
conversationDiv.scrollTo(0, conversationDiv.scrollHeight);
|
||||
}
|
||||
// resizeTextarea() {
|
||||
// const textArea = document.getElementById('messageBox')
|
||||
@@ -208,7 +165,6 @@ export default {
|
||||
flex-direction: row;
|
||||
margin-top: 0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
height: 60px;
|
||||
background-color: hsl(220, calc(1 * 7.7%), 22.9%);
|
||||
bottom: calc(0px - 0.5rem);
|
||||
}
|
||||
|
||||
@@ -5,21 +5,36 @@
|
||||
<nuxt-link to="/channel/@me">
|
||||
<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">
|
||||
<svg width="32"
|
||||
height="32"
|
||||
viewBox="0 0 24 24">
|
||||
<path fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
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" />
|
||||
</svg>
|
||||
<span>
|
||||
<svg width="32"
|
||||
height="32"
|
||||
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"
|
||||
stroke="url(#fire)"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
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" />
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</nuxt-link>
|
||||
</div>
|
||||
<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"
|
||||
@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]">
|
||||
@@ -63,9 +78,6 @@
|
||||
|
||||
<div v-if="createServerModelOpen"
|
||||
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
|
||||
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">
|
||||
@@ -83,7 +95,11 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-zinc-900/80 w-screen h-screen"
|
||||
@click="createServerModelOpen = false">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@@ -101,12 +117,13 @@ export default {
|
||||
methods: {
|
||||
async createServer() {
|
||||
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.serverName = '';
|
||||
globalStore.addServer(server)
|
||||
},
|
||||
openServer(id: string, type: string): void {
|
||||
openServer(id: string, type: "servers" | "dms"): void {
|
||||
useGlobalStore().setActive(type, id)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div
|
||||
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">
|
||||
<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] relative z-[2]">
|
||||
<div v-if="serverType === 'dms' || !server.id">
|
||||
<div>
|
||||
<nuxt-link v-for="dm in dms"
|
||||
:to="'/channel/@me/' + dm.id">
|
||||
@@ -14,36 +14,14 @@
|
||||
</div>
|
||||
<div class="w-full"
|
||||
v-else>
|
||||
<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">
|
||||
<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">
|
||||
<span class="h-fit w-[20px]">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 24 24">
|
||||
<path fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="m6 9l6 6l6-6" />
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</h4>
|
||||
</div>
|
||||
<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"
|
||||
<h4 @click="serverDropdownOpen = !serverDropdownOpen"
|
||||
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>
|
||||
<button>
|
||||
<span v-if="!serverDropdownOpen"
|
||||
class="h-fit w-[20px]">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 24 24">
|
||||
@@ -52,16 +30,79 @@
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M5 9h14M5 15h14M11 4L7 20M17 4l-4 16" />
|
||||
d="m6 9l6 6l6-6" />
|
||||
</svg>
|
||||
</span>
|
||||
<span class="h-fit w-[20px]"
|
||||
v-else>
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 24 24">
|
||||
<path fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
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>
|
||||
</span>
|
||||
<span>{{ channel.name }}</span>
|
||||
</button>
|
||||
<button v-if="userIsOwner || userIsAdmin"
|
||||
@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>
|
||||
<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"
|
||||
height="20"
|
||||
viewBox="0 0 24 24">
|
||||
@@ -78,12 +119,44 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="relative">
|
||||
<DropdownMenu class="bottom-full"
|
||||
:inverted="true"
|
||||
:opened="userDropdownOpen">
|
||||
<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="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="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"
|
||||
width="24"
|
||||
height="24"
|
||||
@@ -100,11 +173,11 @@
|
||||
r="3" />
|
||||
</g>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<div v-if="createChannelModelOpen"
|
||||
class="absolute z-10 top-0 bottom-0 left-0 right-0">
|
||||
@@ -139,31 +212,30 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
server: storeToRefs(useGlobalStore()).activeServer,
|
||||
serverType: storeToRefs(useGlobalStore()).activeServerType,
|
||||
user: storeToRefs(useGlobalStore()).user,
|
||||
dms: storeToRefs(useGlobalStore()).dms,
|
||||
createChannelModelOpen: false,
|
||||
serverDropdownOpen: false,
|
||||
userDropdownOpen: false,
|
||||
channelName: '',
|
||||
userIsOwner: false,
|
||||
userIsAdmin: false,
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
const that = this;
|
||||
var interval = setInterval(function () {
|
||||
// get elem
|
||||
if (typeof that.server.roles == 'undefined') return;
|
||||
clearInterval(interval);
|
||||
|
||||
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);
|
||||
computed: {
|
||||
userIsOwner() {
|
||||
return this.server && this.serverType === "servers" && this.server.roles?.find((e: IRole) => e.users.some((el) => el.id === this.user.id))?.owner
|
||||
},
|
||||
userIsAdmin() {
|
||||
return this.server && this.serverType === "servers" && this.server.roles?.find((e: IRole) => e.users.some((el) => el.id === this.user.id))?.administer
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openCreateChannelModel() {
|
||||
this.createChannelModelOpen = true;
|
||||
},
|
||||
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;
|
||||
|
||||
@@ -176,7 +248,8 @@ export default {
|
||||
router.push({ params: { id } })
|
||||
},
|
||||
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>
|
||||
<Suspense>
|
||||
<div v-if="user.id"
|
||||
class="flex h-screen max-h-screen text-white">
|
||||
<div class="flex h-screen max-h-screen text-white">
|
||||
<Nav />
|
||||
<Sidebar />
|
||||
<div class="w-[calc(100vw-88px-240px)] h-full">
|
||||
@@ -23,24 +22,36 @@ import { SafeUser } from '~/types'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
activeServer: storeToRefs(useGlobalStore()).activeServer,
|
||||
user: storeToRefs(useGlobalStore()).user
|
||||
user: storeToRefs(useGlobalStore()).user,
|
||||
}
|
||||
},
|
||||
async setup() {
|
||||
|
||||
const userStore = useGlobalStore()
|
||||
const globalStore = useGlobalStore()
|
||||
const sessionToken = useCookie('sessionToken')
|
||||
if (userStore.user.id === undefined && sessionToken.value) {
|
||||
const user: SafeUser = await $fetch('/api/getCurrentUser')
|
||||
if (globalStore.user.id === undefined && sessionToken.value) {
|
||||
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)
|
||||
const { channels: dms, servers } = await $fetch('/api/user/getServers')
|
||||
globalStore.setUser(user)
|
||||
|
||||
useGlobalStore().servers = servers
|
||||
useGlobalStore().dms = dms
|
||||
globalStore.setServers(servers)
|
||||
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" />
|
||||
</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">
|
||||
import { useGlobalStore } from '~/stores/store'
|
||||
import { IChannel } from '~/types'
|
||||
@@ -21,8 +11,24 @@ definePageMeta({
|
||||
})
|
||||
|
||||
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() {
|
||||
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>
|
||||
@@ -19,9 +19,14 @@ export default {
|
||||
userId: ''
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
console.log('mounted')
|
||||
useGlobalStore().setActive('dms', '@me')
|
||||
},
|
||||
methods: {
|
||||
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)
|
||||
useRouter().push({ path: '/channel/@me/' + server.id })
|
||||
|
||||
@@ -2,19 +2,6 @@
|
||||
<MessagePane :server="server" />
|
||||
</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">
|
||||
import { useGlobalStore } from '~/stores/store'
|
||||
import { IChannel } from '~/types'
|
||||
@@ -24,12 +11,32 @@ definePageMeta({
|
||||
})
|
||||
|
||||
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() {
|
||||
const route = useRoute()
|
||||
const headers = useRequestHeaders(['cookie']) as Record<string, string>;
|
||||
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>
|
||||
@@ -37,12 +37,14 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
async signup() {
|
||||
const headers = useRequestHeaders(['cookie'])
|
||||
if (!this.username || !this.password) return;
|
||||
const user = await $fetch('/api/login', {
|
||||
method: 'post', body: {
|
||||
username: this.username,
|
||||
password: this.password
|
||||
}
|
||||
},
|
||||
headers
|
||||
}) as { userId: string; token: string; user: SafeUser; }
|
||||
|
||||
const userId = useCookie('userId')
|
||||
|
||||
@@ -48,13 +48,15 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
async signup() {
|
||||
const headers = useRequestHeaders(['cookie']) as Record<string, string>
|
||||
if (!this.username || !this.password || !this.email) return;
|
||||
const user = await $fetch('/api/signup', {
|
||||
method: 'post', body: {
|
||||
username: this.username,
|
||||
email: this.email,
|
||||
password: this.password
|
||||
}
|
||||
},
|
||||
headers
|
||||
}) as { userId: string; token: string; user: SafeUser; }
|
||||
|
||||
const userId = useCookie('userId')
|
||||
|
||||
@@ -4,6 +4,7 @@ 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 view a channel.'
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ const prisma = new PrismaClient()
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
if (!event.context.user.authenticated) {
|
||||
// event.node.res.statusCode = 401;
|
||||
event.node.res.statusCode = 401;
|
||||
return {
|
||||
message: "Unauthenticated"
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ 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 view a channel.'
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ 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 view a channel.'
|
||||
}
|
||||
|
||||
@@ -3,8 +3,11 @@ import { PrismaClient } from '@prisma/client'
|
||||
const prisma = new PrismaClient()
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
if (!event.context.user.authenticated) return {
|
||||
message: 'You must be logged in to view a channel.'
|
||||
if (!event.context.user.authenticated) {
|
||||
event.node.res.statusCode = 401;
|
||||
return {
|
||||
message: 'You must be logged in to view a channel.'
|
||||
}
|
||||
}
|
||||
|
||||
if (!event.context.params.id) {
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import { IServer } from '~/types'
|
||||
import { IInviteCode, IServer } from '~/types'
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
const prisma = new PrismaClient()
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
if (!event.context.user.authenticated) return {
|
||||
message: 'You must be logged in to view a channel.'
|
||||
if (!event.context.user.authenticated) {
|
||||
event.node.res.statusCode = 401;
|
||||
return {
|
||||
message: 'You must be logged in to view a channel.'
|
||||
}
|
||||
}
|
||||
|
||||
const { inviteId } = await readBody(event);
|
||||
@@ -20,10 +23,24 @@ export default defineEventHandler(async (event) => {
|
||||
where: {
|
||||
id: inviteId
|
||||
},
|
||||
include: {
|
||||
server: true
|
||||
select: {
|
||||
id: true,
|
||||
server: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
participants: {
|
||||
select: {
|
||||
id: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
expires: true,
|
||||
expiryDate: true,
|
||||
maxUses: true
|
||||
}
|
||||
})
|
||||
}) as IInviteCode | null;
|
||||
|
||||
if (!invite) {
|
||||
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
|
||||
|
||||
const server = await prisma.server.update({
|
||||
@@ -50,7 +76,7 @@ export default defineEventHandler(async (event) => {
|
||||
channels: true,
|
||||
roles: true
|
||||
}
|
||||
}) as IServer
|
||||
}) as unknown as IServer
|
||||
|
||||
if (!server) {
|
||||
event.node.res.statusCode = 404;
|
||||
|
||||
@@ -1,65 +1,79 @@
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
import { IServer, IUser } from '~/types'
|
||||
import { IChannel, IServer, IUser } from '~/types'
|
||||
const prisma = new PrismaClient()
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
if (!event.context.user.authenticated) {
|
||||
// event.node.res.statusCode = 401;
|
||||
event.node.res.statusCode = 401;
|
||||
return {
|
||||
message: "Unauthenticated"
|
||||
}
|
||||
}
|
||||
|
||||
const { servers, channels } = await prisma.user.findFirst({
|
||||
const servers = await prisma.server.findMany({
|
||||
where: {
|
||||
id: event.context.user.id
|
||||
participants: {
|
||||
some: {
|
||||
id: event.context.user.id
|
||||
}
|
||||
}
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
channels: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
messages: false,
|
||||
DM: true,
|
||||
dmParticipants: true
|
||||
name: true
|
||||
}
|
||||
},
|
||||
servers: {
|
||||
participants: {
|
||||
select: {
|
||||
id: true,
|
||||
username: true
|
||||
}
|
||||
},
|
||||
roles: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
channels: {
|
||||
administrator: true,
|
||||
owner: true,
|
||||
users: {
|
||||
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
|
||||
}
|
||||
}
|
||||
id: true
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}) 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 {
|
||||
servers, channels
|
||||
servers, dms
|
||||
}
|
||||
})
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Ref } from "vue";
|
||||
import { SafeUser, IServer, IChannel } from "../types";
|
||||
|
||||
export const useGlobalStore = defineStore('global', {
|
||||
state: () => ({
|
||||
activeServer: {} as IServer,
|
||||
activeServer: {} as IServer | IChannel,
|
||||
activeServerType: '' as "dms" | "servers" | undefined,
|
||||
user: {} as SafeUser,
|
||||
dms: [] as IChannel[],
|
||||
servers: [] as IServer[]
|
||||
@@ -17,20 +17,38 @@ export const useGlobalStore = defineStore('global', {
|
||||
this.servers.push(server)
|
||||
},
|
||||
addDM(dmChannel: IChannel) {
|
||||
if (!this.channels || this.channels.find((e) => e.id === dmChannel.id)) return;
|
||||
this.channels.push(dmChannel)
|
||||
if (!this.dms || this.dms.find((e) => e.id === dmChannel.id)) return;
|
||||
this.dms.push(dmChannel)
|
||||
},
|
||||
setActive(type: string, serverId: string) {
|
||||
if (serverId === '@me') {
|
||||
this.activeServer = {} as IServer
|
||||
setServers(servers: Array<IServer>) {
|
||||
this.servers = servers
|
||||
},
|
||||
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;
|
||||
}
|
||||
console.log(this.activeServer)
|
||||
|
||||
this.activeServerType = type
|
||||
|
||||
const searchableArray: IChannel[] | IServer[] | undefined = this[type]
|
||||
if (!searchableArray) return;
|
||||
this.activeServer = searchableArray.find((e: IServer | IChannel) => e.id === serverId)
|
||||
console.log(this.activeServer, searchableArray.find((e: IServer | IChannel) => e.id === serverId))
|
||||
let activeServerIndex: number;
|
||||
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 {
|
||||
id: string;
|
||||
name: string;
|
||||
channels?: Array<IChannel>;
|
||||
channels: Array<IChannel>;
|
||||
participants: Array<SafeUser>;
|
||||
roles?: Array<IRole>;
|
||||
roles: Array<IRole>;
|
||||
inviteCode?: Array<IInviteCode>;
|
||||
}
|
||||
|
||||
export interface IChannel {
|
||||
id: string;
|
||||
name: string;
|
||||
server?: IServer;
|
||||
server: IServer;
|
||||
messages?: Array<IMessage>
|
||||
DM: boolean;
|
||||
dmParticipants?: Array<SafeUser>;
|
||||
|
||||
Reference in New Issue
Block a user