Refactor sidebar
This commit is contained in:
parent
b1977447a2
commit
c6fb1528ea
32
src/App.vue
32
src/App.vue
|
@ -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">
|
<router-view />
|
||||||
<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 />
|
|
||||||
</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>
|
||||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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
|
|
@ -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()
|
||||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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',
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in New Issue