feat: allow choosing favorite buttons in bottom navigation bar (#2761)
This commit is contained in:
parent
2a6a994da1
commit
2cb070c83c
14 changed files with 286 additions and 47 deletions
|
@ -1,60 +1,45 @@
|
|||
<script setup lang="ts">
|
||||
import type { Component } from 'vue'
|
||||
import type { NavButtonName } from '../../composables/settings'
|
||||
|
||||
import { STORAGE_KEY_BOTTOM_NAV_BUTTONS } from '~/constants'
|
||||
|
||||
import { NavButtonExplore, NavButtonFederated, NavButtonHome, NavButtonLocal, NavButtonMention, NavButtonMoreMenu, NavButtonNotification, NavButtonSearch } from '#components'
|
||||
|
||||
interface NavButton {
|
||||
name: string
|
||||
component: Component
|
||||
}
|
||||
|
||||
const navButtons: NavButton[] = [
|
||||
{ name: 'home', component: NavButtonHome },
|
||||
{ name: 'search', component: NavButtonSearch },
|
||||
{ name: 'notification', component: NavButtonNotification },
|
||||
{ name: 'mention', component: NavButtonMention },
|
||||
{ name: 'explore', component: NavButtonExplore },
|
||||
{ name: 'local', component: NavButtonLocal },
|
||||
{ name: 'federated', component: NavButtonFederated },
|
||||
{ name: 'moreMenu', component: NavButtonMoreMenu },
|
||||
]
|
||||
|
||||
const defaultSelectedNavButtonNames: NavButtonName[] = currentUser.value
|
||||
? ['home', 'search', 'notification', 'mention', 'moreMenu']
|
||||
: ['explore', 'local', 'federated', 'moreMenu']
|
||||
const selectedNavButtonNames = useLocalStorage<NavButtonName[]>(STORAGE_KEY_BOTTOM_NAV_BUTTONS, defaultSelectedNavButtonNames)
|
||||
|
||||
const selectedNavButtons = computed(() => selectedNavButtonNames.value.map(name => navButtons.find(navButton => navButton.name === name)))
|
||||
|
||||
// only one icon can be lit up at the same time
|
||||
import { STORAGE_KEY_LAST_ACCESSED_EXPLORE_ROUTE, STORAGE_KEY_LAST_ACCESSED_NOTIFICATION_ROUTE } from '~/constants'
|
||||
|
||||
const moreMenuVisible = ref(false)
|
||||
|
||||
const { notifications } = useNotifications()
|
||||
const lastAccessedNotificationRoute = useLocalStorage(STORAGE_KEY_LAST_ACCESSED_NOTIFICATION_ROUTE, '')
|
||||
const lastAccessedExploreRoute = useLocalStorage(STORAGE_KEY_LAST_ACCESSED_EXPLORE_ROUTE, '')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- This weird styles above are used for scroll locking, don't change it unless you know exactly what you're doing. -->
|
||||
<nav
|
||||
h-14 border="t base" flex flex-row text-xl
|
||||
of-y-scroll scrollbar-hide overscroll-none
|
||||
class="after-content-empty after:(h-[calc(100%+0.5px)] w-0.1px pointer-events-none)"
|
||||
>
|
||||
<!-- These weird styles above are used for scroll locking, don't change it unless you know exactly what you're doing. -->
|
||||
<template v-if="currentUser">
|
||||
<NuxtLink to="/home" :aria-label="$t('nav.home')" :active-class="moreMenuVisible ? '' : 'text-primary'" flex flex-row items-center place-content-center h-full flex-1 class="coarse-pointer:select-none" @click="$scrollToTop">
|
||||
<div i-ri:home-5-line />
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/search" :aria-label="$t('nav.search')" :active-class="moreMenuVisible ? '' : 'text-primary'" flex flex-row items-center place-content-center h-full flex-1 class="coarse-pointer:select-none" @click="$scrollToTop">
|
||||
<div i-ri:search-line />
|
||||
</NuxtLink>
|
||||
<NuxtLink :to="`/notifications/${lastAccessedNotificationRoute}`" :aria-label="$t('nav.notifications')" :active-class="moreMenuVisible ? '' : 'text-primary'" flex flex-row items-center place-content-center h-full flex-1 class="coarse-pointer:select-none" @click="$scrollToTop">
|
||||
<div flex relative>
|
||||
<div class="i-ri:notification-4-line" text-xl />
|
||||
<div v-if="notifications" class="top-[-0.3rem] right-[-0.3rem]" absolute font-bold rounded-full h-4 w-4 text-xs bg-primary text-inverted flex items-center justify-center>
|
||||
{{ notifications < 10 ? notifications : '•' }}
|
||||
</div>
|
||||
</div>
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/conversations" :aria-label="$t('nav.conversations')" :active-class="moreMenuVisible ? '' : 'text-primary'" flex flex-row items-center place-content-center h-full flex-1 class="coarse-pointer:select-none" @click="$scrollToTop">
|
||||
<div i-ri:at-line />
|
||||
</NuxtLink>
|
||||
</template>
|
||||
<template v-else>
|
||||
<NuxtLink :to="`/${currentServer}/explore/${lastAccessedExploreRoute}`" :aria-label="$t('nav.explore')" :active-class="moreMenuVisible ? '' : 'text-primary'" flex flex-row items-center place-content-center h-full flex-1 class="coarse-pointer:select-none" @click="$scrollToTop">
|
||||
<div i-ri:compass-3-line />
|
||||
</NuxtLink>
|
||||
<NuxtLink group :to="`/${currentServer}/public/local`" :aria-label="$t('nav.local')" :active-class="moreMenuVisible ? '' : 'text-primary'" flex flex-row items-center place-content-center h-full flex-1 class="coarse-pointer:select-none" @click="$scrollToTop">
|
||||
<div i-ri:group-2-line />
|
||||
</NuxtLink>
|
||||
<NuxtLink :to="`/${currentServer}/public`" :aria-label="$t('nav.federated')" :active-class="moreMenuVisible ? '' : 'text-primary'" flex flex-row items-center place-content-center h-full flex-1 class="coarse-pointer:select-none" @click="$scrollToTop">
|
||||
<div i-ri:earth-line />
|
||||
</NuxtLink>
|
||||
</template>
|
||||
<NavBottomMoreMenu v-slot="{ toggleVisible, show }" v-model="moreMenuVisible" flex flex-row items-center place-content-center h-full flex-1 cursor-pointer>
|
||||
<button
|
||||
flex items-center place-content-center h-full flex-1 class="select-none"
|
||||
:class="show ? '!text-primary' : ''"
|
||||
aria-label="More menu"
|
||||
@click="toggleVisible"
|
||||
>
|
||||
<span :class="show ? 'i-ri:close-fill' : 'i-ri:more-fill'" />
|
||||
</button>
|
||||
</NavBottomMoreMenu>
|
||||
<Component :is="navButton!.component" v-for="navButton in selectedNavButtons" :key="navButton!.name" :active-class="moreMenuVisible ? '' : 'text-primary'" />
|
||||
</nav>
|
||||
</template>
|
||||
|
|
15
components/nav/button/Explore.vue
Normal file
15
components/nav/button/Explore.vue
Normal file
|
@ -0,0 +1,15 @@
|
|||
<script setup lang="ts">
|
||||
import { STORAGE_KEY_LAST_ACCESSED_EXPLORE_ROUTE } from '~/constants'
|
||||
|
||||
defineProps<{
|
||||
activeClass: string
|
||||
}>()
|
||||
|
||||
const lastAccessedExploreRoute = useLocalStorage(STORAGE_KEY_LAST_ACCESSED_EXPLORE_ROUTE, '')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NuxtLink :to="`/${currentServer}/explore/${lastAccessedExploreRoute}`" :aria-label="$t('nav.explore')" :active-class="activeClass" flex flex-row items-center place-content-center h-full flex-1 class="coarse-pointer:select-none" @click="$scrollToTop">
|
||||
<div i-ri:compass-3-line />
|
||||
</NuxtLink>
|
||||
</template>
|
11
components/nav/button/Federated.vue
Normal file
11
components/nav/button/Federated.vue
Normal file
|
@ -0,0 +1,11 @@
|
|||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
activeClass: string
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NuxtLink :to="`/${currentServer}/public`" :aria-label="$t('nav.federated')" :active-class="activeClass" flex flex-row items-center place-content-center h-full flex-1 class="coarse-pointer:select-none" @click="$scrollToTop">
|
||||
<div i-ri:earth-line />
|
||||
</NuxtLink>
|
||||
</template>
|
11
components/nav/button/Home.vue
Normal file
11
components/nav/button/Home.vue
Normal file
|
@ -0,0 +1,11 @@
|
|||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
activeClass: string
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NuxtLink to="/home" :aria-label="$t('nav.home')" :active-class="activeClass" flex flex-row items-center place-content-center h-full flex-1 class="coarse-pointer:select-none" @click="$scrollToTop">
|
||||
<div i-ri:home-5-line />
|
||||
</NuxtLink>
|
||||
</template>
|
11
components/nav/button/Local.vue
Normal file
11
components/nav/button/Local.vue
Normal file
|
@ -0,0 +1,11 @@
|
|||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
activeClass: string
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NuxtLink group :to="`/${currentServer}/public/local`" :aria-label="$t('nav.local')" :active-class="activeClass" flex flex-row items-center place-content-center h-full flex-1 class="coarse-pointer:select-none" @click="$scrollToTop">
|
||||
<div i-ri:group-2-line />
|
||||
</NuxtLink>
|
||||
</template>
|
15
components/nav/button/Mention.vue
Normal file
15
components/nav/button/Mention.vue
Normal file
|
@ -0,0 +1,15 @@
|
|||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
activeClass: string
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NuxtLink
|
||||
to="/conversations" :aria-label="$t('nav.conversations')"
|
||||
:active-class="activeClass" flex flex-row items-center place-content-center h-full
|
||||
flex-1 class="coarse-pointer:select-none" @click="$scrollToTop"
|
||||
>
|
||||
<div i-ri:at-line />
|
||||
</NuxtLink>
|
||||
</template>
|
19
components/nav/button/MoreMenu.vue
Normal file
19
components/nav/button/MoreMenu.vue
Normal file
|
@ -0,0 +1,19 @@
|
|||
<script setup lang="ts">
|
||||
defineModel<boolean>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NavBottomMoreMenu
|
||||
v-slot="{ toggleVisible, show }" v-model="modelValue!" flex flex-row items-center
|
||||
place-content-center h-full flex-1 cursor-pointer
|
||||
>
|
||||
<button
|
||||
flex items-center place-content-center h-full flex-1 class="select-none"
|
||||
:class="show ? '!text-primary' : ''"
|
||||
aria-label="More menu"
|
||||
@click="toggleVisible"
|
||||
>
|
||||
<span :class="show ? 'i-ri:close-fill' : 'i-ri:more-fill'" />
|
||||
</button>
|
||||
</NavBottomMoreMenu>
|
||||
</template>
|
20
components/nav/button/Notification.vue
Normal file
20
components/nav/button/Notification.vue
Normal file
|
@ -0,0 +1,20 @@
|
|||
<script setup lang="ts">
|
||||
import { STORAGE_KEY_LAST_ACCESSED_NOTIFICATION_ROUTE } from '~/constants'
|
||||
|
||||
defineProps<{
|
||||
activeClass: string
|
||||
}>()
|
||||
const { notifications } = useNotifications()
|
||||
const lastAccessedNotificationRoute = useLocalStorage(STORAGE_KEY_LAST_ACCESSED_NOTIFICATION_ROUTE, '')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NuxtLink :to="`/notifications/${lastAccessedNotificationRoute}`" :aria-label="$t('nav.notifications')" :active-class="activeClass" flex flex-row items-center place-content-center h-full flex-1 class="coarse-pointer:select-none" @click="$scrollToTop">
|
||||
<div flex relative>
|
||||
<div class="i-ri:notification-4-line" text-xl />
|
||||
<div v-if="notifications" class="top-[-0.3rem] right-[-0.3rem]" absolute font-bold rounded-full h-4 w-4 text-xs bg-primary text-inverted flex items-center justify-center>
|
||||
{{ notifications < 10 ? notifications : '•' }}
|
||||
</div>
|
||||
</div>
|
||||
</NuxtLink>
|
||||
</template>
|
11
components/nav/button/Search.vue
Normal file
11
components/nav/button/Search.vue
Normal file
|
@ -0,0 +1,11 @@
|
|||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
activeClass: string
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NuxtLink to="/search" :aria-label="$t('nav.search')" :active-class="activeClass" flex flex-row items-center place-content-center h-full flex-1 class="coarse-pointer:select-none" @click="$scrollToTop">
|
||||
<div i-ri:search-line />
|
||||
</NuxtLink>
|
||||
</template>
|
Loading…
Add table
Add a link
Reference in a new issue