Files
oxspeak_server/frontend/src/pages/server/index.vue
T
2026-08-08 20:55:01 +02:00

264 lines
7.3 KiB
Vue

<script lang="ts" setup>
import {storeToRefs} from 'pinia'
import {useChannelStore} from '@/stores/channel'
import {useCategoryStore} from '@/stores/category'
import {computed, onMounted, onUnmounted, ref, watch} from 'vue'
import {useRoute} from 'vue-router'
import CreateChannelDialog from '@/components/channel/CreateChannelDialog.vue'
import CreateCategoryDialog from '@/components/category/CreateCategoryDialog.vue'
import {type MenuItem, useContextMenu} from '@/composables/useContextMenu'
import {useUserStore} from "@/stores/user.ts";
import {useServerStore} from "@/stores/server.ts";
import ChannelPermissionsDialog from '@/components/permissions/ChannelPermissionsDialog.vue'
import {useAuthStore} from '@/stores/auth'
import ServerSettingsDialog from '@/components/server/ServerSettingsDialog.vue'
import {onReloadAll} from '@/plugins/events.ts'
const props = defineProps<{
serverId: string
channelId?: string
}>()
const route = useRoute()
const channelStore = useChannelStore()
const categoryStore = useCategoryStore()
const userStore = useUserStore()
const serverStore = useServerStore()
const {currentTree} = storeToRefs(serverStore)
const {openContextMenu} = useContextMenu()
const authStore = useAuthStore()
const showPermissionsDialog = ref(false)
const selectedChannel = ref<any | null>(null)
const showServerSettings = ref(false)
const serverName = computed(() => serverStore.servers.find(server => server.id === props.serverId)?.name || 'Serveur')
const loadServerData = async (targetServerId: string) => {
if (!targetServerId) return
channelStore.reset()
categoryStore.reset()
userStore.reset()
try {
await Promise.all([
userStore.fetchUsers(targetServerId),
serverStore.fetchServerTree(targetServerId)
])
syncOpenedCategories()
} catch (error) {
console.error('Failed to load server-scoped channels and categories:', error)
}
}
let stopReloadAll: (() => void) | null = null
onMounted(() => {
stopReloadAll = onReloadAll(() => loadServerData(props.serverId))
})
onUnmounted(() => {
stopReloadAll?.()
})
watch(
() => props.serverId,
async (newServerId) => {
if (newServerId) {
await loadServerData(newServerId)
}
},
{immediate: true}
)
const showChannelDialog = ref(false)
const showCategoryDialog = ref(false)
const selectedCategoryId = ref<string | null>(null)
const openedCategories = ref<string[]>([])
function syncOpenedCategories() {
openedCategories.value = currentTree.value
.filter((item) => 'Category' in item)
.map((item) => item.Category[0].id)
}
async function refreshServerTree() {
await serverStore.fetchServerTree(props.serverId)
syncOpenedCategories()
}
// Right click menu (sidebar)
function onSidebarContextMenu(event: MouseEvent) {
const menuItems: MenuItem[] = [
{
label: 'Nouveau canal',
icon: 'mdi-plus',
action: () => {
selectedCategoryId.value = null
showChannelDialog.value = true
},
},
{
label: 'Nouvelle catégorie',
icon: 'mdi-folder-plus',
action: () => { showCategoryDialog.value = true },
}
]
openContextMenu(event, menuItems);
}
function onCategoryContextMenu(event: MouseEvent, category: any) {
const menuItems: MenuItem[] = [
{
label: 'Nouveau canal',
icon: 'mdi-plus',
action: () => {
selectedCategoryId.value = category.id
showChannelDialog.value = true
},
},
]
openContextMenu(event, menuItems)
}
// Right click menu (channel)
async function openEditDialog(channel: any) {
console.log("edit dialog clicked")
}
async function deleteChannel(channelId: string) {
console.log("delete channel clicked")
}
function onChannelContextMenu(event: MouseEvent, channel: any) {
const menuItems: MenuItem[] = [
...(authStore.isAdmin ? [{
label: 'Gérer les permissions',
icon: 'mdi-shield-key',
action: () => {
selectedChannel.value = channel
showPermissionsDialog.value = true
},
}] : []),
{
label: 'Marquer comme lu',
icon: 'mdi-check',
action: () => console.log('Marqué comme lu', channel.id),
},
{
label: 'Modifier le canal',
icon: 'mdi-pencil',
action: () => openEditDialog(channel),
},
{
label: 'Supprimer le canal',
icon: 'mdi-delete',
color: 'error',
action: () => deleteChannel(channel.id),
},
]
openContextMenu(event, menuItems);
}
</script>
<template>
<v-navigation-drawer width="244" @contextmenu="onSidebarContextMenu">
<v-sheet color="grey-lighten-5" height="128" width="100%" class="pa-3">
<v-btn block variant="text" prepend-icon="mdi-cog" @click="showServerSettings = true">Gérer le serveur</v-btn>
</v-sheet>
<v-list
v-model:opened="openedCategories"
open-strategy="multiple"
density="compact"
>
<template v-for="(item, index) in currentTree" :key="index">
<!-- Catégorie et ses canaux enfants -->
<v-list-group v-if="'Category' in item" :value="item.Category[0].id">
<template #activator="{ props: groupProps }">
<v-list-item
:title="item.Category[0].name"
v-bind="groupProps"
@contextmenu="onCategoryContextMenu($event, item.Category[0])"
/>
</template>
<v-list-item
v-for="channel in item.Category[1]"
:key="channel.id"
:title="channel.name"
:to="`/server/${serverId}/channel/${channel.id}`"
:class="{ 'font-weight-bold': (channel.unread_count ?? 0) > 0 }"
link
@contextmenu="onChannelContextMenu($event, channel)"
>
<template #append>
<v-chip
v-if="(channel.unread_count ?? 0) > 0"
color="primary"
density="compact"
size="small"
variant="flat"
>
{{ channel.unread_count }}
</v-chip>
</template>
</v-list-item>
</v-list-group>
<!-- Canal orphelin (racine) -->
<v-list-item
v-else-if="'Channel' in item"
:key="item.Channel.id"
:title="item.Channel.name"
:to="`/server/${serverId}/channel/${item.Channel.id}`"
:class="{ 'font-weight-bold': (item.Channel.unread_count ?? 0) > 0 }"
link
@contextmenu="onChannelContextMenu($event, item.Channel)"
>
<template #append>
<v-chip
v-if="(item.Channel.unread_count ?? 0) > 0"
color="primary"
density="compact"
size="small"
variant="flat"
>
{{ item.Channel.unread_count }}
</v-chip>
</template>
</v-list-item>
</template>
</v-list>
</v-navigation-drawer>
<CreateChannelDialog
v-model="showChannelDialog"
:category-id="selectedCategoryId"
:server-id="serverId"
@created="refreshServerTree"
/>
<CreateCategoryDialog
v-model="showCategoryDialog"
:server-id="serverId"
@created="refreshServerTree"
/>
<ChannelPermissionsDialog
v-model="showPermissionsDialog"
:channel="selectedChannel"
:server-id="serverId"
/>
<ServerSettingsDialog
v-model="showServerSettings"
:server-id="serverId"
:server-name="serverName"
/>
<v-main>
<router-view/>
</v-main>
</template>