stream day 3
This commit is contained in:
18
App.vue
18
App.vue
@@ -2,9 +2,12 @@
|
|||||||
<div>
|
<div>
|
||||||
<NuxtLayout>
|
<NuxtLayout>
|
||||||
<div class="flex h-screen max-h-screen">
|
<div class="flex h-screen max-h-screen">
|
||||||
<Nav />
|
<Nav :servers="servers" />
|
||||||
<Sidebar />
|
<Sidebar :server="activeServer"
|
||||||
<NuxtPage />
|
:user="user" />
|
||||||
|
<div class="w-full h-full">
|
||||||
|
<NuxtPage :user="user" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</NuxtLayout>
|
</NuxtLayout>
|
||||||
</div>
|
</div>
|
||||||
@@ -12,12 +15,19 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { useUserStore } from '~/stores/user'
|
import { useUserStore } from '~/stores/user'
|
||||||
|
import { useServerStore } from './stores/servers'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
servers: useServerStore().servers,
|
||||||
|
activeServer: storeToRefs(useServerStore()).activeServer,
|
||||||
|
user: useUserStore().user
|
||||||
|
}
|
||||||
|
},
|
||||||
async setup() {
|
async setup() {
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const sessionToken = useCookie('sessionToken')
|
const sessionToken = useCookie('sessionToken')
|
||||||
console.log(sessionToken.value)
|
|
||||||
if (userStore.user.id === undefined && sessionToken.value) {
|
if (userStore.user.id === undefined && sessionToken.value) {
|
||||||
const user = await $fetch('/api/getCurrentUser')
|
const user = await $fetch('/api/getCurrentUser')
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<nav class="p-4 bg-zinc-800 grid grid-cols-1 grid-rows-[56px_1fr_56px] h-screen text-white relative">
|
<nav class="p-4 bg-[hsl(216,calc(1*7.2%),13.5%)] grid grid-cols-1 grid-rows-[56px_1fr_56px] h-screen min-w-[88px] text-white relative">
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
@click="openServer('@me')"
|
@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">
|
||||||
<svg width="32"
|
<svg width="32"
|
||||||
height="32"
|
height="32"
|
||||||
@@ -16,10 +16,10 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="overflow-y-scroll my-2 flex gap-2">
|
<div class="overflow-y-scroll my-2 flex gap-y-2 flex-col">
|
||||||
<div v-for="server in servers"
|
<div v-for="server in servers"
|
||||||
:key="server.id"
|
:key="server.id"
|
||||||
@click="openServer(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]">
|
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]">
|
||||||
<svg width="32"
|
<svg width="32"
|
||||||
height="32"
|
height="32"
|
||||||
@@ -89,7 +89,6 @@ import { useServerStore } from '~/stores/servers'
|
|||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
servers: useServerStore().servers,
|
|
||||||
createServerModelOpen: false,
|
createServerModelOpen: false,
|
||||||
serverName: ''
|
serverName: ''
|
||||||
}
|
}
|
||||||
@@ -102,15 +101,12 @@ export default {
|
|||||||
this.serverName = '';
|
this.serverName = '';
|
||||||
serverStore.addServer(server)
|
serverStore.addServer(server)
|
||||||
},
|
},
|
||||||
openServer(id: string): void {
|
openServer(id: string, type: string): void {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
useServerStore().servers.find((e: unknown, i: number) => {
|
useServerStore().setActive(type, id)
|
||||||
if (e.id === id) {
|
|
||||||
useServerStore().activeServer = i
|
|
||||||
}
|
|
||||||
})
|
|
||||||
router.push({ path: `/channel/${id}` });
|
router.push({ path: `/channel/${id}` });
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
props: ['servers']
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex bg-zinc-700 w-60 h-screen shadow-sm text-white select-none">
|
<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="w-full"
|
<div class="w-full"
|
||||||
v-if="server">
|
v-if="server">
|
||||||
<div class="flex p-4 border-b border-zinc-600/80">
|
<div class="flex p-4 border-b border-zinc-600/80">
|
||||||
@@ -22,20 +22,6 @@
|
|||||||
d="M5 9h14M5 15h14M11 4L7 20M17 4l-4 16" />
|
d="M5 9h14M5 15h14M11 4L7 20M17 4l-4 16" />
|
||||||
</svg> {{ channel.name }}
|
</svg> {{ channel.name }}
|
||||||
</div>
|
</div>
|
||||||
<div class="flex text-center hover:bg-zinc-600/70 px-2 py-1.5 w-full transition-colors rounded drop-shadow-sm"
|
|
||||||
v-for="channel in server.channels"
|
|
||||||
:key="channel.id">
|
|
||||||
<svg width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24">
|
|
||||||
<path fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M5 9h14M5 15h14M11 4L7 20M17 4l-4 16" />
|
|
||||||
</svg> {{ channel.name }}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -43,22 +29,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { useUserStore } from '~/stores/user'
|
|
||||||
import { useServerStore } from '~/stores/servers'
|
import { useServerStore } from '~/stores/servers'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
user: useUserStore().user,
|
|
||||||
server: useServerStore().servers[useServerStore().activeServer]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
mounted() {
|
||||||
const route = useRoute()
|
console.log(useServerStore().currentServer, useServerStore().servers)
|
||||||
|
},
|
||||||
if (route.path.includes('@me')) {
|
props: ['server', 'user']
|
||||||
this.server = useServerStore().dms[useServerStore().activeServer]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -28,6 +28,7 @@ export default {
|
|||||||
autoImports: [
|
autoImports: [
|
||||||
// automatically imports `defineStore`
|
// automatically imports `defineStore`
|
||||||
'defineStore', // import { defineStore } from 'pinia'
|
'defineStore', // import { defineStore } from 'pinia'
|
||||||
|
'storeToRefs',
|
||||||
// automatically imports `defineStore` as `definePiniaStore`
|
// automatically imports `defineStore` as `definePiniaStore`
|
||||||
['defineStore', 'definePiniaStore'], // import { defineStore as definePiniaStore } from 'pinia'
|
['defineStore', 'definePiniaStore'], // import { defineStore as definePiniaStore } from 'pinia'
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,16 +1,105 @@
|
|||||||
<template>
|
<template>
|
||||||
hello world
|
<div class="w-full h-full bg-[hsl(220,calc(1*7.7%),22.9%)] relative text-white">
|
||||||
|
<div class="w-full h-[calc(100%-60px)] overflow-y-scroll pb-1"
|
||||||
|
id="conversation-pane">
|
||||||
|
<div>
|
||||||
|
<div v-if="conversation.length === 0">
|
||||||
|
<p>No messages yet</p>
|
||||||
|
</div>
|
||||||
|
<div v-else
|
||||||
|
v-for="conversations in conversation">
|
||||||
|
<div v-if="conversations.userId == user.id"
|
||||||
|
class="message-container"
|
||||||
|
id="messages-container">
|
||||||
|
<div class="message-sender">
|
||||||
|
<div class="message-sender-text">
|
||||||
|
<p class="message-sender-you">You</p>
|
||||||
|
<p>{{ conversations.body }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else
|
||||||
|
class="message-container">
|
||||||
|
<div class="message-sender">
|
||||||
|
<div class="message-sender-text">
|
||||||
|
<p class="message-sender-you">{{ conversations.userId }}</p>
|
||||||
|
<p>{{ conversations.body }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="conversation-input w-full">
|
||||||
|
<form @submit.prevent="sendMessage"
|
||||||
|
class="relative px-4 w-[calc(100%-12rem)]">
|
||||||
|
<input type="text"
|
||||||
|
class="py-2 px-4 rounded-md bg-[hsl(218,calc(1*7.9%),27.3%)]"
|
||||||
|
id="message"
|
||||||
|
v-model="messageContent"
|
||||||
|
placeholder="Send a Message..." />
|
||||||
|
<input type="submit"
|
||||||
|
class=""
|
||||||
|
value="Send" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { useServerStore } from '~/stores/servers'
|
import { useServerStore } from '~/stores/servers'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
conversation: [],
|
||||||
|
messageContent: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
async setup() {
|
async setup() {
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
const { server } = await $fetch(`/api/channel/${route.params.dmId}`)
|
const { server } = await $fetch(`/api/channel/${route.params.dmId}`)
|
||||||
if (!useServerStore().dms.includes(server)) useServerStore().addDM(server);
|
if (!server) return;
|
||||||
}
|
useServerStore().addDM(server);
|
||||||
|
const channelId = server.channels[0].id
|
||||||
|
await useServerStore().setActive('dms', server.id);
|
||||||
|
return {
|
||||||
|
server,
|
||||||
|
channelId
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async mounted() {
|
||||||
|
const { channel } = await $fetch(`/api/channel/messages/${this.channelId}`)
|
||||||
|
this.conversation = channel.messages
|
||||||
|
},
|
||||||
|
async updated() {
|
||||||
|
if (!useServerStore().activeServer == this.server) await useServerStore().setActive('dms', this.server.id)
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async sendMessage() {
|
||||||
|
const route = useRoute()
|
||||||
|
if (!this.messageContent) return;
|
||||||
|
|
||||||
|
const { message } = await $fetch(`/api/channel/sendMessage`, { method: 'post', body: { body: this.messageContent, channelId: this.channelId } })
|
||||||
|
|
||||||
|
this.conversation.push(message)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: ['user']
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.conversation-input {
|
||||||
|
display: flex;
|
||||||
|
position: fixed;
|
||||||
|
flex-direction: row;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
height: 60px;
|
||||||
|
border-top: #ffffff 1px solid;
|
||||||
|
background-color: #3C3C3C;
|
||||||
|
bottom: calc(0px - 0.5rem);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,12 +1,25 @@
|
|||||||
<template>
|
<template>
|
||||||
hello world
|
{{ $route.params.id }}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup async>
|
<script lang="ts">
|
||||||
import { useServerStore } from '~/stores/servers'
|
import { useServerStore } from '~/stores/servers'
|
||||||
|
|
||||||
const route = useRoute()
|
export default {
|
||||||
|
async setup() {
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
const { server } = await $fetch(`/api/channel/${route.params.id}`)
|
const { server } = await $fetch(`/api/channel/${route.params.id}`)
|
||||||
if (!useServerStore().servers.includes(server)) useServerStore().addServer(server);
|
if (!server) return;
|
||||||
|
useServerStore().addServer(server);
|
||||||
|
await useServerStore().setActive('servers', server.id)
|
||||||
|
return {
|
||||||
|
server
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async updated() {
|
||||||
|
if (!this.server) return;
|
||||||
|
if (!await useServerStore().activeServer == this.server.id) await useServerStore().setActive('servers', this.server.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -12,10 +12,10 @@ model User {
|
|||||||
email String @unique
|
email String @unique
|
||||||
username String @unique
|
username String @unique
|
||||||
passwordhash String
|
passwordhash String
|
||||||
Server Server[]
|
server Server[]
|
||||||
serverId String?
|
serverId String?
|
||||||
Messages Message[]
|
messages Message[]
|
||||||
Session Session[]
|
session Session[]
|
||||||
}
|
}
|
||||||
|
|
||||||
model Server {
|
model Server {
|
||||||
@@ -28,9 +28,9 @@ model Server {
|
|||||||
model Channel {
|
model Channel {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
name String
|
name String
|
||||||
Server Server? @relation(fields: [serverId], references: [id])
|
server Server? @relation(fields: [serverId], references: [id])
|
||||||
serverId String?
|
serverId String?
|
||||||
Message Message[]
|
messages Message[]
|
||||||
}
|
}
|
||||||
|
|
||||||
model Message {
|
model Message {
|
||||||
|
|||||||
35
server/api/channel/messages/[id].get.ts
Normal file
35
server/api/channel/messages/[id].get.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
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.params.id) {
|
||||||
|
event.node.res.statusCode = 400;
|
||||||
|
return {
|
||||||
|
message: 'A channelId is required'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const channel = await prisma.channel.findFirst({
|
||||||
|
where: {
|
||||||
|
id: event.context.params.id
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
messages: true,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!channel) {
|
||||||
|
event.node.res.statusCode = 404;
|
||||||
|
return {
|
||||||
|
message: `Channel with id "${event.context.params.id}" not found`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
channel
|
||||||
|
}
|
||||||
|
})
|
||||||
71
server/api/channel/sendMessage.post.ts
Normal file
71
server/api/channel/sendMessage.post.ts
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
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: 'You must be logged in to send a message.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const { body, channelId } = await readBody(event)
|
||||||
|
|
||||||
|
if (!body || !channelId) {
|
||||||
|
event.node.res.statusCode = 400;
|
||||||
|
return {
|
||||||
|
message: 'A body or channelId is required to send a message.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const channel = await prisma.channel.findFirst({
|
||||||
|
where: {
|
||||||
|
id: channelId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const server = await prisma.server.findFirst({
|
||||||
|
where: {
|
||||||
|
id: channel.serverId
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
participants: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const userInServer = server.participants.filter((e) => e.id === event.context.user.id)
|
||||||
|
|
||||||
|
if (!userInServer.length > 0) {
|
||||||
|
event.node.res.statusCode = 401;
|
||||||
|
return {
|
||||||
|
message: 'You must be in the server to send a message.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!server) {
|
||||||
|
event.node.res.statusCode = 404;
|
||||||
|
return {
|
||||||
|
message: 'Server not found'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = await prisma.message.create({
|
||||||
|
data: {
|
||||||
|
body,
|
||||||
|
creator: {
|
||||||
|
connect: {
|
||||||
|
id: event.context.user.id
|
||||||
|
}
|
||||||
|
},
|
||||||
|
channel: {
|
||||||
|
connect: {
|
||||||
|
id: channelId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
message
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -4,8 +4,6 @@ const prisma = new PrismaClient()
|
|||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
const cookies = parseCookies(event)
|
const cookies = parseCookies(event)
|
||||||
|
|
||||||
console.log(cookies.sessionToken)
|
|
||||||
|
|
||||||
if (!cookies.sessionToken) {
|
if (!cookies.sessionToken) {
|
||||||
event.context.user = { authenticated: false }
|
event.context.user = { authenticated: false }
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -2,14 +2,19 @@ export const useServerStore = defineStore('server', {
|
|||||||
state: () => ({
|
state: () => ({
|
||||||
servers: [],
|
servers: [],
|
||||||
dms: [],
|
dms: [],
|
||||||
activeServer: undefined
|
activeServer: {}
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
addServer(server) {
|
addServer(server) {
|
||||||
|
if (this.servers.includes(server)) return;
|
||||||
this.servers.push(server)
|
this.servers.push(server)
|
||||||
},
|
},
|
||||||
addDM(server) {
|
addDM(server) {
|
||||||
|
if (this.dms.includes(server)) return;
|
||||||
this.dms.push(server)
|
this.dms.push(server)
|
||||||
}
|
},
|
||||||
|
setActive(type, serverId) {
|
||||||
|
this.activeServer = this[type].find((e) => e.id === serverId)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user