Refactor the client to implement a channel navbar #76

Open
twig wants to merge 23 commits from navbar into main
4 changed files with 227 additions and 76 deletions
Showing only changes of commit 28f5e8dc27 - Show all commits

View file

@ -0,0 +1,124 @@
<template>
<div id="navbar">
<div v-for="entry of props.clientItems" id="navbar-left">
<button class="navbar-item" :title="entry.title"
@click.prevent="entry.callback()">
<Icon :name="entry.icon" class="navbar-item-icon" />
</button>
</div>
<div id="navbar-middle">
<NuxtImg v-if="props.contextIcon"
class="context-icon"
:alt="props.contextName"
:src="props.contextIcon" />
<DefaultIcon v-else-if="props.contextName && props.guildUuid"
class="context-icon"
:alt="props.contextName"
:name="props.contextName" :seed="props.guildUuid"/>
<div class="context-title">
{{ props.contextName }}
</div>
</div>
<div v-for="entry of props.channelItems" id="navbar-right">
<button class="navbar-item" :title="entry.title"
@click.prevent="entry.callback()">
<Icon :name="entry.icon" class="navbar-item-icon" />
</button>
</div>
</div>
</template>
<script lang="ts" setup>
import type { NavbarInterface, NavbarItem } from '~/types/interfaces';
const props = defineProps<NavbarInterface>();
</script>
<style scoped>
#navbar {
--navbar-height: 5dvh;
--navbar-icon-size: 3dvh;
--navbar-gap: calc(3dvh * .2);
--side-margins: calc(.6dvw + .35dvh); /* try to make it reasonable at any aspect ratio */
twig marked this conversation as resolved Outdated

Shouldn't these variable definitions be defined in theme files?

Shouldn't these variable definitions be defined in theme files?

i suppose? they're really just there to make development easier, but they could be included in the layout theme files, if so, do we include all 4?

i suppose? they're really just there to make development easier, but they could be included in the layout theme files, if so, do we include all 4?

Yeah, don't see a reason not to

Yeah, don't see a reason not to
left: 0;
right: 0;
top: 0;
min-height: var(--navbar-height);
max-height: var(--navbar-height);
width: 100%;
display: flex;
justify-content: center;
background: var(--optional-topbar-background);
background-color: var(--topbar-background-color);
border-bottom: 1px solid var(--padding-color);
}
#navbar-left,
#navbar-middle,
#navbar-right {
top: 0;
height: var(--navbar-height);
display: inline-flex;
justify-content: center;
align-items: center;
gap: var(--navbar-gap);
}
#navbar-left {
left: var(--side-margins);
position: absolute;
}
#navbar-middle {
max-width: 50dvw;
}
#navbar-right {
right: var(--side-margins);
position: absolute;
}
.context-icon {
height: calc(var(--navbar-height) * 0.7);
width: calc(var(--navbar-height) * 0.7);
border-radius: var(--guild-icon-radius);
}
.context-title {
min-height: var(--navbar-height);
max-height: var(--navbar-height);
font-weight: 500;
font-size: calc(var(--navbar-height) * .5);
line-height: calc(var(--navbar-height) * .9);
}
.navbar-item {
color: var(--reply-text-color);
background-color: transparent;
border: none;
cursor: pointer;
padding: 0;
transition: color 300ms;
display: flex;
align-items: center;
height: var(--navbar-icon-size);
}
.navbar-item:hover {
color: var(--primary-highlighted-color);
}
.navbar-item-icon {
width: var(--navbar-icon-size);
height: 100%;
}
</style>

View file

@ -1,13 +1,7 @@
<template>
<Loading v-show="loading" />
<div :class="{ hidden: loading, visible: !loading }" id="client-root">
<div id="homebar">
<div class="homebar-item">
<marquee>
gorb!!!!!
</marquee>
</div>
</div>
<Navbar id="navbar" v-bind="navbar" />
<div id="page-content">
<div id="left-column">
<div class="left-column-segment">
@ -52,14 +46,16 @@ import DefaultIcon from '~/components/DefaultIcon.vue';
import GuildDropdown from '~/components/Guild/GuildDropdown.vue';
import Loading from '~/components/Popups/Loading.vue';
import Button from '~/components/UserInterface/Button.vue';
import Navbar from '~/components/UserInterface/Navbar.vue';
import VerticalSpacer from '~/components/UserInterface/VerticalSpacer.vue';
import type { GuildResponse } from '~/types/interfaces';
import type { GuildResponse, NavbarInterface, NavbarItem } from '~/types/interfaces';
definePageMeta({
keepalive: true
});
const loading = useState("loading", () => false);
const navbar = useState<NavbarInterface>("navbar")
const createButtonContainer = ref<HTMLButtonElement>();
twig marked this conversation as resolved Outdated

This should be an anchor element, not a button

This should be an anchor element, not a button

disagree, i'd like to put an inbox here, that would be a popup, not a link

disagree, i'd like to put an inbox here, that would be a popup, not a link

Right but I think all clickable elements that lead you to an external site should be anchor elements. Here I'm specifically talking about the source code element.

Right but I think all clickable elements that lead you to an external site should be anchor elements. Here I'm specifically talking about the source code element.

sure, i'm reworking it anyways

sure, i'm reworking it anyways
@ -108,8 +104,8 @@ const options = [
document.body.appendChild(div);
render(guildJoinModal, div);
}
},
{ name: "Create", value: "create", callback: async () => {
},
{ name: "Create", value: "create", callback: async () => {
console.log("create guild");
const user = await useAuth().getUser();
const div = document.createElement("div");
@ -154,6 +150,23 @@ const options = [
const guilds = await api.fetchMyGuilds();
onMounted(() => {
if (!navbar.value) {
const helpItem = {
title: "Source",
icon: "lucide:code-xml",
callback: () => { open("https://git.gorb.app/gorb/frontend") }
} as NavbarItem
navbar.value = {
clientItems: [
helpItem
],
channelItems: [] // set by the channel
} as NavbarInterface
}
})
function createDropdown() {
const dropdown = h(GuildDropdown, { options });
const div = document.createElement("div");
@ -192,22 +205,6 @@ function createDropdown() {
transition: opacity 500ms;
}
#homebar {
min-height: 4dvh;
display: flex;
justify-content: space-evenly;
align-items: center;
background: var(--optional-topbar-background);
background-color: var(--topbar-background-color);
border-bottom: 1px solid var(--padding-color);
padding-left: 5dvw;
padding-right: 5dvw;
}
.homebar-item {
width: 100dvw;
}
#page-content {
display: flex;
flex-direction: row;

View file

@ -36,11 +36,12 @@ import ChannelEntry from "~/components/Guild/ChannelEntry.vue";
import GuildOptionsMenu from "~/components/Guild/GuildOptionsMenu.vue";
import MemberEntry from "~/components/Guild/MemberEntry.vue";
import ResizableSidebar from "~/components/UserInterface/ResizableSidebar.vue";
import type { ChannelResponse, GuildMemberResponse, GuildResponse } from "~/types/interfaces";
import type { ChannelResponse, GuildMemberResponse, GuildResponse, NavbarInterface } from "~/types/interfaces";
const route = useRoute();
const loading = useState("loading");
const navbar = useState<NavbarInterface>("navbar");
const channelUrlPath = `channels/${route.params.channelId}`;
@ -65,13 +66,18 @@ onMounted(async () => {
console.log("fetched guild");
await setArrayVariables();
console.log("set array variables");
updateNavbar()
});
onActivated(async () => {
console.log("activating");
updateNavbar()
const guildUrl = `guilds/${route.params.serverId}`;
server.value = await fetchWithApi(guildUrl);
console.log("fetched guild");
await setArrayVariables();
console.log("set array variables");
});
@ -98,6 +104,14 @@ function toggleInvitePopup(e: Event) {
function handleMemberClick(member: GuildMemberResponse) {
}
function updateNavbar() {
if (server.value) {
navbar.value.contextName = server.value.name
navbar.value.contextIcon = server.value.icon ?? undefined
navbar.value.guildUuid = server.value.uuid
}
}
</script>
<style>

View file

@ -121,3 +121,19 @@ export interface ContextMenuInterface {
pointerY: number,
items: ContextMenuItem[]
}
export interface NavbarItem {
title: string,
icon: string,
hasPing?: boolean, // whether to draw a "ping" icon or not
callback: (...args: any[]) => any;
}
export interface NavbarInterface {
clientItems: NavbarItem[]
channelItems: NavbarItem[] // search bar will require some changes
contextName?: string
contextIcon?: string
guildUuid?: string
}