a bunch of bug fixes and improvements
This commit is contained in:
@@ -20,7 +20,8 @@
|
||||
"storeToRefs": true,
|
||||
"useNuxtApp": true,
|
||||
"NodeJS": true,
|
||||
"useHeadSafe": true
|
||||
"useHeadSafe": true,
|
||||
"defineEmits": true
|
||||
},
|
||||
"parser": "vue-eslint-parser",
|
||||
"parserOptions": {
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
import emojiJson from '~/assets/json/emoji.json';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
opened: Boolean,
|
||||
},
|
||||
emits: ['picked-emoji'],
|
||||
data() {
|
||||
return {
|
||||
@@ -22,7 +19,9 @@ export default {
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
emojiStyles(emojiShortName: string, width: number) {
|
||||
emojiStyles(emojiShortName: string | undefined, width: number) {
|
||||
if (!emojiShortName) return;
|
||||
|
||||
const emojis = emojiJson;
|
||||
const emoji = emojis.find((e) => e.short_names[0] === emojiShortName);
|
||||
if (!emoji) return;
|
||||
@@ -38,17 +37,17 @@ export default {
|
||||
};
|
||||
},
|
||||
scrollTo(categoryName: string) {
|
||||
const emojiPane = document.getElementById('emojiPane');
|
||||
const emojiPane = (this.$refs.emojiPane as HTMLDivElement);
|
||||
const category = document.getElementById(categoryName);
|
||||
if (!emojiPane || !category) return;
|
||||
emojiPane.scrollTop = category.offsetTop - 96;
|
||||
emojiPane.scrollTop = category.offsetTop - 550;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="p-3">
|
||||
<div class="py-1.5 flex flex-col">
|
||||
<div class="flex-row gap-x-2 overflow-x-scroll">
|
||||
<button
|
||||
@@ -232,13 +231,13 @@ export default {
|
||||
</div>
|
||||
|
||||
<div
|
||||
id="emoji-pane"
|
||||
class="overflow-hidden overflow-y-scroll scroll-smooth EmojiPicker max-h-[450px]"
|
||||
ref="emojiPane"
|
||||
class="overflow-hidden overflow-y-scroll scroll-smooth EmojiPicker max-h-[calc(475px-24px-48px)]"
|
||||
>
|
||||
<div
|
||||
v-for="category in categories"
|
||||
:key="category.name"
|
||||
class="text-black flex flex-col category bg-[var(--primary-dark)]"
|
||||
class="text-black flex flex-col category bg-[var(--secondary-bg)]"
|
||||
>
|
||||
<h6 class="uppercase text-[var(--primary-text)] sticky top-0 bg-inherit z-10 py-1">
|
||||
{{
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<span
|
||||
class="before:bg-[var(--primary-accent)] before:h-2 before:w-2 before:inline-block before:my-auto before:rounded-full before:mr-1"
|
||||
/>
|
||||
<span>{{ invite.server.participants.filter((e: IUser) => e.online === true).length }} Online</span>
|
||||
<span>{{ invite.server.participants.filter((e: SafeUser) => !!e.online).length }} Online</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex w-full justify-end">
|
||||
@@ -40,7 +40,7 @@
|
||||
import { PropType } from 'vue';
|
||||
import { useServerStore } from '~/stores/serverStore';
|
||||
import { useUserStore } from '~/stores/userStore';
|
||||
import { IInviteCode, IUser } from '~/types';
|
||||
import { IInviteCode, IServer, IUser, SafeUser } from '~/types';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
@@ -57,16 +57,18 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
userInServer(): boolean {
|
||||
return !!this.invite.server.participants.find((e: IUser) => e.id === this.user?.id);
|
||||
return !!this.invite.server.participants.find((e: SafeUser) => 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 });
|
||||
const { server } = await $fetch('/api/guilds/joinGuild', { method: 'POST', body: { inviteId: invite.id }, headers }) as { server: IServer };
|
||||
if (!server) return;
|
||||
this.servers?.push(server);
|
||||
|
||||
useServerStore().addServer(server);
|
||||
this.invite.server.participants.push(this.user);
|
||||
},
|
||||
}
|
||||
|
||||
@@ -120,13 +120,38 @@
|
||||
<div class="message-sender-text">
|
||||
<p
|
||||
v-if="showUsername"
|
||||
class="mb-1 font-semibold w-fit"
|
||||
class="flex flex-row"
|
||||
>
|
||||
<span
|
||||
ref="username"
|
||||
class="mb-1 font-semibold w-fit cursor-pointer hover:underline"
|
||||
@click="openUserProfile()"
|
||||
>
|
||||
{{ message.creator.username }}
|
||||
</span>
|
||||
<span
|
||||
v-if="userIsOwner"
|
||||
class="ml-0.5"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
><path
|
||||
class="text-yellow-300"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="m12 6l4 6l5-4l-2 10H5L3 8l5 4z"
|
||||
/></svg>
|
||||
</span>
|
||||
</p>
|
||||
<p
|
||||
class="break-words max-w-full"
|
||||
v-html="message.body"
|
||||
<div
|
||||
class="break-words max-w-full whitespace-pre-wrap"
|
||||
v-html="parseMessageBody(message.body, participants)"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
@@ -206,6 +231,10 @@ export default {
|
||||
computed: {
|
||||
reactions(): IReaction[] {
|
||||
return this.message.reactions?.filter((e) => e.users.length > 0) || [];
|
||||
},
|
||||
userIsOwner(): boolean {
|
||||
if (useActiveStore().type !== 'server') return false;
|
||||
return !!useActiveStore().server.server.participants.find((e) => e.id === this.message.creator.id)?.roles?.find((e) => e.owner) || false;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@@ -214,7 +243,8 @@ export default {
|
||||
if (useEmojiPickerStore().emojiPickerData.openedBy?.messageId !== this.message.id) return;
|
||||
const replacementEmoji = emojiJson.find((e) => e.short_names[0] === emoji);
|
||||
if (!replacementEmoji?.emoji) return;
|
||||
if (this.message.reactions?.find((e) => e.emoji === replacementEmoji.emoji)) return;
|
||||
if (this.message.reactions?.find((e) => e.emoji === replacementEmoji.emoji) &&
|
||||
this.message.reactions?.find((e) => e.emoji === replacementEmoji.emoji)?.users.find((e) => e.id === this.user?.id)) return;
|
||||
this.toggleReaction(replacementEmoji.emoji);
|
||||
});
|
||||
},
|
||||
@@ -222,11 +252,15 @@ export default {
|
||||
async toggleReaction(emoji: string) {
|
||||
let { message } = await $fetch(`/api/channels/${this.channelId}/messages/${this.message.id}/reactions/${emoji}`, { method: 'POST' }) as { message: IMessage };
|
||||
|
||||
message.body = parseMessageBody(message.body, this.participants);
|
||||
|
||||
useActiveStore().updateMessage(message);
|
||||
},
|
||||
openEmojiPicker() {
|
||||
console.log(useEmojiPickerStore().emojiPickerData);
|
||||
if (useEmojiPickerStore().emojiPickerData.opened && useEmojiPickerStore().emojiPickerData.type === 'emojiPicker' && useEmojiPickerStore().emojiPickerData.openedBy?.messageId === this.message.id) {
|
||||
useEmojiPickerStore().closeEmojiPicker();
|
||||
return;
|
||||
}
|
||||
|
||||
const actionButtons = document.getElementById(`actions-${this.message.id}`);
|
||||
if (!actionButtons) return;
|
||||
|
||||
@@ -235,6 +269,7 @@ export default {
|
||||
if (top + 522 > window.innerHeight) top = window.innerHeight - 522;
|
||||
|
||||
const payload = {
|
||||
type: 'emojiPicker',
|
||||
top,
|
||||
right: actionButtons.clientWidth + 40,
|
||||
openedBy: {
|
||||
@@ -243,7 +278,35 @@ export default {
|
||||
}
|
||||
} as IPopupData;
|
||||
|
||||
useEmojiPickerStore().toggleEmojiPicker(payload);
|
||||
useEmojiPickerStore().openEmojiPicker(payload);
|
||||
},
|
||||
openUserProfile() {
|
||||
const messagePane = document.getElementById('messagePane') as HTMLDivElement;
|
||||
const usernameElement = this.$refs.username as HTMLParagraphElement;
|
||||
if (!usernameElement || !messagePane) return;
|
||||
|
||||
const elementRect = usernameElement.getBoundingClientRect();
|
||||
let top = elementRect.top + window.pageYOffset;
|
||||
const left = window.innerWidth - messagePane.clientWidth + 28 + usernameElement.clientWidth;
|
||||
if (top + 522 > window.innerHeight) top = window.innerHeight - 522;
|
||||
|
||||
if (useEmojiPickerStore().emojiPickerData.opened &&
|
||||
useEmojiPickerStore().emojiPickerData.type === 'userInfo' &&
|
||||
useEmojiPickerStore().emojiPickerData.userId === this.message.creator.id &&
|
||||
useEmojiPickerStore().emojiPickerData.top === top &&
|
||||
useEmojiPickerStore().emojiPickerData.left === left) {
|
||||
useEmojiPickerStore().closeEmojiPicker();
|
||||
return;
|
||||
}
|
||||
|
||||
const payload = {
|
||||
type: 'userInfo',
|
||||
top,
|
||||
left,
|
||||
userId: this.message.creator.id
|
||||
} as IPopupData;
|
||||
|
||||
useEmojiPickerStore().openEmojiPicker(payload);
|
||||
},
|
||||
emojiStyles(emoji: string, width: number) {
|
||||
const emojis = emojiJson;
|
||||
@@ -298,6 +361,7 @@ pre.codeblock code {
|
||||
}
|
||||
|
||||
code.inline-code {
|
||||
color: var(--primary-accent);
|
||||
background-color: var(--secondary-bg);
|
||||
padding: 0.2rem;
|
||||
font-size: 85%;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<div
|
||||
id="messagePane"
|
||||
class="h-full relative bg-[var(--primary-bg)] flex flex-col"
|
||||
@mouseenter="mouseEnter"
|
||||
@mouseleave="mouseLeave"
|
||||
@@ -77,8 +78,8 @@
|
||||
:key="message.id"
|
||||
:message="message"
|
||||
:shift-pressed="shiftPressed"
|
||||
:show-username="i === 0 || channel.messages[i - 1]?.creator.id !== message.creator.id"
|
||||
:classes="calculateMessageClasses(message, i)"
|
||||
:show-username="calculateMessageDesign(message, i).showUsername"
|
||||
:classes="calculateMessageDesign(message, i).classes"
|
||||
:channel-id="channel.id"
|
||||
:participants="participants"
|
||||
/>
|
||||
@@ -214,21 +215,21 @@ export default {
|
||||
methods: {
|
||||
async sendMessage() {
|
||||
const headers = useRequestHeaders(['cookie']) as Record<string, string>;
|
||||
if (!this.messageContent) return;
|
||||
if (!this.messageContent || !this.messageContent.trim()) return;
|
||||
|
||||
let message: IMessage = await $fetch(`/api/channels/${this.channel.id}/sendMessage`, { method: 'post', body: { body: this.messageContent }, headers });
|
||||
|
||||
if (!message) return;
|
||||
if (this.channel.messages.includes(message)) return;
|
||||
|
||||
message.body = parseMessageBody(message.body, this.participants);
|
||||
|
||||
this.channel.messages.push(message);
|
||||
useActiveStore().addMessage(message);
|
||||
this.messageContent = '';
|
||||
const conversationDiv = this.$refs.conversationPane as HTMLDivElement;
|
||||
if (!conversationDiv) throw new Error('wtf');
|
||||
|
||||
setTimeout(() => {
|
||||
this.scrollToBottom();
|
||||
});
|
||||
},
|
||||
scrollToBottom() {
|
||||
const conversationDiv = this.$refs.conversationPane as HTMLDivElement;
|
||||
@@ -273,20 +274,20 @@ export default {
|
||||
this.shiftPressed = false;
|
||||
}
|
||||
},
|
||||
calculateMessageClasses(message: IMessage, i: number) {
|
||||
if (i === 0 || this.channel.messages[i - 1]?.creator.id !== message.creator.id) {
|
||||
if (i !== this.channel.messages.length - 1 || this.channel.messages[i + 1]?.creator.id === message.creator.id) {
|
||||
return 'mb-0 pb-0.5';
|
||||
calculateMessageDesign(message: IMessage, i: number) {
|
||||
if (i === 0 || (this.channel.messages[i - 1]?.creator.id !== message.creator.id || new Date(this.channel.messages[i-1]?.createdAt).getTime()+((30*60)*1000)<new Date(this.channel.messages[i]?.createdAt).getTime())) {
|
||||
if (i !== this.channel.messages.length - 1) {
|
||||
return { classes: 'mb-0 pb-0.5', showUsername: true };
|
||||
}
|
||||
} else {
|
||||
if (i !== this.channel.messages.length - 1 || this.channel.messages[i + 1]?.creator.id === message.creator.id) {
|
||||
return 'mt-0 mb-0 !py-0.5';
|
||||
return { classes: 'mt-0 mb-0 !py-0.5', showUsername: false };
|
||||
} else {
|
||||
return 'mt-0 pt-0.5 pb-1';
|
||||
return { classes: 'mt-0 pt-0.5 pb-1', showUsername: false };
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
return {classes: '', showUsername: true };
|
||||
},
|
||||
checkForMentions() {
|
||||
const input = this.$refs.messageBox as HTMLTextAreaElement;
|
||||
@@ -342,7 +343,7 @@ export default {
|
||||
completeMention(user: SafeUser) {
|
||||
this.messageContent = this.messageContent.replace('@' + this.search.content, `<@${user.id}>`);
|
||||
this.search.show = false;
|
||||
this.$refs.messageBox.focus();
|
||||
(this.$refs.messageBox as HTMLInputElement).focus();
|
||||
},
|
||||
async listenToWebsocket(conversationDiv: HTMLElement) {
|
||||
let { $io } = useNuxtApp();
|
||||
@@ -357,17 +358,12 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
message.body = parseMessageBody(message.body, this.participants);
|
||||
|
||||
if (this.channel.messages.find((e) => e.id === message.id)) {
|
||||
// message is already in the server, replace it with the updated message
|
||||
useActiveStore().updateMessage(message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.creator.id === this.user?.id) return;
|
||||
|
||||
|
||||
if (!document.hasFocus()) {
|
||||
new Notification(`Message from @${message.creator.username}`, { body: message.body, tag: this.channel.id.toString() });
|
||||
}
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
<template>
|
||||
<nav class="bg-[var(--primary-bg)] h-screen p-4 grid grid-cols-1 grid-rows-[56px_1fr_56px] shadow shadow-black/80">
|
||||
<div>
|
||||
<nuxt-link to="/channel/@me">
|
||||
<nuxt-link
|
||||
to="/channel/@me"
|
||||
draggable="false"
|
||||
>
|
||||
<button
|
||||
class="bg-[var(--tertiary-bg)] p-3 rounded-full transition-all hover:rounded-[1.375rem] ease-in-out hover:bg-[var(--tertiary-lightened-bg)] duration-300"
|
||||
class="bg-[var(--tertiary-bg)] p-3 transition-all ease-in-out hover:bg-[var(--tertiary-lightened-bg)] duration-300"
|
||||
:class="(activeConversation.type === 'dm') ? 'rounded-[1.375rem]' : 'rounded-full hover:rounded-[1.375rem]'"
|
||||
aria-label="Home"
|
||||
>
|
||||
<span>
|
||||
<svg
|
||||
@@ -51,9 +56,12 @@
|
||||
v-for="server in servers"
|
||||
:key="server.id"
|
||||
:to="'/channel/' + server.channels[0]?.id"
|
||||
draggable="false"
|
||||
>
|
||||
<button
|
||||
class="bg-[var(--tertiary-bg)] p-3 rounded-full transition-all hover:rounded-[1.375rem] ease-in-out hover:bg-[var(--tertiary-lightened-bg)] duration-300 h-[56px] w-[56px]"
|
||||
class="bg-[var(--tertiary-bg)] p-3 transition-all ease-in-out hover:bg-[var(--tertiary-lightened-bg)] duration-300 h-[56px] w-[56px]"
|
||||
:class="(activeConversation.type === 'server' && activeConversation.server.server.id === server.id) ? 'rounded-[1.375rem]' : 'rounded-full hover:rounded-[1.375rem]'"
|
||||
:aria-label="server.name"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -73,6 +81,7 @@
|
||||
|
||||
<button
|
||||
class="p-3 rounded-full transition-colors ease-in-out hover:bg-[var(--tertiary-lightened-bg)] duration-300 cursor-pointer"
|
||||
@click="createServerModalOpen = true"
|
||||
>
|
||||
<svg
|
||||
width="32"
|
||||
@@ -89,17 +98,71 @@
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<Modal
|
||||
:opened="createServerModalOpen"
|
||||
@close="createServerModalOpen = false"
|
||||
>
|
||||
<div
|
||||
class="bg-[var(--secondary-bg)] rounded-xl shadow-2xl flex flex-row overflow-hidden z-20 absolute border border-[var(--tertiary-bg)] -translate-x-1/2 -translate-y-1/2 top-1/2 left-1/2"
|
||||
>
|
||||
<img
|
||||
src="/eberhard-grossgasteiger-eBXIZe1DU7Y-unsplash.jpg"
|
||||
class="h-96 w-64 object-cover"
|
||||
/>
|
||||
<div class="p-4 flex flex-col text-center">
|
||||
<h1 class="font-semibold text-2xl">
|
||||
Create Server
|
||||
</h1>
|
||||
<form
|
||||
class="flex flex-col gap-y-3 my-2"
|
||||
@submit.prevent="createServer"
|
||||
>
|
||||
<input
|
||||
v-model="serverName"
|
||||
class="px-4 py-2 rounded-md w-full bg-[var(--primary-input)] shadow-2xl placeholder:text-[var(--primary-placeholder)] focus:outline-none"
|
||||
name="name"
|
||||
placeholder="Server Name"
|
||||
/>
|
||||
<input
|
||||
type="submit"
|
||||
value="Submit"
|
||||
class="w-full bg-[#5865F2] py-2 px-4 rounded-md cursor-pointer"
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { useActiveStore } from '~/stores/activeStore';
|
||||
import { useServerStore } from '~/stores/serverStore';
|
||||
import { IServer } from '~/types';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
servers: storeToRefs(useServerStore()).servers
|
||||
createServerModalOpen: false,
|
||||
serverName: '',
|
||||
servers: storeToRefs(useServerStore()).servers,
|
||||
activeConversation: {
|
||||
type: storeToRefs(useActiveStore()).type,
|
||||
server: storeToRefs(useActiveStore()).server
|
||||
}
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
async createServer() {
|
||||
const serverStore = useServerStore();
|
||||
const headers = useRequestHeaders(['cookie']) as Record<string, string>;
|
||||
const server: IServer = await $fetch('/api/channels/create', { method: 'post', body: { serverName: this.serverName }, headers });
|
||||
this.createServerModalOpen = false;
|
||||
this.serverName = '';
|
||||
serverStore.addServer(server);
|
||||
|
||||
navigateTo(`/channel/${server.channels[0].id}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -1,13 +1,16 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="opened"
|
||||
class="z-10 bg-[var(--primary-dark)] w-fit rounded-md shadow-md p-3"
|
||||
class="z-10 bg-[var(--secondary-bg)] w-fit rounded-lg shadow-md border border-[var(--tertiary-bg)] overflow-hidden"
|
||||
>
|
||||
<div class="max-w-[350px] max-h-[450px] overflow-hidden">
|
||||
<div class="max-w-[374px] max-h-[475px] overflow-hidden">
|
||||
<EmojiPicker
|
||||
v-if="openedBy === 'emojiPicker'"
|
||||
@picked-emoji="$emit('picked-emoji', $event)"
|
||||
/>
|
||||
<UserProfile
|
||||
v-else
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -16,10 +19,9 @@
|
||||
export default {
|
||||
props: {
|
||||
openedBy: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
opened: Boolean
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -292,6 +292,41 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Modal
|
||||
:opened="createChannelModelOpen"
|
||||
@close="createChannelModelOpen = false"
|
||||
>
|
||||
<div
|
||||
class="bg-[var(--secondary-bg)] rounded-xl shadow-2xl flex flex-row overflow-hidden z-20 absolute border border-[var(--tertiary-bg)] -translate-x-1/2 -translate-y-1/2 top-1/2 left-1/2"
|
||||
>
|
||||
<img
|
||||
src="/ryan-klaus-5CkzYaubjkk-unsplash.jpg"
|
||||
class="h-96 w-64 object-cover"
|
||||
/>
|
||||
<div class="p-4 flex flex-col text-center">
|
||||
<h1 class="font-semibold text-2xl">
|
||||
Create Channel
|
||||
</h1>
|
||||
<form
|
||||
class="flex flex-col gap-y-3 my-2"
|
||||
@submit.prevent="createChannel"
|
||||
>
|
||||
<input
|
||||
v-model="channelName"
|
||||
class="px-4 py-2 rounded-md w-full bg-[var(--primary-input)] shadow-2xl placeholder:text-[var(--primary-placeholder)] focus:outline-none"
|
||||
name="name"
|
||||
placeholder="Channel Name"
|
||||
/>
|
||||
<input
|
||||
type="submit"
|
||||
value="Submit"
|
||||
class="w-full bg-[#5865F2] py-2 px-4 rounded-md cursor-pointer"
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</aside>
|
||||
</template>
|
||||
|
||||
@@ -319,10 +354,10 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
userIsOwner() {
|
||||
return this.activeServer.data.server && this.activeServer.type === 'server' && this.activeServer.data.server.roles?.find((e: IRole) => e.users.some((el) => el.id === this.user?.id))?.owner;
|
||||
return this.activeServer.type === 'server' && this.activeServer.data.server.participants.find((e) => e.id === this.user?.id)?.roles?.some((e) => e.owner === true);
|
||||
},
|
||||
userIsAdmin() {
|
||||
return this.activeServer.data.server && this.activeServer.type === 'server' && this.activeServer.data.server.roles?.find((e: IRole) => e.users.some((el) => el.id === this.user?.id))?.administer;
|
||||
return this.activeServer.type === 'server' && this.activeServer.data.server.participants.find((e) => e.id === this.user?.id)?.roles?.some((e) => e.administer === true);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -337,12 +372,14 @@ export default {
|
||||
|
||||
useServerStore().addChannel(this.activeServer.data.server.id, channel);
|
||||
this.createChannelModelOpen = false;
|
||||
|
||||
navigateTo(`/channel/${channel.id}`);
|
||||
},
|
||||
async createInvite() {
|
||||
const headers = useRequestHeaders(['cookie']) as Record<string, string>;
|
||||
const inviteCode = await $fetch(`/api/guilds/${this.activeServer.data.server.id}/createInvite`, { method: 'POST', headers });
|
||||
},
|
||||
async logout() {
|
||||
logout() {
|
||||
useUserStore().logout();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,138 @@
|
||||
<template>
|
||||
<div />
|
||||
<div class="w-[374px] max-h-[475px] overflow-y-scroll">
|
||||
<div class="relative h-[calc(160px+56px)]">
|
||||
<div class="w-full h-40 absolute">
|
||||
<img
|
||||
src="/tansu-topuzoglu-v2mlqhy5dLU-unsplash.jpg"
|
||||
class="h-40 w-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div class="w-[28%] aspect-square bg-[var(--primary-bg)] border-2 border-[var(--tertiary-bg)] rounded-xl overflow-hidden left-1/2 -translate-x-1/2 top-28 z-10 absolute">
|
||||
<img
|
||||
src="/daiga-ellaby-snUtnGUp2zU-unsplash.jpg"
|
||||
class="h-40 w-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-3 pb-2">
|
||||
<div class="text-center">
|
||||
<p class="font-semibold">
|
||||
{{ user.username }}
|
||||
</p>
|
||||
</div>
|
||||
<hr class="border-[var(--tertiary-lightened-bg)] my-2" />
|
||||
<div class="m-1 p-2 rounded-lg bg-[var(--tertiary-bg)] flex flex-col gap-y-1">
|
||||
<div v-if="true">
|
||||
<p class="font-semibold text-sm">
|
||||
About Me
|
||||
</p>
|
||||
<div class="text-sm p-1">
|
||||
<p>lorem ipsum</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p class="font-semibold text-sm">
|
||||
Member since
|
||||
</p>
|
||||
<div class="text-sm p-1">
|
||||
<p>
|
||||
{{
|
||||
new Date(user.createdAt).toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
})
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="!isDm"
|
||||
class="mb-2"
|
||||
>
|
||||
<p
|
||||
v-if="roles.length < 1"
|
||||
class="font-semibold text-sm"
|
||||
>
|
||||
No Roles
|
||||
</p>
|
||||
</div>
|
||||
<div v-if="user.id !== userData.id">
|
||||
<input
|
||||
v-model="message"
|
||||
class="bg-[var(--secondary-bg)] placeholder:text-[var(--primary-placeholder)] px-2 focus:outline-none py-1 rounded-md w-full border border-[var(--tertiary-lightened-bg)]"
|
||||
:placeholder="`Message @${user.username}`"
|
||||
@keypress.enter="sendDM()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<!--
|
||||
<script>
|
||||
import { IUser } from '~/types';
|
||||
|
||||
<script lang="ts">
|
||||
import { useActiveStore } from '~/stores/activeStore';
|
||||
import { useDmStore } from '~/stores/dmStore';
|
||||
import { useEmojiPickerStore } from '~/stores/emojiPickerStore';
|
||||
import { useUserStore } from '~/stores/userStore';
|
||||
import { IUser, IRole } from '~/types';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
user: {
|
||||
type: IUser,
|
||||
required: true
|
||||
async setup() {
|
||||
const userData = useUserStore().user;
|
||||
async function fetchUser() {
|
||||
const emojiPickerData = useEmojiPickerStore().emojiPickerData;
|
||||
const headers = useRequestHeaders(['cookie']) as Record<string, string>;
|
||||
const activeServer = useActiveStore().server;
|
||||
|
||||
const isDm = useRoute().path.includes('@me');
|
||||
|
||||
let user: IUser | null;
|
||||
|
||||
if (isDm) {
|
||||
user = await $fetch(`/api/user/${emojiPickerData.userId}/profile`, { headers }) as IUser | null;
|
||||
} else {
|
||||
user = await $fetch(`/api/user/${emojiPickerData.userId}/${activeServer.server.id}/profile`, { headers }) as IUser | null;
|
||||
}
|
||||
|
||||
return { user, isDm };
|
||||
}
|
||||
|
||||
const { user, isDm } = await fetchUser();
|
||||
|
||||
if (!user) return;
|
||||
|
||||
return { user, isDm, fetchUser, userData };
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
message: ''
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
roles(): IRole[] {
|
||||
return this.user.roles?.filter((e: IRole) => e.owner === false) || [];
|
||||
},
|
||||
userIsOwner(): boolean {
|
||||
return this.user.roles?.some((e: IRole) => e.owner === true) || false;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async sendDM() {
|
||||
if (!this.message.trim()) return;
|
||||
|
||||
const headers = useRequestHeaders(['cookie']) as Record<string, string>;
|
||||
const preExistingDM = useDmStore().getByPartnerId(this.user.id);
|
||||
|
||||
if (preExistingDM && useRoute().path !== `/channel/@me/${preExistingDM.id}`) {
|
||||
await navigateTo(`/channel/@me/${preExistingDM.id}`);
|
||||
}
|
||||
|
||||
await $fetch(`/api/channels/${preExistingDM.id}/sendMessage`, { method: 'post', body: { body: this.message }, headers });
|
||||
this.message = '';
|
||||
|
||||
useEmojiPickerStore().closeEmojiPicker();
|
||||
},
|
||||
}
|
||||
};
|
||||
</script> -->
|
||||
</script>
|
||||
@@ -13,7 +13,7 @@ import { useUserStore } from '~/stores/userStore';
|
||||
import { useServerStore } from '~/stores/serverStore';
|
||||
import { useDmStore } from '~/stores/dmStore';
|
||||
import { useActiveStore } from '~/stores/activeStore';
|
||||
import { IChannel } from '~/types';
|
||||
import { IChannel, IServer, SafeUser } from '~/types';
|
||||
|
||||
export default {
|
||||
async setup() {
|
||||
@@ -25,8 +25,8 @@ export default {
|
||||
|
||||
if (!userStore.isLoggedIn) {
|
||||
const [userData, serverData] = await Promise.all([
|
||||
$fetch('/api/getCurrentUser', { headers }),
|
||||
$fetch('/api/user/getServers', { headers })
|
||||
$fetch('/api/getCurrentUser', { headers }) as Promise<SafeUser | null>,
|
||||
$fetch('/api/user/getServers', { headers }) as Promise<{ dms: IChannel[], servers: IServer[] } | null>
|
||||
]);
|
||||
|
||||
if (!userData || !serverData) throw new Error('No user data or server data');
|
||||
@@ -40,7 +40,7 @@ export default {
|
||||
|
||||
const isDm = route.path.includes('@me');
|
||||
|
||||
if (isDm && route.params.dmId) {
|
||||
if (isDm && route.params.dmId && useActiveStore().dm.id !== route.params.dmId) {
|
||||
const dmData: IChannel = await $fetch(`/api/channels/${route.params.dmId}`, { headers });
|
||||
|
||||
if (!dmData) throw new Error('Could not find dm.');
|
||||
@@ -49,12 +49,14 @@ export default {
|
||||
useActiveStore().setActiveDM(dmData);
|
||||
}
|
||||
|
||||
if (!isDm && route.params.channelId) {
|
||||
if (!isDm && route.params.channelId && useActiveStore().server.channel.id !== route.params.channelId) {
|
||||
const [channel, server] = await Promise.all([
|
||||
$fetch(`/api/channels/${route.params.channelId}`, { headers }) as unknown as IChannel,
|
||||
$fetch(`/api/channels/${route.params.channelId}/guild`, { headers })
|
||||
$fetch(`/api/channels/${route.params.channelId}`, { headers }) as Promise<IChannel | null>,
|
||||
$fetch(`/api/channels/${route.params.channelId}/guild`, { headers }) as Promise<IServer | null>
|
||||
]);
|
||||
|
||||
if (!server || !channel) throw new Error('No channel or server');
|
||||
|
||||
if (!server) throw new Error('Could not find server.');
|
||||
useServerStore().addServer(server);
|
||||
useActiveStore().setActiveServer(channel, useServerStore().servers);
|
||||
@@ -62,14 +64,8 @@ export default {
|
||||
|
||||
if (isDm && !route.params.dmId) {
|
||||
// on '/@me'
|
||||
useActiveStore().type = 'dm';
|
||||
useActiveStore().setActiveHome();
|
||||
}
|
||||
|
||||
// const socket = ref(null);
|
||||
|
||||
// socket.value = io('127.0.0.1:3000', {
|
||||
// auth: (cb) => cb({ token: useCookie('sessionToken').value })
|
||||
// });
|
||||
}
|
||||
};
|
||||
</script>
|
||||
19
package-lock.json
generated
19
package-lock.json
generated
@@ -4734,19 +4734,6 @@
|
||||
"version": "1.0.0",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.1",
|
||||
"license": "MIT"
|
||||
@@ -13849,12 +13836,6 @@
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"optional": true
|
||||
},
|
||||
"function-bind": {
|
||||
"version": "1.1.1"
|
||||
},
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
:participants="participants"
|
||||
/>
|
||||
<div
|
||||
class="fixed mr-3"
|
||||
:style="`top: ${emojiPickerData.top}px; right: ${emojiPickerData.right}px`"
|
||||
class="fixed mx-3"
|
||||
:style="`top: ${emojiPickerData.top}px; ${(emojiPickerData.right !== undefined) ? `right: ${emojiPickerData.right}px;` : `left: ${emojiPickerData.left}px`}`"
|
||||
>
|
||||
<Transition>
|
||||
<Popup
|
||||
@@ -22,9 +22,8 @@
|
||||
import { useActiveStore } from '~/stores/activeStore';
|
||||
import { useDmStore } from '~/stores/dmStore';
|
||||
import { useEmojiPickerStore } from '~/stores/emojiPickerStore';
|
||||
import { useServerStore } from '~/stores/serverStore';
|
||||
import { useUserStore } from '~/stores/userStore';
|
||||
import { IChannel, IMessage, IServer, SafeUser } from '~/types';
|
||||
import { IChannel, IMessage, SafeUser } from '~/types';
|
||||
|
||||
definePageMeta({
|
||||
middleware: 'auth'
|
||||
@@ -53,10 +52,6 @@ export default {
|
||||
|
||||
const channel = useActiveStore().dm;
|
||||
|
||||
channel.messages?.forEach((e: IMessage) => {
|
||||
e.body = parseMessageBody(e.body, participants);
|
||||
});
|
||||
|
||||
const friend = participants.find((e) => e.id !== useUserStore().user?.id)?.username;
|
||||
|
||||
useHeadSafe({
|
||||
@@ -70,12 +65,7 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// socket: storeToRefs(useGlobalStore()).socket as unknown as Server,
|
||||
emojiPickerData: storeToRefs(useEmojiPickerStore()).emojiPickerData,
|
||||
emojiPickerStyles: {
|
||||
top: storeToRefs(useEmojiPickerStore()).emojiPickerData.value.top + 'px',
|
||||
right: storeToRefs(useEmojiPickerStore()).emojiPickerData.value.right + 'px',
|
||||
}
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -21,7 +21,7 @@ export default {
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
useActiveStore().type = 'dm';
|
||||
useActiveStore().setActiveHome();
|
||||
},
|
||||
methods: {
|
||||
async startDM() {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<!-- eslint-disable vue/no-multiple-template-root -->
|
||||
<template>
|
||||
<MessagePane
|
||||
:channel="channel"
|
||||
:participants="server.participants"
|
||||
:channel="server.channel"
|
||||
:participants="server.server.participants"
|
||||
/>
|
||||
<div
|
||||
class="fixed mr-3"
|
||||
:style="`top: ${emojiPickerData.top}px; right: ${emojiPickerData.right}px`"
|
||||
class="fixed mx-3"
|
||||
:style="`top: ${emojiPickerData.top}px; ${(emojiPickerData.right !== undefined) ? `right: ${emojiPickerData.right}px;` : `left: ${emojiPickerData.left}px`}`"
|
||||
>
|
||||
<Transition>
|
||||
<Popup
|
||||
@@ -49,38 +49,27 @@ export default {
|
||||
}
|
||||
useEmojiPickerStore().closeEmojiPicker();
|
||||
|
||||
const channel = useActiveStore().server.channel;
|
||||
const server = useActiveStore().server.server;
|
||||
|
||||
channel.messages?.forEach((e: IMessage) => {
|
||||
e.body = parseMessageBody(e.body, server.participants);
|
||||
});
|
||||
const server = useActiveStore().server;
|
||||
|
||||
useHeadSafe({
|
||||
title: `#${channel.name} | ${server.name} - Blop`
|
||||
title: `#${server.channel.name} | ${server.server.name} - Blop`
|
||||
});
|
||||
|
||||
return {
|
||||
channel,
|
||||
server
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// socket: storeToRefs(useGlobalStore()).socket as unknown as Server,
|
||||
emojiPickerData: storeToRefs(useEmojiPickerStore()).emojiPickerData,
|
||||
emojiPickerStyles: {
|
||||
top: storeToRefs(useEmojiPickerStore()).emojiPickerData.value.top + 'px',
|
||||
right: storeToRefs(useEmojiPickerStore()).emojiPickerData.value.right + 'px',
|
||||
}
|
||||
};
|
||||
},
|
||||
async mounted() {
|
||||
const { $io } = useNuxtApp();
|
||||
|
||||
(await $io).on(`addChannel-${this.server.id}`, (ev) => {
|
||||
(await $io).on(`addChannel-${this.server.server.id}`, (ev) => {
|
||||
const newChannel = ev as IChannel;
|
||||
useServerStore().addChannel(this.server.id, newChannel);
|
||||
useServerStore().addChannel(this.server.server.id, newChannel);
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="w-screen h-screen flex justify-center items-center bg-[var(--primary-bg)]">
|
||||
<div class="bg-[var(--secondary-bg)] rounded-xl shadow-2xl flex flex-row overflow-hidden">
|
||||
<img
|
||||
src="/plants.jpg"
|
||||
src="/nahil-naseer-xljtGZ2-P3Y-unsplash.jpg"
|
||||
class="h-96 w-64 object-cover"
|
||||
/>
|
||||
<div class="p-4 flex flex-col text-center">
|
||||
@@ -47,7 +47,7 @@
|
||||
import { useDmStore } from '~/stores/dmStore';
|
||||
import { useServerStore } from '~/stores/serverStore';
|
||||
import { useUserStore } from '~/stores/userStore';
|
||||
import { SafeUser } from '~/types';
|
||||
import { IChannel, IServer, SafeUser } from '~/types';
|
||||
|
||||
definePageMeta({
|
||||
layout: 'clean'
|
||||
@@ -77,8 +77,8 @@ export default {
|
||||
|
||||
useUserStore().setUser(loginData.user);
|
||||
|
||||
useServerStore().setServers(loginData.user.servers);
|
||||
useDmStore().setDms(loginData.user.channels);
|
||||
useServerStore().setServers(loginData.user.servers || [] as IServer[]);
|
||||
useDmStore().setDms(loginData.user.channels || [] as IChannel[]);
|
||||
|
||||
return navigateTo('/');
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="w-screen h-screen flex justify-center items-center bg-[var(--primary-bg)]">
|
||||
<div class="bg-[var(--secondary-bg)] rounded-xl shadow-2xl flex flex-row overflow-hidden">
|
||||
<img
|
||||
src="/flowery-plants.jpg"
|
||||
src="/annie-spratt-8mqOw4DBBSg-unsplash.jpg"
|
||||
class="h-96 w-64 object-cover filter brightness-95"
|
||||
/>
|
||||
<div class="p-4 flex flex-col text-center">
|
||||
@@ -54,7 +54,7 @@
|
||||
import { useDmStore } from '~/stores/dmStore';
|
||||
import { useServerStore } from '~/stores/serverStore';
|
||||
import { useUserStore } from '~/stores/userStore';
|
||||
import { SafeUser } from '~/types';
|
||||
import { IChannel, IServer, SafeUser } from '~/types';
|
||||
|
||||
definePageMeta({
|
||||
layout: 'clean'
|
||||
@@ -86,8 +86,8 @@ export default {
|
||||
|
||||
useUserStore().setUser(signupData.user);
|
||||
|
||||
useServerStore().setServers(signupData.user.servers);
|
||||
useDmStore().setDms(signupData.user.channels);
|
||||
useServerStore().setServers(signupData.user.servers || [] as IServer[]);
|
||||
useDmStore().setDms(signupData.user.channels || [] as IChannel[]);
|
||||
|
||||
return navigateTo('/');
|
||||
}
|
||||
|
||||
@@ -1,3 +1,31 @@
|
||||
<template>
|
||||
<p>hey</p>
|
||||
<div class="bg-[var(--primary-bg)] h-full">
|
||||
<Popup
|
||||
:opened="true"
|
||||
:openedBy="'userInfo'"
|
||||
/>
|
||||
<Popup
|
||||
:opened="true"
|
||||
:openedBy="'emojiPicker'"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { useEmojiPickerStore } from '~/stores/emojiPickerStore';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
emojiPickerData: storeToRefs(useEmojiPickerStore()).emojiPickerData,
|
||||
emojiPickerStyles: {
|
||||
top: storeToRefs(useEmojiPickerStore()).emojiPickerData.value.top + 'px',
|
||||
right: storeToRefs(useEmojiPickerStore()).emojiPickerData.value.right + 'px',
|
||||
}
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
useEmojiPickerStore().openEmojiPicker({ opened: true, type: 'userInfo', right: 0, top: 0 });
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -105,4 +105,6 @@ model Reaction {
|
||||
messageId String?
|
||||
Message Message? @relation(fields: [messageId], references: [id])
|
||||
users User[] @relation("ReactionToUser")
|
||||
|
||||
@@unique([emoji, messageId])
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 216 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 156 KiB |
@@ -1,5 +1,5 @@
|
||||
import emojiRegex from 'emoji-regex';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { Prisma, PrismaClient } from '@prisma/client';
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
@@ -123,6 +123,7 @@ export default defineEventHandler(async (event) => {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
try {
|
||||
reaction = await prisma.reaction.create({
|
||||
data: {
|
||||
emoji,
|
||||
@@ -138,6 +139,14 @@ export default defineEventHandler(async (event) => {
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
if (err instanceof Prisma.PrismaClientKnownRequestError && err.code === 'P2002') {
|
||||
// gracefully fail as it's likely a race condition
|
||||
return;
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!reaction.messageId) return;
|
||||
|
||||
@@ -21,7 +21,7 @@ export default defineEventHandler(async (event) => {
|
||||
|
||||
const channelId = event.context.params.id;
|
||||
|
||||
if (!req || !channelId) {
|
||||
if (!req.body || !channelId || !req.body.trim()) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: 'A body is required to send a message.',
|
||||
@@ -166,6 +166,7 @@ export default defineEventHandler(async (event) => {
|
||||
select: {
|
||||
id: true,
|
||||
body: true,
|
||||
createdAt: true,
|
||||
creator: {
|
||||
select: {
|
||||
id: true,
|
||||
|
||||
@@ -21,19 +21,6 @@ export default defineEventHandler(async (event) => {
|
||||
|
||||
const { serverName } = body;
|
||||
|
||||
const preExistingServer = await prisma.server.findFirst({
|
||||
where: {
|
||||
name: serverName
|
||||
}
|
||||
}) as IServer | null;
|
||||
|
||||
if (preExistingServer) {
|
||||
throw createError({
|
||||
statusCode: 409,
|
||||
statusMessage: `Server with name ${serverName} already exists.`,
|
||||
});
|
||||
}
|
||||
|
||||
const server = await prisma.server.create({
|
||||
data: {
|
||||
name: serverName,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { IChannel, IMessage, IServer } from '~/types';
|
||||
import { useServerStore } from './serverStore';
|
||||
import { IChannel, IMessage, IRole, IServer, SafeUser } from '~/types';
|
||||
|
||||
export const useActiveStore = defineStore('activeStore', {
|
||||
state: () => ({
|
||||
@@ -11,11 +10,25 @@ export const useActiveStore = defineStore('activeStore', {
|
||||
}
|
||||
}),
|
||||
actions: {
|
||||
setActiveHome() {
|
||||
this.server = {
|
||||
server: {} as IServer,
|
||||
channel: {} as IChannel
|
||||
};
|
||||
|
||||
this.type = 'dm';
|
||||
},
|
||||
setActiveDM(dm: IChannel) {
|
||||
this.server = {
|
||||
server: {} as IServer,
|
||||
channel: {} as IChannel
|
||||
};
|
||||
|
||||
this.type = 'dm';
|
||||
this.dm = dm;
|
||||
},
|
||||
setActiveServer(channel: IChannel, servers: IServer[]) {
|
||||
this.dm = {} as IChannel;
|
||||
this.type = 'server';
|
||||
|
||||
const activeServer = servers.find((e: IServer) => {
|
||||
@@ -32,13 +45,39 @@ export const useActiveStore = defineStore('activeStore', {
|
||||
|
||||
const activeChannel = activeServer.channels[activeChannelIndex];
|
||||
|
||||
activeServer.roles.map((role: IRole) => {
|
||||
role.users.map((e: SafeUser) => {
|
||||
const userIndex = activeServer.participants.findIndex((user: SafeUser) => user.id === e.id);
|
||||
|
||||
if (activeServer.participants[userIndex] == undefined) return;
|
||||
|
||||
activeServer.participants[userIndex].roles = activeServer.participants[userIndex].roles || [];
|
||||
|
||||
const userRole = role;
|
||||
|
||||
delete(userRole.users);
|
||||
|
||||
activeServer.participants[userIndex].roles.push(userRole);
|
||||
});
|
||||
});
|
||||
|
||||
delete(activeServer.roles);
|
||||
|
||||
if (!activeChannel) return;
|
||||
|
||||
this.server.server = activeServer;
|
||||
this.server.channel = activeChannel;
|
||||
},
|
||||
getMessageById(id: string) {
|
||||
const channel = (this.type === 'server') ? this.server.channel : this.dm;
|
||||
|
||||
return channel.messages.find((e: IMessage) => e.id === id);
|
||||
},
|
||||
addMessage(message: IMessage) {
|
||||
const channel = (this.type === 'server') ? this.server.channel : this.dm;
|
||||
|
||||
if (channel.messages.findIndex((e: IMessage) => e.id === message.id) !== -1) return;
|
||||
|
||||
channel.messages.push(message);
|
||||
},
|
||||
updateMessage(message: IMessage) {
|
||||
@@ -52,11 +91,10 @@ export const useActiveStore = defineStore('activeStore', {
|
||||
},
|
||||
removeMessage(messageId: string) {
|
||||
const channel = (this.type === 'server') ? this.server.channel : this.dm;
|
||||
const messageIndex = channel.messages.findIndex((e: IMessage) => e.id === messageId);
|
||||
|
||||
if (messageIndex == -1) return;
|
||||
if (!channel.messages.find(m => m.id === messageId)) return;
|
||||
|
||||
delete(channel.messages[messageIndex]);
|
||||
channel.messages = channel.messages.filter((e: IMessage) => e.id !== messageId);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { IChannel } from '~/types';
|
||||
import { IChannel, IUser, SafeUser } from '~/types';
|
||||
|
||||
export const useDmStore = defineStore('dmStore', {
|
||||
state: () => ({
|
||||
@@ -18,6 +18,9 @@ export const useDmStore = defineStore('dmStore', {
|
||||
},
|
||||
getById(id: string) {
|
||||
return this.dms.find((e) => e.id === id);
|
||||
},
|
||||
getByPartnerId(id: string) {
|
||||
return this.dms.find((e) => e.dmParticipants?.some((e: SafeUser) => e.id === id));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2,15 +2,13 @@ import { IPopupData } from '~/types';
|
||||
|
||||
export const useEmojiPickerStore = defineStore('emojiPickerStore', {
|
||||
state: () => ({
|
||||
emojiPickerData: {type: 'emojiPicker'} as IPopupData,
|
||||
emojiPickerData: {} as IPopupData,
|
||||
}),
|
||||
actions: {
|
||||
openEmojiPicker(payload: IPopupData) {
|
||||
this.emojiPickerData.type = 'emojiPicker';
|
||||
this.emojiPickerData.top = payload.top;
|
||||
this.emojiPickerData.right = payload.right;
|
||||
this.emojiPickerData.openedBy = payload.openedBy;
|
||||
this.emojiPickerData.opened = true;
|
||||
console.log(this.emojiPickerData, payload);
|
||||
|
||||
this.emojiPickerData = { ...payload, opened: true };
|
||||
},
|
||||
toggleEmojiPicker(payload: IPopupData) {
|
||||
let messageId;
|
||||
@@ -31,7 +29,7 @@ export const useEmojiPickerStore = defineStore('emojiPickerStore', {
|
||||
},
|
||||
closeEmojiPicker() {
|
||||
if (this.emojiPickerData.openedBy) this.emojiPickerData.openedBy.messageId = '';
|
||||
this.emojiPickerData.opened = false;
|
||||
this.emojiPickerData = { opened: false } as IPopupData;
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
@@ -77,10 +77,12 @@ export interface IReaction {
|
||||
export interface IPopupData {
|
||||
opened: boolean;
|
||||
top: number;
|
||||
right: number;
|
||||
right?: number;
|
||||
left?: number;
|
||||
type: 'emojiPicker' | 'userInfo';
|
||||
openedBy?: {
|
||||
type: 'message' | 'messageInput';
|
||||
messageId?: string;
|
||||
};
|
||||
userId?: string;
|
||||
}
|
||||
Reference in New Issue
Block a user