Merge pull request 'guild-settings' (#35) from guild-settings into main
All checks were successful
ci/woodpecker/push/build-and-publish Pipeline was successful
All checks were successful
ci/woodpecker/push/build-and-publish Pipeline was successful
Reviewed-on: #35
This commit is contained in:
commit
2299d3a17a
12 changed files with 573 additions and 76 deletions
7
app.vue
7
app.vue
|
@ -25,6 +25,13 @@ onMounted(() => {
|
|||
if (e.target instanceof HTMLElement && e.target.classList.contains("message-text") && e.target.contentEditable) {
|
||||
e.target.contentEditable = "false";
|
||||
}
|
||||
const destroyOnClick = document.getElementsByClassName("destroy-on-click");
|
||||
for (const element of destroyOnClick) {
|
||||
const closest = (e.target as HTMLElement).closest(".destroy-on-click");
|
||||
if (element != closest) {
|
||||
unrender(element);
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener("keyup", (e) => {
|
||||
const messageReply = document.getElementById("message-reply") as HTMLDivElement;
|
||||
|
|
13
components/Banner.vue
Normal file
13
components/Banner.vue
Normal file
|
@ -0,0 +1,13 @@
|
|||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
46
components/Dropdown.vue
Normal file
46
components/Dropdown.vue
Normal file
|
@ -0,0 +1,46 @@
|
|||
<template>
|
||||
<div class="dropdown-body">
|
||||
<div v-for="option of props.options" class="dropdown-option">
|
||||
<button class="dropdown-button" :data-value="option.value" @click.prevent="option.callback" tabindex="0">{{ option.name }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { DropdownOption } from '~/types/interfaces';
|
||||
|
||||
const props = defineProps<{ options: DropdownOption[] }>();
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.dropdown-body {
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
left: 4dvw;
|
||||
bottom: 4dvh;
|
||||
background-color: var(--chat-background-color);
|
||||
width: 8rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.dropdown-option {
|
||||
border: .09rem solid rgb(70, 70, 70);
|
||||
}
|
||||
|
||||
.dropdown-button {
|
||||
padding-top: .5dvh;
|
||||
padding-bottom: .5dvh;
|
||||
color: var(--text-color);
|
||||
background-color: transparent;
|
||||
width: 100%;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.dropdown-button:hover {
|
||||
background-color: var(--padding-color);
|
||||
}
|
||||
|
||||
</style>
|
59
components/GuildOptionsMenu.vue
Normal file
59
components/GuildOptionsMenu.vue
Normal file
|
@ -0,0 +1,59 @@
|
|||
<template>
|
||||
<div id="guild-options-menu" class="destroy-on-click">
|
||||
<div v-for="setting of settings" class="guild-option" tabindex="0">
|
||||
<button class="guild-option-button" @click="setting.action" tabindex="0">{{ setting.name }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { render } from 'vue';
|
||||
import InviteModal from './InviteModal.vue';
|
||||
|
||||
const settings = [
|
||||
{ name: "Invite", icon: "lucide:letter", action: openInviteModal }
|
||||
]
|
||||
|
||||
function openInviteModal() {
|
||||
const div = document.createElement("div");
|
||||
const guildId = useRoute().params.serverId as string;
|
||||
console.log("guild id:", guildId);
|
||||
const inviteModal = h(InviteModal, { guildId });
|
||||
document.body.appendChild(div);
|
||||
render(inviteModal, div);
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#guild-options-menu {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
background-color: var(--chat-background-color);
|
||||
top: 8dvh;
|
||||
z-index: 10;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.guild-option {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 2em;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.guild-option:hover {
|
||||
background-color: var(--padding-color);
|
||||
}
|
||||
|
||||
.guild-option-button {
|
||||
border: 0;
|
||||
background-color: transparent;
|
||||
color: var(--main-text-color);
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
70
components/InviteModal.vue
Normal file
70
components/InviteModal.vue
Normal file
|
@ -0,0 +1,70 @@
|
|||
<template>
|
||||
<Modal v-bind="props" :title="props.title || 'Create an invite'">
|
||||
<div v-if="invite" id="invite-body">
|
||||
<div id="invite-label">{{ invite }}</div>
|
||||
<div id="invite-buttons">
|
||||
<Button text="Copy as link" variant="neutral" :callback="() => copyInvite('link')" />
|
||||
<Button text="Copy as code" variant="neutral" :callback="() => copyInvite('code')" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<Button text="Generate Invite" variant="normal" :callback="generateInvite">Generate Invite</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { InviteResponse, ModalProps } from '~/types/interfaces';
|
||||
import Button from './UserInterface/Button.vue';
|
||||
|
||||
const props = defineProps<ModalProps & { guildId: string }>();
|
||||
|
||||
const invite = ref<string>();
|
||||
|
||||
async function generateInvite(): Promise<void> {
|
||||
const chars = "ABCDEFGHIJKLMNOQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890"
|
||||
let randCode = "";
|
||||
for (let i = 0; i < 6; i++) {
|
||||
randCode += chars[Math.floor(Math.random() * chars.length)];
|
||||
}
|
||||
const createdInvite: InviteResponse | undefined = await fetchWithApi(
|
||||
`/guilds/${props.guildId}/invites`,
|
||||
{ method: "POST", body: { custom_id: randCode } }
|
||||
);
|
||||
|
||||
invite.value = createdInvite?.id;
|
||||
return;
|
||||
}
|
||||
|
||||
function copyInvite(type: "link" | "code") {
|
||||
if (!invite.value) return;
|
||||
|
||||
if (type == "link") {
|
||||
const inviteUrl = URL.parse(`invite/${invite.value}`, `${window.location.protocol}//${window.location.host}`);
|
||||
if (inviteUrl) {
|
||||
navigator.clipboard.writeText(inviteUrl.href);
|
||||
}
|
||||
} else {
|
||||
navigator.clipboard.writeText(invite.value);
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
#invite-body, #invite-buttons {
|
||||
display: flex;
|
||||
gap: 1em;
|
||||
}
|
||||
|
||||
#invite-body {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#invite-label {
|
||||
text-align: center;
|
||||
color: aquamarine;
|
||||
}
|
||||
|
||||
</style>
|
84
components/Modal.vue
Normal file
84
components/Modal.vue
Normal file
|
@ -0,0 +1,84 @@
|
|||
<template>
|
||||
<dialog ref="dialog" class="modal" :class="props.obscure ? 'modal-obscure' : 'modal-regular'">
|
||||
<span class="modal-exit-button-container" style="position: absolute; right: 2em; top: .2em; width: .5em; height: .5em;">
|
||||
<Button text="X" variant="neutral" :callback="() => dialog?.remove()" />
|
||||
</span>
|
||||
<div class="modal-content">
|
||||
<h1 class="modal-title">{{ title }}</h1>
|
||||
<slot />
|
||||
</div>
|
||||
</dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { ModalProps } from '~/types/interfaces';
|
||||
import Button from './UserInterface/Button.vue';
|
||||
|
||||
const props = defineProps<ModalProps>();
|
||||
const dialog = ref<HTMLDialogElement>();
|
||||
|
||||
console.log("props:", props);
|
||||
|
||||
onMounted(() => {
|
||||
if (dialog.value) {
|
||||
dialog.value.showModal();
|
||||
if (props.onClose) {
|
||||
dialog.value.addEventListener("close", props.onClose);
|
||||
}
|
||||
if (props.onCancel) {
|
||||
dialog.value.addEventListener("cancel", props.onCancel);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.modal {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
opacity: 100%;
|
||||
padding: 1%;
|
||||
background-color: var(--sidebar-highlighted-background-color);
|
||||
color: var(--text-color);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.modal-regular::backdrop {
|
||||
background-color: var(--chat-background-color);
|
||||
opacity: 0%;
|
||||
}
|
||||
|
||||
.modal-obscure::backdrop {
|
||||
background-color: var(--chat-background-color);
|
||||
opacity: 80%;
|
||||
}
|
||||
|
||||
.modal-top-container {
|
||||
position: fixed;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
font-size: 1.5rem;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 1em;
|
||||
margin: 1em;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -1,40 +0,0 @@
|
|||
<template>
|
||||
<div id="invite-popup">
|
||||
<div v-if="invite">
|
||||
<p>{{ invite }}</p>
|
||||
<button @click="copyInvite">Copy Link</button>
|
||||
</div>
|
||||
<div v-else>
|
||||
<button @click="generateInvite">Generate Invite</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { InviteResponse } from '~/types/interfaces';
|
||||
|
||||
|
||||
const invite = ref<string>();
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
async function generateInvite(): Promise<void> {
|
||||
const createdInvite: InviteResponse | undefined = await fetchWithApi(
|
||||
`/guilds/${route.params.serverId}/invites`,
|
||||
{ method: "POST", body: { custom_id: "oijewfoiewf" } }
|
||||
);
|
||||
|
||||
invite.value = createdInvite?.id;
|
||||
return;
|
||||
}
|
||||
|
||||
function copyInvite() {
|
||||
const inviteUrl = URL.parse(`invite/${invite.value}`, `${window.location.protocol}//${window.location.host}`);
|
||||
navigator.clipboard.writeText(inviteUrl!.href);
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
|
@ -53,6 +53,18 @@ export const useApi = () => {
|
|||
return await fetchWithApi(`/channels/${channelId}/messages/${messageId}`);
|
||||
}
|
||||
|
||||
async function createGuild(name: string): Promise<GuildResponse | undefined> {
|
||||
return await fetchWithApi(`/guilds`, { method: "POST", body: { name } });
|
||||
}
|
||||
|
||||
async function joinGuild(invite: string): Promise<GuildResponse> {
|
||||
return await fetchWithApi(`/invites/${invite}`, { method: "POST" }) as GuildResponse;
|
||||
}
|
||||
|
||||
async function createChannel(guildId: string, name: string, description?: string): Promise<void> {
|
||||
return await fetchWithApi(`/guilds/${guildId}/channels`, { method: "POST", body: { name, description } });
|
||||
}
|
||||
|
||||
async function fetchInstanceStats(apiBase: string): Promise<StatsResponse> {
|
||||
return await $fetch(`${apiBase}/stats`, { method: "GET" });
|
||||
}
|
||||
|
@ -76,6 +88,9 @@ export const useApi = () => {
|
|||
removeFriend,
|
||||
fetchMessages,
|
||||
fetchMessage,
|
||||
createGuild,
|
||||
joinGuild,
|
||||
createChannel,
|
||||
fetchInstanceStats,
|
||||
sendVerificationEmail
|
||||
}
|
||||
|
|
|
@ -10,18 +10,27 @@
|
|||
</div>
|
||||
<div id = "page-content">
|
||||
<div id="left-column">
|
||||
<NuxtLink id="home-button" href="/me">
|
||||
<img class="sidebar-icon" src="/public/icon.svg"/>
|
||||
</NuxtLink>
|
||||
<div id="servers-list">
|
||||
<NuxtLink v-for="guild of guilds" :href="`/servers/${guild.uuid}`">
|
||||
<img v-if="guild.icon" class="sidebar-icon" :src="guild.icon" :alt="guild.name"/>
|
||||
<Icon v-else name="lucide:server" class="sidebar-icon white" :alt="guild.name" />
|
||||
<div id="left-column-top">
|
||||
<NuxtLink id="home-button" href="/me">
|
||||
<img class="sidebar-icon" src="/public/icon.svg"/>
|
||||
</NuxtLink>
|
||||
<div id="servers-list">
|
||||
<NuxtLink v-for="guild of guilds" :href="`/servers/${guild.uuid}`">
|
||||
<img v-if="guild.icon" class="sidebar-icon" :src="guild.icon" :alt="guild.name"/>
|
||||
<Icon v-else name="lucide:server" class="sidebar-icon white" :alt="guild.name" />
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
<div id="left-column-bottom">
|
||||
<div ref="createButtonContainer">
|
||||
<button id="create-button" @click.prevent="createDropdown">
|
||||
<Icon id="create-icon" name="lucide:square-plus" />
|
||||
</button>
|
||||
</div>
|
||||
<NuxtLink id="settings-menu" href="/settings">
|
||||
<Icon name="lucide:settings" class="sidebar-icon" alt="Settings menu" />
|
||||
</NuxtLink>
|
||||
</div>
|
||||
<NuxtLink id="settings-menu" href="/settings">
|
||||
<Icon name="lucide:settings" class="sidebar-icon" alt="Settings menu" />
|
||||
</NuxtLink>
|
||||
</div>
|
||||
<slot />
|
||||
</div>
|
||||
|
@ -29,11 +38,165 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { render } from 'vue';
|
||||
import Dropdown from '~/components/Dropdown.vue';
|
||||
import Modal from '~/components/Modal.vue';
|
||||
import Button from '~/components/UserInterface/Button.vue';
|
||||
import type { GuildResponse } from '~/types/interfaces';
|
||||
|
||||
const loading = useState("loading", () => false);
|
||||
|
||||
const createButtonContainer = ref<HTMLButtonElement>();
|
||||
|
||||
const api = useApi();
|
||||
|
||||
const options = [
|
||||
{ name: "Join", value: "join", callback: async () => {
|
||||
console.log("join guild!");
|
||||
const div = document.createElement("div");
|
||||
const guildJoinModal = h(Modal, {
|
||||
title: "Join Guild",
|
||||
id: "guild-join-modal",
|
||||
onClose: () => {
|
||||
unrender(div);
|
||||
},
|
||||
onCancel: () => {
|
||||
unrender(div);
|
||||
},
|
||||
style: "height: 20dvh; width: 15dvw"
|
||||
},
|
||||
[
|
||||
h("input", {
|
||||
id: "guild-invite-input",
|
||||
type: "text",
|
||||
placeholder: "oyqICZ",
|
||||
}),
|
||||
h(Button, {
|
||||
text: "Join",
|
||||
variant: "normal",
|
||||
callback: async () => {
|
||||
const input = document.getElementById("guild-invite-input") as HTMLInputElement;
|
||||
const invite = input.value;
|
||||
if (invite.length == 6) {
|
||||
try {
|
||||
const joinedGuild = await api.joinGuild(invite);
|
||||
guilds?.push(joinedGuild);
|
||||
return await navigateTo(`/servers/${joinedGuild.uuid}`);
|
||||
} catch (error) {
|
||||
alert(`Couldn't use invite: ${error}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
]);
|
||||
document.body.appendChild(div);
|
||||
render(guildJoinModal, div);
|
||||
}
|
||||
},
|
||||
{ name: "Create", value: "create", callback: async () => {
|
||||
console.log("create guild");
|
||||
const user = await useAuth().getUser();
|
||||
const div = document.createElement("div");
|
||||
const guildCreateModal = h(Modal, {
|
||||
title: "Create a Guild",
|
||||
id: "guild-join-modal",
|
||||
onClose: () => {
|
||||
unrender(div);
|
||||
},
|
||||
onCancel: () => {
|
||||
unrender(div);
|
||||
},
|
||||
style: "height: 20dvh; width: 15dvw;"
|
||||
},
|
||||
[
|
||||
h("input", {
|
||||
id: "guild-name-input",
|
||||
type: "text",
|
||||
placeholder: `${user?.display_name || user?.username}'s Awesome Bouncy Castle'`,
|
||||
style: "width: 100%"
|
||||
}),
|
||||
h(Button, {
|
||||
text: "Create!",
|
||||
variant: "normal",
|
||||
callback: async () => {
|
||||
const input = document.getElementById("guild-name-input") as HTMLInputElement;
|
||||
const name = input.value;
|
||||
try {
|
||||
const guild = (await api.createGuild(name)) as GuildResponse;
|
||||
await api.createChannel(guild.uuid, "general");
|
||||
} catch (error) {
|
||||
alert(`Couldn't create guild: ${error}`);
|
||||
}
|
||||
}
|
||||
})
|
||||
]);
|
||||
document.body.appendChild(div);
|
||||
render(guildCreateModal, div);
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const guilds: GuildResponse[] | undefined = await fetchWithApi("/me/guilds");
|
||||
|
||||
//const servers = await fetchWithApi("/servers") as { uuid: string, name: string, description: string }[];
|
||||
//console.log("servers:", servers);
|
||||
const members = [
|
||||
{
|
||||
id: "3287484395",
|
||||
displayName: "SauceyRed"
|
||||
},
|
||||
{
|
||||
id: "3287484395",
|
||||
displayName: "SauceyRed"
|
||||
},
|
||||
{
|
||||
id: "3287484395",
|
||||
displayName: "SauceyRed"
|
||||
},
|
||||
{
|
||||
id: "3287484395",
|
||||
displayName: "SauceyRed"
|
||||
},
|
||||
{
|
||||
id: "3287484395",
|
||||
displayName: "SauceyRed"
|
||||
},
|
||||
{
|
||||
id: "3287484395",
|
||||
displayName: "SauceyRed"
|
||||
},
|
||||
{
|
||||
id: "3287484395",
|
||||
displayName: "SauceyRed"
|
||||
},
|
||||
{
|
||||
id: "3287484395",
|
||||
displayName: "SauceyRed"
|
||||
},
|
||||
{
|
||||
id: "3287484395",
|
||||
displayName: "SauceyRed"
|
||||
}
|
||||
];
|
||||
|
||||
function createDropdown() {
|
||||
const dropdown = h(Dropdown, { options });
|
||||
const div = document.createElement("div");
|
||||
div.classList.add("dropdown", "destroy-on-click");
|
||||
if (createButtonContainer.value) {
|
||||
createButtonContainer.value.appendChild(div);
|
||||
} else {
|
||||
document.body.appendChild(div);
|
||||
}
|
||||
render(dropdown, div);
|
||||
div.addEventListener("keyup", (e) => {
|
||||
if (e.key == "Escape") {
|
||||
unrender(div);
|
||||
}
|
||||
});
|
||||
div.focus();
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
@ -80,6 +243,8 @@ const guilds: GuildResponse[] | undefined = await fetchWithApi("/me/guilds");
|
|||
#left-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: .75em;
|
||||
padding-left: .25em;
|
||||
padding-right: .25em;
|
||||
|
@ -89,6 +254,31 @@ const guilds: GuildResponse[] | undefined = await fetchWithApi("/me/guilds");
|
|||
background-color: var(--sidebar-background-color);
|
||||
}
|
||||
|
||||
#left-column-top, #left-column-bottom {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 1.5dvh;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
#left-column-bottom {
|
||||
padding-top: 1dvh;
|
||||
border-top: 1px solid var(--padding-color);
|
||||
}
|
||||
|
||||
#middle-left-column {
|
||||
padding-left: 1dvw;
|
||||
padding-right: 1dvw;
|
||||
border-right: 1px solid var(--padding-color);
|
||||
}
|
||||
|
||||
#home-button {
|
||||
border-bottom: 1px solid var(--padding-color);
|
||||
padding-bottom: 1dvh;
|
||||
}
|
||||
|
||||
#servers-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -97,6 +287,20 @@ const guilds: GuildResponse[] | undefined = await fetchWithApi("/me/guilds");
|
|||
padding-top: .5em;
|
||||
}
|
||||
|
||||
#create-button {
|
||||
color: var(--primary-color);
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: 2rem;
|
||||
padding: 0;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#create-icon {
|
||||
float: left;
|
||||
}
|
||||
|
||||
#middle-left-column {
|
||||
padding-left: .25em;
|
||||
padding-right: .25em;
|
||||
|
@ -120,9 +324,6 @@ const guilds: GuildResponse[] | undefined = await fetchWithApi("/me/guilds");
|
|||
}
|
||||
|
||||
#settings-menu {
|
||||
position: absolute;
|
||||
bottom: .25em;
|
||||
|
||||
color: var(--primary-color)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,21 +1,12 @@
|
|||
<template>
|
||||
<NuxtLayout name="client">
|
||||
<div id="middle-left-column" class="main-grid-row">
|
||||
<div id="server-title">
|
||||
<h3>
|
||||
{{ server?.name }}
|
||||
<span>
|
||||
<button @click="showGuildSettings">
|
||||
<Icon name="lucide:settings" />
|
||||
</button>
|
||||
</span>
|
||||
<span>
|
||||
<button @click="toggleInvitePopup">
|
||||
<Icon name="lucide:share-2" />
|
||||
</button>
|
||||
</span>
|
||||
<InvitePopup v-if="showInvitePopup" />
|
||||
</h3>
|
||||
<div id="server-name-container">
|
||||
<span id="server-name">{{ server?.name }}</span>
|
||||
<button id="server-settings-button" @click="toggleGuildSettings">
|
||||
<Icon id="server-settings-icon" name="lucide:chevron-down" />
|
||||
</button>
|
||||
<GuildOptionsMenu v-if="showGuildSettings" />
|
||||
</div>
|
||||
<div id="channels-list">
|
||||
<ChannelEntry v-for="channel of channels" :name="channel.name"
|
||||
|
@ -45,7 +36,10 @@ const server = ref<GuildResponse | undefined>();
|
|||
const channels = ref<ChannelResponse[] | undefined>();
|
||||
const channel = ref<ChannelResponse | undefined>();
|
||||
|
||||
const members = ref<GuildMemberResponse[]>();
|
||||
|
||||
const showInvitePopup = ref(false);
|
||||
const showGuildSettings = ref(false);
|
||||
|
||||
import type { ChannelResponse, GuildMemberResponse, GuildResponse, MessageResponse } from "~/types/interfaces";
|
||||
|
||||
|
@ -53,23 +47,34 @@ import type { ChannelResponse, GuildMemberResponse, GuildResponse, MessageRespon
|
|||
//console.log("channelid: servers:", servers);
|
||||
|
||||
const { fetchMembers } = useApi();
|
||||
const members = await fetchMembers(route.params.serverId as string);
|
||||
|
||||
onMounted(async () => {
|
||||
console.log("channelid: set loading to true");
|
||||
console.log("mounting");
|
||||
const guildUrl = `guilds/${route.params.serverId}`;
|
||||
server.value = await fetchWithApi(guildUrl);
|
||||
await setArrayVariables();
|
||||
});
|
||||
|
||||
onActivated(async () => {
|
||||
console.log("activating");
|
||||
const guildUrl = `guilds/${route.params.serverId}`;
|
||||
server.value = await fetchWithApi(guildUrl);
|
||||
await setArrayVariables();
|
||||
});
|
||||
|
||||
async function setArrayVariables() {
|
||||
members.value = await fetchMembers(route.params.serverId as string);
|
||||
const guildUrl = `guilds/${route.params.serverId}`;
|
||||
channels.value = await fetchWithApi(`${guildUrl}/channels`);
|
||||
console.log("channels:", channels.value);
|
||||
channel.value = await fetchWithApi(`/channels/${route.params.channelId}`);
|
||||
console.log("channel:", channel.value);
|
||||
}
|
||||
|
||||
console.log("channelid: channel:", channel);
|
||||
console.log("channelid: set loading to false");
|
||||
});
|
||||
|
||||
function showGuildSettings() { }
|
||||
function toggleGuildSettings(e: Event) {
|
||||
e.preventDefault();
|
||||
showGuildSettings.value = !showGuildSettings.value;
|
||||
}
|
||||
|
||||
function toggleInvitePopup(e: Event) {
|
||||
e.preventDefault();
|
||||
|
@ -81,7 +86,6 @@ function handleMemberClick(member: GuildMemberResponse) {
|
|||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
#middle-left-column {
|
||||
padding-left: .5em;
|
||||
padding-right: .5em;
|
||||
|
@ -136,4 +140,23 @@ function handleMemberClick(member: GuildMemberResponse) {
|
|||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
#server-name-container {
|
||||
padding-top: 3dvh;
|
||||
padding-bottom: 3dvh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#server-name {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
#server-settings-button {
|
||||
background-color: transparent;
|
||||
font-size: 1em;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0%;
|
||||
}
|
||||
</style>
|
|
@ -86,6 +86,19 @@ export interface ScrollPosition {
|
|||
offsetLeft: number
|
||||
}
|
||||
|
||||
export interface DropdownOption {
|
||||
name: string,
|
||||
value: string | number,
|
||||
callback: () => void
|
||||
}
|
||||
|
||||
export interface ModalProps {
|
||||
title?: string,
|
||||
obscure?: boolean,
|
||||
onClose?: () => void,
|
||||
onCancel?: () => void
|
||||
}
|
||||
|
||||
export interface ContextMenuItem {
|
||||
name: string,
|
||||
callback: (...args: any[]) => any;
|
||||
|
|
6
utils/unrender.ts
Normal file
6
utils/unrender.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
import { render } from "vue";
|
||||
|
||||
export default (div: Element) => {
|
||||
render(null, div);
|
||||
div.remove();
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue