stream day 5

This commit is contained in:
Zoe
2023-01-10 19:19:37 -06:00
parent 1cb01289bc
commit 880d1bf375
21 changed files with 315 additions and 158 deletions

37
App.vue
View File

@@ -1,40 +1,5 @@
<template> <template>
<div>
<NuxtLayout> <NuxtLayout>
<div class="flex h-screen max-h-screen"> <NuxtPage />
<Nav :servers="servers" />
<Sidebar :server="activeServer"
:user="user" />
<div class="w-[calc(100vw-88px-240px)] h-full">
<NuxtPage :user="user" />
</div>
</div>
</NuxtLayout> </NuxtLayout>
</div>
</template> </template>
<script lang="ts">
import { useUserStore } from '~/stores/user'
import { useServerStore } from './stores/servers'
export default {
data() {
return {
servers: storeToRefs(useServerStore()).servers,
activeServer: storeToRefs(useServerStore()).activeServer,
user: storeToRefs(useUserStore()).user
}
},
async setup() {
const userStore = useUserStore()
const sessionToken = useCookie('sessionToken')
if (userStore.user.id === undefined && sessionToken.value) {
const user = await $fetch('/api/getCurrentUser')
if (!user) return;
userStore.setUser(user)
}
}
}
</script>

View File

@@ -7,13 +7,13 @@
<p>No messages yet</p> <p>No messages yet</p>
</div> </div>
<div v-else <div v-else
v-for="conversations in conversation"> v-for="message in conversation">
<div class="message-container"> <div class="message-container">
<div> <div>
<div class="message-sender-text"> <div class="message-sender-text">
<p :class="(conversations.userId == user.id) ? 'message-sender-you' : 'message-sender'"> <p :class="(message.userId == user.id) ? 'message-sender-you' : 'message-sender'">
{{ conversations.userId }}</p> {{ message.userId }}</p>
<p class="break-words max-w-full">{{ conversations.body }}</p> <p class="break-words max-w-full">{{ message.body }}</p>
</div> </div>
</div> </div>
</div> </div>
@@ -52,29 +52,21 @@
</div> </div>
</template> </template>
<script async setup lang="ts">
</script>
<script lang="ts"> <script lang="ts">
import { useServerStore } from '~/stores/servers' import { useGlobalStore } from '~/stores/store';
import { io } from 'socket.io-client' import { io } from 'socket.io-client'
export default { export default {
props: ['server'],
data() { data() {
return { return {
messageContent: '' user: useGlobalStore().user,
} messageContent: '',
}, conversation: this.server.messages
async setup() {
const route = useRoute()
const { channel: server } = await $fetch(`/api/channels/${route.params.dmId}`)
if (!server) return;
useServerStore().addDM(server);
await useServerStore().setActive('dms', server.id);
const conversation: Array<Record<string, unknown>> = ref(server.messages)
return {
server,
conversation,
} }
}, },
mounted() { mounted() {
@@ -89,7 +81,7 @@ export default {
socket.on('connect', () => { socket.on('connect', () => {
// listen for messages from the server // listen for messages from the server
socket.on(`message-${route.params.dmId}`, (ev) => { socket.on(`message-${route.params.id}`, (ev) => {
const { message } = ev const { message } = ev
console.log(message.userId, this.user.id, message, this.conversation) console.log(message.userId, this.user.id, message, this.conversation)
if (message.userId == this.user.id) return; if (message.userId == this.user.id) return;
@@ -107,15 +99,12 @@ export default {
}) })
}); });
}, },
async updated() {
if (!useServerStore().activeServer == this.server) await useServerStore().setActive('dms', this.server.id)
},
methods: { methods: {
async sendMessage() { async sendMessage() {
const route = useRoute() const route = useRoute()
if (!this.messageContent) return; if (!this.messageContent) return;
const { message } = await $fetch(`/api/channels/sendMessage`, { method: 'post', body: { body: this.messageContent, channelId: route.params.dmId } }) const { message } = await $fetch(`/api/channels/sendMessage`, { method: 'post', body: { body: this.messageContent, channelId: route.params.id } })
this.conversation.push(message) this.conversation.push(message)
this.messageContent = ''; this.messageContent = '';
@@ -137,7 +126,6 @@ export default {
// console.log('a') // console.log('a')
// } // }
}, },
props: ['user']
} }
</script> </script>

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 servers" :to="'/channel/' + server.id"> <nuxt-link v-for="server in user.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]">
@@ -87,7 +87,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { useServerStore } from '~/stores/servers' import { useGlobalStore } from '~/stores/store'
export default { export default {
data() { data() {
@@ -98,16 +98,16 @@ export default {
}, },
methods: { methods: {
async createServer() { async createServer() {
const serverStore = useServerStore(); const globalStore = useGlobalStore();
const { server } = await $fetch('/api/channels/create', { method: 'post', body: { serverName: this.serverName } }) const { server } = await $fetch('/api/channels/create', { method: 'post', body: { serverName: this.serverName } })
this.createServerModelOpen = false; this.createServerModelOpen = false;
this.serverName = ''; this.serverName = '';
serverStore.addServer(server) globalStore.addServer(server)
}, },
openServer(id: string, type: string): void { openServer(id: string, type: string): void {
useServerStore().setActive(type, id) useGlobalStore().setActive(type, id)
} }
}, },
props: ['servers'] props: ['user']
} }
</script> </script>

View File

@@ -1,9 +1,12 @@
<template> <template>
<div class="flex bg-[hsl(223,calc(1*6.9%),19.8%)] min-w-60 w-60 h-screen shadow-sm text-white select-none"> <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"> <div v-if="!server.id || server.DM == true">
<div> <div>
<nuxt-link v-for="dm in user.channels" :to="'/channel/@me/' + dm.id"> <nuxt-link v-for="dm in user.channels"
<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"> :to="'/channel/@me/' + dm.id">
<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">
{{ (dm.name).split('-').filter((e: string) => e !== user.id)[0] }} {{ (dm.name).split('-').filter((e: string) => e !== user.id)[0] }}
</div> </div>
</nuxt-link> </nuxt-link>
@@ -34,6 +37,32 @@
</div> </div>
</div> </div>
<div>
<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">
<svg xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24">
<g fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2">
<path
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 0 0 2.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 0 0 1.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 0 0-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 0 0-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 0 0-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 0 0-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 0 0 1.066-2.573c-.94-1.543.826-3.31 2.37-2.37c1 .608 2.296.07 2.572-1.065z" />
<circle cx="12"
cy="12"
r="3" />
</g>
</svg>
</span>
</div>
</div>
</div>
</div> </div>
</template> </template>

5
layouts/clean.vue Normal file
View File

@@ -0,0 +1,5 @@
<template>
<div class="flex h-screen max-h-screen text-white">
<slot />
</div>
</template>

35
layouts/default.vue Normal file
View File

@@ -0,0 +1,35 @@
<template>
<div class="flex h-screen max-h-screen text-white">
<Nav :user="user"/>
<Sidebar :server="activeServer"
:user="user" />
<div class="w-[calc(100vw-88px-240px)] h-full">
<slot />
</div>
</div>
</template>
<script lang="ts">
import { useGlobalStore } from '~/stores/store'
export default {
data() {
return {
activeServer: storeToRefs(useGlobalStore()).activeServer,
user: storeToRefs(useGlobalStore()).user
}
},
async setup() {
const userStore = useGlobalStore()
const sessionToken = useCookie('sessionToken')
if (userStore.user.id === undefined && sessionToken.value) {
const user = await $fetch('/api/getCurrentUser')
if (!user) return;
userStore.setUser(user)
}
}
}
</script>

6
middleware/auth.ts Normal file
View File

@@ -0,0 +1,6 @@
export default defineNuxtRouteMiddleware((to, from) => {
// isAuthenticated() is an example method verifying if a user is authenticated
if (!useCookie('sessionToken').value) {
return navigateTo('/login')
}
})

View File

@@ -1,6 +1,7 @@
// https://v3.nuxtjs.org/api/configuration/nuxt.config // https://v3.nuxtjs.org/api/configuration/nuxt.config
export default { export default {
ssr: false,
app: { app: {
head: { head: {
meta: [ meta: [

View File

@@ -0,0 +1,30 @@
<template>
<MessagePane :server="server" />
</template>
<script lang="ts">
import { useGlobalStore } from '~/stores/store'
definePageMeta({
middleware: 'auth'
})
export default {
async setup() {
const route = useRoute()
const { channel: server } = await $fetch(`/api/channels/${route.params.id}`)
if (!server) return;
useGlobalStore().addDM(server);
await useGlobalStore().setActive('dms', server.id);
console.log(server)
return {
server,
}
},
async updated() {
if (!useGlobalStore().activeServer == this.server) await useGlobalStore().setActive('dms', this.server.id)
},
}
</script>

View File

@@ -6,7 +6,11 @@
</template> </template>
<script> <script>
import { useServerStore } from '~/stores/servers' import { useGlobalStore } from '~/stores/store'
definePageMeta({
middleware: 'auth'
})
export default { export default {
data() { data() {
@@ -18,7 +22,7 @@ export default {
async startDM() { async startDM() {
const { server } = await $fetch('/api/channels/createDM', { method: 'post', body: { partnerId: this.userId } }) const { server } = await $fetch('/api/channels/createDM', { method: 'post', body: { partnerId: this.userId } })
useServerStore().addDM(server) useGlobalStore().addDM(server)
useRouter().push({ path: '/channel/@me/' + server.id }) useRouter().push({ path: '/channel/@me/' + server.id })
} }
} }

View File

@@ -1,25 +1,37 @@
<template> <template>
{{ $route.params.id }} <MessagePane :server="server" />
</template> </template>
<script lang="ts"> <script lang="ts">
import { useServerStore } from '~/stores/servers' import { useGlobalStore } from '~/stores/store'
definePageMeta({
middleware: 'auth'
})
export default { export default {
async setup() { async setup() {
const route = useRoute() const route = useRoute()
const { server } = await $fetch(`/api/guilds/${route.params.id}`) const { channel: server } = await $fetch(`/api/channels/${route.params.id}`)
if (!server) return; if (!server) return;
useServerStore().addServer(server);
await useServerStore().setActive('servers', server.id) const realServer = await useGlobalStore().user.servers.find((e) => e.channels.some((el) => el.id == route.params.id ) )
useGlobalStore().addServer(realServer);
await useGlobalStore().setActive('servers', realServer.id)
return { return {
server server
} }
}, },
async updated() { async updated() {
if (!this.server) return; if (!this.server) return;
if (!await useServerStore().activeServer == this.server.id) await useServerStore().setActive('servers', this.server.id)
this.server = await $fetch(`/api/channels/${route.params.id}`);
if (!await useGlobalStore().activeServer == this.server.id) await useGlobalStore().setActive('servers', this.server.id)
} }
} }
</script> </script>

View File

@@ -5,3 +5,9 @@
<nuxt-link to="/signup">Signup</nuxt-link> <nuxt-link to="/signup">Signup</nuxt-link>
</div> </div>
</template> </template>
<script setup>
definePageMeta({
middleware: 'auth'
})
</script>

View File

@@ -1,21 +1,31 @@
<template> <template>
<form class="flex flex-col" <div class="w-screen h-screen bg-[hsl(216,calc(1*7.2%),10%)] relative">
<div class="-translate-y-1/2 -translate-x-1/2 top-1/2 left-1/2 absolute bg-[hsl(216,calc(1*7.2%),16%)] p-4 rounded-md shadow-lg">
<h2 class="text-xl font-semibold text-center">Login</h2>
<form class="flex flex-col gap-y-2 my-2"
@submit.prevent="signup()"> @submit.prevent="signup()">
<input class="border border-zinc-700" <input class="border border-[hsl(218,calc(1*7.9%),23.7%)] px-4 py-2 rounded w-full bg-[hsl(218,calc(1*7.9%),27.3%)] placeholder:text-[hsl(218,calc(1*4.6%),46.9%)] focus:outline-none"
name="username" name="username"
v-model="username" v-model="username"
placeholder="username" /> placeholder="username" />
<input class="border border-zinc-700" <input class="border border-[hsl(218,calc(1*7.9%),23.7%)] px-4 py-2 rounded w-full bg-[hsl(218,calc(1*7.9%),27.3%)] placeholder:text-[hsl(218,calc(1*4.6%),46.9%)] focus:outline-none"
name="password" name="password"
type="password" type="password"
v-model="password" v-model="password"
placeholder="password" /> placeholder="password" />
<input type="submit" /> <input type="submit" class="w-full bg-[#5865F2] py-2 px-4 rounded cursor-pointer" />
</form> </form>
<div class="text-center">Or <nuxt-link class="hover:underline text-blue-500" to="/signup">Signup</nuxt-link></div>
</div>
</div>
</template> </template>
<script> <script>
import { useUserStore } from '~/stores/user' import { useGlobalStore } from '~/stores/store'
definePageMeta({
layout: 'clean'
})
export default { export default {
data() { data() {
@@ -39,7 +49,9 @@ export default {
const token = useCookie('sessionToken') const token = useCookie('sessionToken')
token.value = user.token token.value = user.token
useUserStore().setUser(user) useGlobalStore().setUser(user)
navigateTo('/channel/@me')
} }
} }
} }

View File

@@ -1,25 +1,40 @@
<template> <template>
<form class="flex flex-col" <div class="w-screen h-screen bg-[hsl(216,calc(1*7.2%),10%)] relative">
<div
class="-translate-y-1/2 -translate-x-1/2 top-1/2 left-1/2 absolute bg-[hsl(216,calc(1*7.2%),16%)] p-4 rounded-md shadow-lg">
<h2 class="text-xl font-semibold text-center">Sign up</h2>
<form class="flex flex-col gap-y-2 my-2"
@submit.prevent="signup()"> @submit.prevent="signup()">
<input class="border border-zinc-700" <input
class="border border-[hsl(218,calc(1*7.9%),23.7%)] px-4 py-2 rounded w-full bg-[hsl(218,calc(1*7.9%),27.3%)] placeholder:text-[hsl(218,calc(1*4.6%),46.9%)] focus:outline-none"
name="username" name="username"
v-model="username" v-model="username"
placeholder="username" /> placeholder="username" />
<input class="border border-zinc-700" <input
class="border border-[hsl(218,calc(1*7.9%),23.7%)] px-4 py-2 rounded w-full bg-[hsl(218,calc(1*7.9%),27.3%)] placeholder:text-[hsl(218,calc(1*4.6%),46.9%)] focus:outline-none"
name="email" name="email"
v-model="email" v-model="email"
placeholder="email" /> placeholder="email" />
<input class="border border-zinc-700" <input
class="border border-[hsl(218,calc(1*7.9%),23.7%)] px-4 py-2 rounded w-full bg-[hsl(218,calc(1*7.9%),27.3%)] placeholder:text-[hsl(218,calc(1*4.6%),46.9%)] focus:outline-none"
name="password" name="password"
type="password" type="password"
v-model="password" v-model="password"
placeholder="password" /> placeholder="password" />
<input type="submit" /> <input type="submit" class="w-full bg-[#5865F2] py-2 px-4 rounded cursor-pointer" />
</form> </form>
<div class="text-center">Or <nuxt-link class="hover:underline text-blue-500"
to="/login">Login</nuxt-link></div>
</div>
</div>
</template> </template>
<script> <script>
import { useUserStore } from '~/stores/user' import { useGlobalStore } from '~/stores/store'
definePageMeta({
layout: 'clean'
})
export default { export default {
data() { data() {
@@ -45,7 +60,9 @@ export default {
const token = useCookie('sessionToken') const token = useCookie('sessionToken')
token.value = user.token token.value = user.token
useUserStore().setUser(user) useGlobalStore().setUser(user)
navigateTo('/channel/@me')
} }
} }
} }

View File

@@ -12,7 +12,7 @@ model User {
email String @unique email String @unique
username String @unique username String @unique
passwordhash String passwordhash String
server Server[] servers Server[]
serverId String? serverId String?
messages Message[] messages Message[]
session Session[] session Session[]

View File

@@ -32,17 +32,16 @@ export default defineEventHandler(async (event) => {
} }
} }
if (channel.id && !channel.DM) { if (channel.serverId && !channel.DM) {
const server = await prisma.server.findFirst({ const server = await prisma.server.findFirst({
where: { where: {
id: channel.id id: channel.serverId
}, },
include: { include: {
participants: true participants: true
} }
}) })
const userInServer = server.participants.filter((e) => e.id === event.context.user.id) const userInServer = server.participants.filter((e) => e.id === event.context.user.id)
if (!userInServer) { if (!userInServer) {

View File

@@ -13,8 +13,37 @@ export default defineEventHandler(async (event) => {
where: { where: {
id: event.context.user.id id: event.context.user.id
}, },
include: { select: {
channels: true id: 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
}
}
},
},
} }
}) })

View File

@@ -0,0 +1,24 @@
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
export default defineEventHandler(async (event) => {
if (!event.context.user.authenticated) {
// event.node.res.statusCode = 401;
return {
message: "Unauthenticated"
}
}
const servers = await prisma.user.findFirst({
where: {
id: event.context.user.id
},
include: {
channels: true
}
})
user.passwordhash = undefined;
return user
})

View File

@@ -1,24 +0,0 @@
export const useServerStore = defineStore('server', {
state: () => ({
servers: [],
dms: [],
activeServer: {}
}),
actions: {
addServer(server) {
if (this.servers.find((e) => e.id === server.id)) return;
this.servers.push(server)
},
addDM(server) {
if (this.dms.includes(server)) return;
this.dms.push(server)
},
setActive(type, serverId) {
if (serverId === '@me') {
this.activeServer = {}
return;
}
this.activeServer = this[type].find((e) => e.id === serverId)
},
},
})

29
stores/store.ts Normal file
View File

@@ -0,0 +1,29 @@
export const useGlobalStore = defineStore('global', {
state: () => ({
activeServer: {},
user: {}
}),
actions: {
setUser(user) {
this.user = user;
},
addServer(server) {
if (this.user.servers.find((e) => e.id === server.id)) return;
this.user.servers.push(server)
},
addDM(dmChannel) {
if (this.user.channels.includes(dmChannel)) return;
this.user.channels.push(dmChannel)
},
setActive(type, serverId) {
if (serverId === '@me') {
this.activeServer = {}
return;
}
type = (type === 'dm') ? 'channels' : 'servers'
this.activeServer = this["user"][type].find((e) => e.id === serverId)
},
},
})

View File

@@ -1,10 +0,0 @@
export const useUserStore = defineStore('user', {
state: () => ({
user: {}
}),
actions: {
setUser(user) {
this.user = user;
}
},
})