Compare commits
7 commits
0f02142eb1
...
8b8c0591a5
Author | SHA1 | Date | |
---|---|---|---|
8b8c0591a5 | |||
e28bc23d12 | |||
839920f124 | |||
99ed210d26 | |||
1d21d476d5 | |||
538566e9e1 | |||
b46533aa5f |
7 changed files with 71 additions and 39 deletions
11
app.vue
11
app.vue
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +1,33 @@
|
||||||
<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) }}
|
||||||
</span>
|
</span>
|
||||||
</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>
|
||||||
|
|
|
@ -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: () => {} });
|
||||||
}
|
}
|
||||||
|
|
||||||
messageMenuSections.push(regularSection);
|
if (regularSection.items.length) {
|
||||||
|
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());
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
menuSections.push(moderationSection);
|
if (moderationSection.items.length) {
|
||||||
|
menuSections.push(moderationSection);
|
||||||
|
}
|
||||||
|
|
||||||
console.log("[MENUITEM] returning menu items:", menuSections);
|
console.log("[MENUITEM] returning menu items:", menuSections);
|
||||||
return menuSections;
|
return menuSections;
|
||||||
|
|
|
@ -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();
|
||||||
console.log("Menu sections:", sections);
|
const contextMenu = useState<ContextMenuInterface>("contextMenu");
|
||||||
if (sections.length) {
|
if (contextMenu.value.show) {
|
||||||
console.log("Showing context menu");
|
removeContextMenu(contextMenu);
|
||||||
contextMenu.show = true;
|
} else {
|
||||||
contextMenu.pointerX = e.clientX;
|
console.log("Menu sections:", sections);
|
||||||
contextMenu.pointerY = e.clientY;
|
if (sections.length) {
|
||||||
contextMenu.sections = sections;
|
console.log("Showing context menu");
|
||||||
console.log("Showed");
|
contextMenu.value.show = true;
|
||||||
}
|
contextMenu.value.pointerX = e.clientX;
|
||||||
|
contextMenu.value.pointerY = e.clientY;
|
||||||
|
contextMenu.value.sections = sections;
|
||||||
|
console.log("Showed");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue