Compare commits

...

7 commits

7 changed files with 71 additions and 39 deletions

11
app.vue
View file

@ -17,12 +17,21 @@ const contextMenu = useState<ContextMenuInterface>("contextMenu", () => ({ show:
onMounted(() => { onMounted(() => {
loadPreferredThemes() loadPreferredThemes()
document.addEventListener("contextmenu", (e) => {
if (contextMenu.value.show) {
e.preventDefault();
if (e.target instanceof Element && !e.target.classList.contains("context-menu-item")) {
removeContextMenu(contextMenu);
}
}
});
document.addEventListener("mousedown", (e) => { document.addEventListener("mousedown", (e) => {
if (e.target instanceof HTMLElement && e.target.classList.contains("context-menu-item")) return; if (e.target instanceof HTMLElement && e.target.classList.contains("context-menu-item")) return;
console.log("click"); console.log("click");
console.log("target:", e.target); console.log("target:", e.target);
console.log(e.target instanceof HTMLDivElement); console.log(e.target instanceof HTMLDivElement);
if (contextMenu.value.show) { if (e.button != 2 && contextMenu.value.show) {
console.log("context menu is shown, hiding"); console.log("context menu is shown, hiding");
removeContextMenu(contextMenu); removeContextMenu(contextMenu);
} }

View file

@ -1,5 +1,5 @@
<template> <template>
<div class="member-item" @click.prevent="showModalPopup" tabindex="0" @contextmenu="showContextMenu($event, contextMenu, menuSections)"> <div class="member-item" @click.prevent="showModalPopup" tabindex="0" @contextmenu="showContextMenu($event, menuSections)">
<Avatar :profile="props.member" class="member-avatar"/> <Avatar :profile="props.member" class="member-avatar"/>
<span class="member-display-name" :style="`color: ${generateIrcColor(props.member.user.uuid)}`"> <span class="member-display-name" :style="`color: ${generateIrcColor(props.member.user.uuid)}`">
{{ getDisplayName(props.member) }} {{ getDisplayName(props.member) }}
@ -7,24 +7,27 @@
</div> </div>
<ModalProfilePopup v-if="modalPopupVisible" :profile="props.member" <ModalProfilePopup v-if="modalPopupVisible" :profile="props.member"
:onFinish="hideModalPopup" :keepalive="false" /> :onFinish="hideModalPopup" :keepalive="false" />
<ModalConfirmation v-if="confirmationModal && confirmationModal.show" :action-name="confirmationModal.actionName"
:target-name="getDisplayName(props.member)" :callback="confirmationModal.callback"
:onClose="resetConfirmationModal" :onCancel="resetConfirmationModal" />
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ModalProfilePopup } from '#components'; import { ModalProfilePopup } from '#components';
import type { ContextMenuInterface, GuildMemberResponse } from '~/types/interfaces'; import type { GuildMemberResponse, IConfirmationModal } from '~/types/interfaces';
const { getDisplayName } = useProfile() const { getDisplayName } = useProfile()
const contextMenu = useState<ContextMenuInterface>("contextMenu");
const props = defineProps<{ const props = defineProps<{
member: GuildMemberResponse member: GuildMemberResponse
}>(); }>();
const menuSections = await createMemberContextMenuItems(props.member, props.member.guild_uuid); const confirmationModal = ref<IConfirmationModal>();
const menuSections = await createMemberContextMenuItems(props.member, props.member.guild_uuid, confirmationModal);
const modalPopupVisible = ref<boolean>(false); const modalPopupVisible = ref<boolean>(false);
function showModalPopup() { function showModalPopup() {
modalPopupVisible.value = true modalPopupVisible.value = true
} }
@ -32,6 +35,14 @@ function showModalPopup() {
function hideModalPopup() { function hideModalPopup() {
modalPopupVisible.value = false modalPopupVisible.value = false
} }
function resetConfirmationModal() {
console.log("[CONFIRM] resetting");
if (confirmationModal) {
confirmationModal.value = { show: false, actionName: "", callback: () => {} };
}
}
</script> </script>
<style> <style>

View file

@ -1,5 +1,5 @@
<template> <template>
<div v-if="props.type == 'normal' || props.replyMessage" ref="messageElement" @contextmenu="showContextMenu($event, contextMenu, messageMenuSections)" <div v-if="props.type == 'normal' || props.replyMessage" ref="messageElement" @contextmenu="showContextMenu($event, messageMenuSections)"
class="message normal-message" :class="{ 'highlighted': (props.isMentioned || (props.replyMessage && props.message.member.user.uuid != me!.uuid && props.replyMessage?.member.user.uuid == me!.uuid)) }" class="message normal-message" :class="{ 'highlighted': (props.isMentioned || (props.replyMessage && props.message.member.user.uuid != me!.uuid && props.replyMessage?.member.user.uuid == me!.uuid)) }"
:data-message-id="props.message.uuid" :editing.sync="props.editing"> :data-message-id="props.message.uuid" :editing.sync="props.editing">
<div v-if="props.replyMessage" class="message-reply-svg"> <div v-if="props.replyMessage" class="message-reply-svg">
@ -27,12 +27,12 @@
:text="props.replyMessage?.message" :text="props.replyMessage?.message"
:reply-id="props.replyMessage.uuid" max-width="reply" /> :reply-id="props.replyMessage.uuid" max-width="reply" />
<div class="left-column"> <div class="left-column">
<Avatar :profile="props.message.member" class="message-author-avatar" @contextmenu="showContextMenu($event, contextMenu, memberMenuSections)" /> <Avatar :profile="props.message.member" class="message-author-avatar" @contextmenu="showContextMenu($event, memberMenuSections)" />
</div> </div>
<div class="message-data"> <div class="message-data">
<div class="message-metadata"> <div class="message-metadata">
<span class="message-author-username" tabindex="0" :style="`color: ${generateIrcColor(props.message.member.user.uuid)}`" <span class="message-author-username" tabindex="0" :style="`color: ${generateIrcColor(props.message.member.user.uuid)}`"
@contextmenu="showContextMenu($event, contextMenu, memberMenuSections)"> @contextmenu="showContextMenu($event, memberMenuSections)">
{{ getDisplayName(props.message.member) }} {{ getDisplayName(props.message.member) }}
</span> </span>
<span class="message-date" :title="date.toString()"> <span class="message-date" :title="date.toString()">
@ -46,7 +46,7 @@
<MessageMedia v-if="mediaLinks.length" :links="mediaLinks" /> <MessageMedia v-if="mediaLinks.length" :links="mediaLinks" />
</div> </div>
</div> </div>
<div v-else ref="messageElement" @contextmenu="showContextMenu($event, contextMenu, messageMenuSections)" <div v-else ref="messageElement" @contextmenu="showContextMenu($event, messageMenuSections)"
class="message grouped-message" :class="{ 'mentioned': props.replyMessage || props.isMentioned }" class="message grouped-message" :class="{ 'mentioned': props.replyMessage || props.isMentioned }"
:data-message-id="props.message.uuid" :editing.sync="props.editing"> :data-message-id="props.message.uuid" :editing.sync="props.editing">
<div class="left-column"> <div class="left-column">
@ -60,7 +60,7 @@
</div> </div>
</div> </div>
<ModalConfirmation v-if="confirmationModal && confirmationModal.show" :action-name="confirmationModal.actionName" <ModalConfirmation v-if="confirmationModal && confirmationModal.show" :action-name="confirmationModal.actionName"
:display-name="getDisplayName(props.message.member)" :callback="confirmationModal.callback" :target-name="getDisplayName(props.message.member)" :callback="confirmationModal.callback"
:onClose="resetConfirmationModal" :onCancel="resetConfirmationModal" /> :onClose="resetConfirmationModal" :onCancel="resetConfirmationModal" />
</template> </template>
@ -70,7 +70,7 @@ import { parse } from 'marked';
import type { MessageProps } from '~/types/props'; import type { MessageProps } from '~/types/props';
import MessageMedia from './MessageMedia.vue'; import MessageMedia from './MessageMedia.vue';
import MessageReply from './UserInterface/MessageReply.vue'; import MessageReply from './UserInterface/MessageReply.vue';
import type { ContextMenuInterface, ContextMenuItem, ContextMenuSection, IConfirmationModal } from '~/types/interfaces'; import type { ContextMenuSection, IConfirmationModal } from '~/types/interfaces';
const { getDisplayName } = useProfile() const { getDisplayName } = useProfile()
const { getUser } = useAuth() const { getUser } = useAuth()
@ -81,8 +81,6 @@ const props = defineProps<MessageProps>();
const me = await getUser() const me = await getUser()
const contextMenu = useState<ContextMenuInterface>("contextMenu", () => ({ show: false, pointerX: 0, pointerY: 0, sections: [] }));
const messageElement = ref<HTMLDivElement>(); const messageElement = ref<HTMLDivElement>();
const dateHidden = ref<boolean>(true); const dateHidden = ref<boolean>(true);
@ -193,7 +191,9 @@ if (props.message.member.user.uuid == me!.uuid) {
regularSection.items.push({ name: "Delete (WIP)", icon: "lucide:trash", type: "danger", callback: () => {} }); regularSection.items.push({ name: "Delete (WIP)", icon: "lucide:trash", type: "danger", callback: () => {} });
} }
if (regularSection.items.length) {
messageMenuSections.push(regularSection); messageMenuSections.push(regularSection);
}
function getDayDifference(date1: Date, date2: Date) { function getDayDifference(date1: Date, date2: Date) {
const midnight1 = new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()); const midnight1 = new Date(date1.getFullYear(), date1.getMonth(), date1.getDate());

View file

@ -1,5 +1,5 @@
<template> <template>
<div ref="resizableSidebar" class="resizable-sidebar" @contextmenu="showContextMenu($event, contextMenu, menuSections)" <div ref="resizableSidebar" class="resizable-sidebar"
:style="{ :style="{
'width': storedWidth ? storedWidth : props.width, 'width': storedWidth ? storedWidth : props.width,
'min-width': props.minWidth, 'min-width': props.minWidth,
@ -8,13 +8,15 @@
'border-top': props.borderSides?.includes('top') ? borderStyling : undefined, 'border-top': props.borderSides?.includes('top') ? borderStyling : undefined,
'border-bottom': props.borderSides?.includes('bottom') ? borderStyling : undefined, 'border-bottom': props.borderSides?.includes('bottom') ? borderStyling : undefined,
}"> }">
<div v-if="props.borderSides != 'right'" class="width-resizer-bar"> <div v-if="props.borderSides != 'right'" class="width-resizer-bar"
@contextmenu="showContextMenu($event, menuSections)">
<div ref="widthResizer" class="width-resizer"></div> <div ref="widthResizer" class="width-resizer"></div>
</div> </div>
<div class="sidebar-content"> <div class="sidebar-content">
<slot /> <slot />
</div> </div>
<div v-if="props.borderSides == 'right'" class="width-resizer-bar"> <div v-if="props.borderSides == 'right'" class="width-resizer-bar"
@contextmenu="showContextMenu($event, menuSections)">
<div ref="widthResizer" class="width-resizer"></div> <div ref="widthResizer" class="width-resizer"></div>
</div> </div>
</div> </div>
@ -31,8 +33,6 @@ const resizableSidebar = ref<HTMLDivElement>();
const widthResizer = ref<HTMLDivElement>(); const widthResizer = ref<HTMLDivElement>();
const storedWidth = ref<string>(); const storedWidth = ref<string>();
const contextMenu = useState<ContextMenuInterface>("contextMenu");
const menuSections: ContextMenuSection[] = [{ const menuSections: ContextMenuSection[] = [{
items: [ items: [
{ {
@ -53,6 +53,7 @@ onMounted(() => {
if (resizableSidebar.value && widthResizer.value) { if (resizableSidebar.value && widthResizer.value) {
widthResizer.value.addEventListener("pointerdown", (e) => { widthResizer.value.addEventListener("pointerdown", (e) => {
if (e.button != 0) return;
e.preventDefault(); e.preventDefault();
document.body.style.cursor = "ew-resize"; document.body.style.cursor = "ew-resize";
function handleMove(pointer: PointerEvent) { function handleMove(pointer: PointerEvent) {

View file

@ -16,6 +16,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import type { GuildMemberResponse } from '~/types/interfaces';
const route = useRoute(); const route = useRoute();
const { fetchGuild, fetchChannel } = useApi() const { fetchGuild, fetchChannel } = useApi()
@ -28,6 +30,13 @@ const channelUrlPath = `channels/${channelId}`;
const guild = await fetchGuild(guildId) const guild = await fetchGuild(guildId)
const channel = await fetchChannel(channelId) const channel = await fetchChannel(channelId)
const { fetchMeMember } = useApi();
const me = useState<GuildMemberResponse | undefined>("me");
if (!me.value || me.value.guild_uuid != guildId) {
const fetchedMe = await fetchMeMember(guildId);
me.value = fetchedMe;
}
// function toggleInvitePopup(e: Event) { // function toggleInvitePopup(e: Event) {
// e.preventDefault(); // e.preventDefault();
// showInvitePopup.value = !showInvitePopup.value; // showInvitePopup.value = !showInvitePopup.value;

View file

@ -8,12 +8,7 @@ export default async (member: GuildMemberResponse, guildId: string, confirmation
items: [] items: []
}; };
const { fetchMeMember } = useApi();
const me = useState<GuildMemberResponse | undefined>("me"); const me = useState<GuildMemberResponse | undefined>("me");
if (!me.value) {
const fetchedMe = await fetchMeMember(member.guild_uuid);
me.value = fetchedMe;
}
const { banMember, kickMember } = useApi(); const { banMember, kickMember } = useApi();
console.log("[MENUITEM] hi"); console.log("[MENUITEM] hi");
@ -66,7 +61,9 @@ export default async (member: GuildMemberResponse, guildId: string, confirmation
} }
} }
if (moderationSection.items.length) {
menuSections.push(moderationSection); menuSections.push(moderationSection);
}
console.log("[MENUITEM] returning menu items:", menuSections); console.log("[MENUITEM] returning menu items:", menuSections);
return menuSections; return menuSections;

View file

@ -2,16 +2,21 @@ import { render } from "vue";
import ContextMenu from "~/components/UserInterface/ContextMenu.vue"; import ContextMenu from "~/components/UserInterface/ContextMenu.vue";
import type { ContextMenuInterface, ContextMenuItem, ContextMenuSection } from "~/types/interfaces"; import type { ContextMenuInterface, ContextMenuItem, ContextMenuSection } from "~/types/interfaces";
export default (e: MouseEvent | PointerEvent, contextMenu: ContextMenuInterface, sections: ContextMenuSection[]) => { export default (e: MouseEvent | PointerEvent, sections: ContextMenuSection[]) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
const contextMenu = useState<ContextMenuInterface>("contextMenu");
if (contextMenu.value.show) {
removeContextMenu(contextMenu);
} else {
console.log("Menu sections:", sections); console.log("Menu sections:", sections);
if (sections.length) { if (sections.length) {
console.log("Showing context menu"); console.log("Showing context menu");
contextMenu.show = true; contextMenu.value.show = true;
contextMenu.pointerX = e.clientX; contextMenu.value.pointerX = e.clientX;
contextMenu.pointerY = e.clientY; contextMenu.value.pointerY = e.clientY;
contextMenu.sections = sections; contextMenu.value.sections = sections;
console.log("Showed"); console.log("Showed");
} }
} }
}