stream day 6
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
<div class="w-full h-[calc(100%-60px)] overflow-y-scroll pb-1"
|
||||
id="conversation-pane">
|
||||
<div>
|
||||
<div v-if="!conversation">
|
||||
<div v-if="conversation.length === 0">
|
||||
<p>No messages yet</p>
|
||||
</div>
|
||||
<div v-else
|
||||
@@ -11,10 +11,23 @@
|
||||
<div class="message-container">
|
||||
<div>
|
||||
<div class="message-sender-text">
|
||||
<p :class="(message.userId == user.id) ? 'message-sender-you' : 'message-sender'">
|
||||
{{ message.userId }}</p>
|
||||
<p class="mb-1.5 font-semibold">
|
||||
{{ message.creator.username }}
|
||||
</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">You've been invited</p>
|
||||
<span class="text-xl font-bold capitalize">{{ invite.server.name }}</span>
|
||||
<div class="flex w-full justify-end">
|
||||
<button @click="joinServer(invite)"
|
||||
class="font-semibold rounded px-4 py-2 bg-green-700 hover:bg-green-600 transition-colors">Join</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -22,7 +35,7 @@
|
||||
</div>
|
||||
<div class="conversation-input w-[calc(100vw-88px-240px)]">
|
||||
<form @submit.prevent="sendMessage"
|
||||
@keydown.enter="sendMessage"
|
||||
@keydown.enter.exact.prevent="sendMessage"
|
||||
class="relative px-4 w-full">
|
||||
<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">
|
||||
@@ -55,52 +68,88 @@
|
||||
<script lang="ts">
|
||||
import { useGlobalStore } from '~/stores/store';
|
||||
import { io } from 'socket.io-client'
|
||||
import { IChannel, IInviteCode, IMessage } from '~/types';
|
||||
|
||||
export default {
|
||||
props: ['server'],
|
||||
data() {
|
||||
return {
|
||||
user: useGlobalStore().user,
|
||||
user: storeToRefs(useGlobalStore()).user,
|
||||
messageContent: '',
|
||||
conversation: this.server.messages
|
||||
conversation: this.server.messages as IMessage[],
|
||||
canSendNotifications: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
const route = useRoute()
|
||||
const socket = io();
|
||||
|
||||
Notification.requestPermission().then((result) => {
|
||||
const permission = (result === 'granted') ? true : false
|
||||
this.canSendNotifications = permission
|
||||
});
|
||||
|
||||
const conversationDiv = document.getElementById('conversation-pane');
|
||||
if (!conversationDiv) throw new Error('wtf');
|
||||
setTimeout(() => {
|
||||
conversationDiv.scrollTop = conversationDiv.scrollHeight;
|
||||
})
|
||||
if (!conversationDiv) throw new Error('conversation div not found')
|
||||
this.scrollToBottom()
|
||||
|
||||
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;
|
||||
socket.on(`message-${route.params.id}`, (ev) => {
|
||||
const { message } = ev
|
||||
if (message.creator.id === this.user.id) return;
|
||||
if (!document.hasFocus()) {
|
||||
new Notification(`Message from @${message.creator.username}`, { body: message.body, tag: message.serverId });
|
||||
}
|
||||
|
||||
this.conversation.push(message)
|
||||
this.conversation.push(message)
|
||||
|
||||
const lastElementChild = conversationDiv.children[0]?.lastElementChild
|
||||
if (!lastElementChild) return;
|
||||
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;
|
||||
})
|
||||
setTimeout(() => {
|
||||
if (conversationDiv.scrollTop + 11.2 < (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;
|
||||
// })
|
||||
// })
|
||||
// });
|
||||
// },
|
||||
methods: {
|
||||
async sendMessage() {
|
||||
const route = useRoute()
|
||||
if (!this.messageContent) return;
|
||||
|
||||
const { message } = await $fetch(`/api/channels/sendMessage`, { method: 'post', body: { body: this.messageContent, channelId: route.params.id } })
|
||||
const message: IChannel = await $fetch(`/api/channels/sendMessage`, { method: 'post', body: { body: this.messageContent, channelId: route.params.id } })
|
||||
|
||||
if (!message) return;
|
||||
if (this.conversation.includes(message)) return;
|
||||
|
||||
this.conversation.push(message)
|
||||
this.messageContent = '';
|
||||
@@ -110,6 +159,18 @@ 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.user.servers?.push(server)
|
||||
},
|
||||
scrollToBottom() {
|
||||
const conversationDiv = document.getElementById('conversation-pane');
|
||||
if (!conversationDiv) throw new Error('wtf');
|
||||
setTimeout(() => {
|
||||
conversationDiv.scrollTop = conversationDiv.scrollHeight;
|
||||
})
|
||||
}
|
||||
// resizeTextarea() {
|
||||
// const textArea = document.getElementById('messageBox')
|
||||
// const textBox = document.getElementById('textBox')
|
||||
|
||||
@@ -15,25 +15,66 @@
|
||||
<div class="w-full"
|
||||
v-else>
|
||||
<div class="flex p-4 border-b border-zinc-600/80">
|
||||
<h4 class="text-lg font-semibold w-fit ">
|
||||
{{ server.name }}
|
||||
<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 hover:backdrop-brightness-110 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">
|
||||
<div class="flex text-center hover:bg-zinc-600/70 px-2 py-1.5 w-full transition-colors rounded drop-shadow-sm"
|
||||
<button @click="createInvite"
|
||||
v-if="userIsOwnerOrAdmin">make invite</button>
|
||||
<button
|
||||
class="flex text-center hover:bg-zinc-600/70 px-2 py-1.5 w-full transition-colors rounded drop-shadow-sm gap-1/5 cursor-pointer"
|
||||
v-for="channel in server.channels"
|
||||
@click="openChannel(channel.id)"
|
||||
: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>
|
||||
<span>
|
||||
<svg class="text-zinc-300 my-auto"
|
||||
width="20"
|
||||
height="20"
|
||||
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>
|
||||
</span>
|
||||
<span>{{ channel.name }}</span>
|
||||
</button>
|
||||
<button v-if="userIsOwnerOrAdmin"
|
||||
@click="openCreateChannelModel"
|
||||
class="flex text-center hover:bg-zinc-600/70 px-2 py-1.5 w-full transition-colors rounded drop-shadow-sm cursor-pointer">
|
||||
<span>
|
||||
<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="M12 5v14m-7-7h14" />
|
||||
</svg>
|
||||
</span>
|
||||
<span>Add channel</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -64,10 +105,72 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="createChannelModelOpen"
|
||||
class="absolute z-10 top-0 bottom-0 left-0 right-0">
|
||||
<div class="bg-zinc-900/80 w-screen h-screen"
|
||||
@click="createChannelModelOpen = 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">
|
||||
Create a channel:
|
||||
</h2>
|
||||
<div>
|
||||
<form @submit.prevent="createChannel"
|
||||
class="w-3/5">
|
||||
<input v-model="channelName"
|
||||
type="text"
|
||||
class="py-2 px-3 rounded-md mb-2 bg-zinc-700 shadow-md border border-zinc-700/80"
|
||||
placeholder="Channel name" />
|
||||
<input type="submit"
|
||||
class="py-2 px-3 rounded-md bg-zinc-700 shadow-md border border-zinc-700/80" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { IRole, IServer } from '~/types';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
createChannelModelOpen: false,
|
||||
channelName: '',
|
||||
userIsOwnerOrAdmin: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
setTimeout(() => {
|
||||
if (!this.server.roles) {
|
||||
console.log(this.server)
|
||||
throw new Error('server must be null');
|
||||
}
|
||||
this.userIsOwnerOrAdmin = this.server.roles.find((e: IRole) => e.users.some((el) => el.id === this.user.id)).owner ||
|
||||
this.server.roles.find((e: IRole) => e.users.some((el) => el.id === this.user.id)).administrator
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
openCreateChannelModel() {
|
||||
this.createChannelModelOpen = true;
|
||||
},
|
||||
async createChannel() {
|
||||
const channel = await $fetch(`/api/guilds/${this.server.id}/addChannel`, { method: 'POST', body: { channelName: this.channelName } })
|
||||
|
||||
this.server.channels.push(channel)
|
||||
this.createChannelModelOpen = false;
|
||||
},
|
||||
openChannel(id: string) {
|
||||
const router = useRouter()
|
||||
|
||||
router.push({ params: { id } })
|
||||
},
|
||||
async createInvite() {
|
||||
const inviteCode = await $fetch(`/api/guilds/${this.server.id}/createInvite`, { method: 'POST' })
|
||||
},
|
||||
},
|
||||
props: ['server', 'user']
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user