Refactor sidebar

This commit is contained in:
Jack Hadrill 2022-03-20 00:12:03 +00:00
parent b1977447a2
commit c6fb1528ea
12 changed files with 167 additions and 73 deletions

View File

@ -1,42 +1,12 @@
<template> <template>
<div class="container-fluid vh-100"> <div class="container-fluid vh-100">
<div class="row vh-100"> <div class="row vh-100">
<div class="col-2 bg-dark">
<div class="d-flex flex-column vh-100">
<div class="d-flex flex-row logo align-items-center py-2">
<img src="./assets/logo-dark.png" class="me-2" />
<span class="fs-3 text-light">Mercury</span>
</div>
<div>
<h5 class="text-muted">Channels</h5>
<Channels />
</div>
<div>
<h5 class="text-muted mt-3">Users</h5>
<Users />
</div>
<div class="d-flex flex-column mt-auto mb-3">
<span v-if="connected" class="text-light"><i class="bi bi-circle-fill text-success"></i> Connected</span>
<span v-else class="text-light"><i class="bi bi-circle-fill text-warning"></i> Connecting...</span>
<router-link :to="{ name: 'Settings' }" class="text-muted"><i class="bi bi-gear-fill"></i> Settings</router-link>
</div>
</div>
</div>
<div class="col g-0">
<router-view /> <router-view />
</div> </div>
</div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { computed } from 'vue'
import { useMercuryStore } from './stores/mercuryStore'
import Channels from './components/sidebar/Channels.vue'
import Users from './components/sidebar/Users.vue'
const mercuryStore = useMercuryStore()
const connected = computed(() => { return mercuryStore.connected })
</script> </script>
<style> <style>

View File

@ -0,0 +1,45 @@
<template>
<div class="col g-0">
<div class="d-flex flex-column vh-100">
<header class="bg-light px-3 py-2 d-flex flex-row align-items-center">
<span class="me-3 fs-3">
<slot name="title"></slot>
</span>
<slot name="header"></slot>
<span v-if="!home" class="ms-auto fs-4">
<router-link :to="{ name: 'Home' }"><i class="text-dark bi bi-house-fill"></i></router-link>
</span>
</header>
<div class="px-3 py-2 overflow-auto">
<slot name="content"></slot>
<div ref="bottom"></div>
</div>
<div class="px-3 py-2 mt-auto">
<slot name="footer"></slot>
</div>
</div>
</div>
</template>
<script setup>
import { computed, onMounted, onUpdated, ref } from 'vue'
import { useRoute } from 'vue-router'
const props = defineProps({ autoScroll: Boolean })
const route = useRoute()
const home = computed(() => { return route.name == 'Home' })
// Scroll to bottom.
const bottom = ref(null)
onUpdated(() => {
if (props.autoScroll) {
bottom.value.scrollIntoView({ behavior: "smooth", block: "center" })
}
})
onMounted(() => {
if (props.autoScroll) {
bottom.value.scrollIntoView({ block: "center" })
}
})
</script>

View File

@ -0,0 +1,30 @@
<template>
<div class="col-2 bg-dark">
<div class="d-flex flex-column vh-100">
<div class="d-flex flex-row logo align-items-center py-2">
<img src="../assets/logo-dark.png" class="me-2" />
<span class="fs-3 text-light">Mercury</span>
</div>
<div>
<h5 class="text-muted">Channels</h5>
<Channels />
<slot name="top"></slot>
</div>
<div class="d-flex flex-column mt-auto mb-3">
<span v-if="connected" class="text-light"><i class="bi bi-circle-fill text-success"></i> Connected</span>
<span v-else class="text-light"><i class="bi bi-circle-fill text-warning"></i> Connecting...</span>
<router-link :to="{ name: 'Settings' }" class="text-muted"><i class="bi bi-gear-fill"></i> Settings</router-link>
<slot name="bottom"></slot>
</div>
</div>
</div>
</template>
<script setup>
import { computed } from 'vue'
import { useMercuryStore } from '../stores/mercuryStore'
import Channels from './sidebar/Channels.vue'
const mercuryStore = useMercuryStore()
const connected = computed (() => { return mercuryStore.connected })
</script>

View File

@ -4,12 +4,17 @@
</template> </template>
<script setup> <script setup>
import { ref } from 'vue' import { computed, ref } from 'vue'
import { onBeforeRouteUpdate } from 'vue-router'
const props = defineProps(['channel']) const props = defineProps(['channel'])
const key = props.channel.key.base64 const key = computed(() => { return props.channel.key.base64 })
const locked = ref(true) const locked = ref(true)
onBeforeRouteUpdate(() => {
locked.value = true
})
</script> </script>
<style scoped> <style scoped>

View File

@ -8,7 +8,7 @@
<script setup> <script setup>
import { computed } from 'vue' import { computed } from 'vue'
import { useUserStore } from '../stores/userStore' import { useUserStore } from '../../stores/userStore'
const props = defineProps(['message']) const props = defineProps(['message'])
const message = props.message const message = props.message

View File

@ -7,10 +7,10 @@
<script setup> <script setup>
import { computed, inject, ref } from 'vue' import { computed, inject, ref } from 'vue'
import { onBeforeRouteUpdate } from 'vue-router'
import { MessageTypes, packers } from 'bennc-js' import { MessageTypes, packers } from 'bennc-js'
import { useMessageStore } from '../stores/messageStore' import { useMessageStore } from '../../stores/messageStore'
import { animateCSS } from '../common/animate' import { animateCSS } from '../../common/animate'
import { onBeforeRouteUpdate } from 'vue-router';
const encoder = new TextEncoder() const encoder = new TextEncoder()

View File

@ -8,5 +8,5 @@ import { computed } from 'vue'
const props = defineProps(['user']) const props = defineProps(['user'])
const user = props.user const user = props.user
const color = computed(() => { return user.color?.hex() ?? '#000000' }) const color = computed(() => { return user.color?.hex() ?? '#6c757d' })
</script> </script>

View File

@ -8,16 +8,15 @@
</template> </template>
<script setup> <script setup>
import { computed, watch } from 'vue' import { computed } from 'vue'
import { useRoute } from 'vue-router'
import { useUserStore } from '../../stores/userStore' import { useUserStore } from '../../stores/userStore'
import User from './User.vue' import User from './User.vue'
const route = useRoute() const props = defineProps(['channel'])
const channelId = computed(() => { return props.channel.id })
const userStore = useUserStore() const userStore = useUserStore()
const channelId = computed(() => { return route.params.channelId })
const users = computed(() => { return userStore.getUsersByChannelId(channelId.value) }) const users = computed(() => { return userStore.getUsersByChannelId(channelId.value) })
const self = userStore.getSelf() const self = userStore.getSelf()
</script> </script>

View File

@ -1,11 +1,17 @@
import { createRouter, createWebHistory } from 'vue-router' import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
import ConversationView from '../views/ConversationView.vue' import ConversationView from '../views/ConversationView.vue'
import SettingsView from '../views/SettingsView.vue' import SettingsView from '../views/SettingsView.vue'
export default createRouter({ export default createRouter({
history: createWebHistory(), history: createWebHistory(),
routes: [ routes: [
{
name: 'Home',
path: '/',
component: HomeView
},
{ {
name: 'Channel', name: 'Channel',
path: '/channel/:channelId', path: '/channel/:channelId',

View File

@ -1,25 +1,35 @@
<template> <template>
<div class="d-flex flex-column vh-100"> <Sidebar>
<header class="bg-light px-3 py-2 d-flex flex-row align-items-center"> <template v-slot:top>
<span class="mb-0 me-3 fs-3">{{ channel.name }}</span> <h5 class="text-muted mt-3">Users</h5>
<UserList :channel="channel" />
</template>
</Sidebar>
<Content autoScroll>
<template v-slot:title>{{ channel.name }}</template>
<template v-slot:header>
<Key :channel="channel" /> <Key :channel="channel" />
</header> </template>
<div class="messages overflow-auto p-3"> <template v-slot:content>
<Message v-for="message in messages" :key="message.id" :message="message" /> <Message v-for="message in messages" :key="message.id" :message="message" />
<div ref="bottom"></div> </template>
</div> <template v-slot:footer>
<Input :channel="channel" class="mt-auto px-3" /> <MessageInput :channel="channel" />
</div> </template>
</Content>
</template> </template>
<script setup> <script setup>
import { computed, onMounted, onUpdated, ref } from 'vue' import { computed, ref } from 'vue'
import { onBeforeRouteUpdate, useRoute } from 'vue-router' import { onBeforeRouteUpdate, useRoute } from 'vue-router'
import { useChannelStore } from '../stores/channelStore' import { useChannelStore } from '../stores/channelStore'
import { useMessageStore } from '../stores/messageStore' import { useMessageStore } from '../stores/messageStore'
import Input from '../components/Input.vue' import Sidebar from '../components/Sidebar.vue'
import Message from '../components/Message.vue' import Content from '../components/Content.vue'
import Key from '../components/Key.vue' import Key from '../components/common/Key.vue'
import UserList from '../components/userlist/UserList.vue'
import Message from '../components/conversation/Message.vue'
import MessageInput from '../components/conversation/MessageInput.vue'
const route = useRoute() const route = useRoute()
const channelStore = useChannelStore() const channelStore = useChannelStore()
@ -31,13 +41,5 @@ const messages = computed(() => { return messageStore.getMessagesByChannelId(rou
// Show the key. // Show the key.
const locked = ref(false) const locked = ref(false)
onBeforeRouteUpdate(async (to, from) => { locked.value = false }) onBeforeRouteUpdate(async (to, from) => { locked.value = false })
const abcdef = ref(null)
// Scroll to bottom.
const bottom = ref(null)
onUpdated(() => {
bottom.value.scrollIntoView({ behavior: "smooth", block: "center" })
})
onMounted(() => {
bottom.value.scrollIntoView({ block: "center" })
})
</script> </script>

35
src/views/HomeView.vue Normal file
View File

@ -0,0 +1,35 @@
<template>
<Sidebar>
</Sidebar>
<Content>
<template v-slot:title>Welcome to Mercury!</template>
<template v-slot:content>
<div class="d-flex flex-column text-center align-items-center px-5 overflow-hidden">
<img class="animate__animated animate__rotateIn py-5 w-25" id="logo" src="../assets/logo-light.png" />
<h1 class="animate__animated animate__fadeInDown animate__delay-1s">
Mercury is a web-based BENNC client.
</h1>
<h2 class="animate__animated animate__fadeInDown animate__delay-2s">
Go to <router-link :to="{ name: 'Settings' }" class="text-muted"><i class="bi bi-gear-fill"></i> Settings</router-link> to configure your client.
</h2>
</div>
</template>
</Content>
</template>
<script setup>
import Sidebar from '../components/Sidebar.vue'
import Content from '../components/Content.vue'
</script>
<style scoped>
#logo {
filter: invert(80%);
}
h1 {
--animate-delay: 0.5s;
}
h2 {
--animate-delay: 0.5s;
}
</style>

View File

@ -1,9 +1,8 @@
<template> <template>
<div class="d-flex flex-column vh-100"> <Sidebar />
<header class="bg-light px-3 py-2 d-flex flex-row align-items-center"> <Content>
<h2 class="mb-0 me-3">Settings</h2> <template v-slot:title>Settings</template>
</header> <template v-slot:content>
<div class="px-3 py-2">
<h3>Channels</h3> <h3>Channels</h3>
<table class="table"> <table class="table">
<thead> <thead>
@ -29,13 +28,16 @@
</div> </div>
</div> </div>
<button type="submit" class="btn btn-primary float-end">Add channel</button> <button type="submit" class="btn btn-primary float-end">Add channel</button>
</div> </template>
</div> </Content>
</template> </template>
<script setup> <script setup>
import { useChannelStore } from '../stores/channelStore' import { useChannelStore } from '../stores/channelStore'
import Key from '../components/Key.vue' import Key from '../components/common/Key.vue'
import Sidebar from '../components/Sidebar.vue'
import Content from '../components/Content.vue'
const channelStore = useChannelStore() const channelStore = useChannelStore()
</script> </script>