forked from nimbus-town/nimbus
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: allow choosing favorite buttons in bottom navigation bar (#2761)
- Loading branch information
Showing
14 changed files
with
286 additions
and
47 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,60 +1,45 @@ | ||
<script setup lang="ts"> | ||
// 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' | ||
import type { Component } from 'vue' | ||
import type { NavButtonName } from '../../composables/settings' | ||
const moreMenuVisible = ref(false) | ||
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 { notifications } = useNotifications() | ||
const lastAccessedNotificationRoute = useLocalStorage(STORAGE_KEY_LAST_ACCESSED_NOTIFICATION_ROUTE, '') | ||
const lastAccessedExploreRoute = useLocalStorage(STORAGE_KEY_LAST_ACCESSED_EXPLORE_ROUTE, '') | ||
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 | ||
const moreMenuVisible = ref(false) | ||
</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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
<script setup lang="ts"> | ||
import type { NavButtonName } from '~/composables/settings' | ||
import { STORAGE_KEY_BOTTOM_NAV_BUTTONS } from '~/constants' | ||
interface NavButton { | ||
name: NavButtonName | ||
label: string | ||
icon: string | ||
} | ||
const availableNavButtons: NavButton[] = [ | ||
{ name: 'home', label: 'nav.home', icon: 'i-ri:home-5-line' }, | ||
{ name: 'search', label: 'nav.search', icon: 'i-ri:search-line' }, | ||
{ name: 'notification', label: 'nav.notifications', icon: 'i-ri:notification-4-line' }, | ||
{ name: 'mention', label: 'nav.conversations', icon: 'i-ri:at-line' }, | ||
{ name: 'explore', label: 'nav.explore', icon: 'i-ri:compass-3-line' }, | ||
{ name: 'local', label: 'nav.local', icon: 'i-ri:group-2-line' }, | ||
{ name: 'federated', label: 'nav.federated', icon: 'i-ri:earth-line' }, | ||
{ name: 'moreMenu', label: 'nav.more_menu', icon: 'i-ri:more-fill' }, | ||
] as const | ||
const defaultSelectedNavButtonNames = computed<NavButtonName[]>(() => | ||
currentUser.value | ||
? ['home', 'search', 'notification', 'mention', 'moreMenu'] | ||
: ['explore', 'local', 'federated', 'moreMenu'], | ||
) | ||
const navButtonNamesSetting = useLocalStorage<NavButtonName[]>(STORAGE_KEY_BOTTOM_NAV_BUTTONS, defaultSelectedNavButtonNames.value) | ||
const selectedNavButtonNames = ref(navButtonNamesSetting.value) | ||
const selectedNavButtons = computed<NavButton[]>(() => | ||
selectedNavButtonNames.value.map(name => | ||
availableNavButtons.find(navButton => navButton.name === name)!, | ||
), | ||
) | ||
const canSave = computed(() => | ||
selectedNavButtonNames.value.length > 0 | ||
&& selectedNavButtonNames.value.includes('moreMenu') | ||
&& JSON.stringify(selectedNavButtonNames.value) !== JSON.stringify(navButtonNamesSetting.value), | ||
) | ||
function isAdded(name: NavButtonName) { | ||
return selectedNavButtonNames.value.includes(name) | ||
} | ||
function append(navButtonName: NavButtonName) { | ||
const maxButtonNumber = 5 | ||
if (selectedNavButtonNames.value.length < maxButtonNumber) | ||
selectedNavButtonNames.value = [...selectedNavButtonNames.value, navButtonName] | ||
} | ||
function remove(navButtonName: NavButtonName) { | ||
selectedNavButtonNames.value = selectedNavButtonNames.value.filter(name => name !== navButtonName) | ||
} | ||
function clear() { | ||
selectedNavButtonNames.value = [] | ||
} | ||
function reset() { | ||
selectedNavButtonNames.value = defaultSelectedNavButtonNames.value | ||
} | ||
function save() { | ||
navButtonNamesSetting.value = selectedNavButtonNames.value | ||
} | ||
</script> | ||
|
||
<template> | ||
<!-- preview --> | ||
<div flex="~ gap4 wrap" items-center select-settings h-14 p0> | ||
<nav | ||
v-for="availableNavButton in selectedNavButtons" :key="availableNavButton.name" | ||
flex="~ 1" items-center justify-center text-xl | ||
scrollbar-hide overscroll-none | ||
> | ||
<button btn-base :class="availableNavButton.icon" mx-4 tabindex="-1" /> | ||
</nav> | ||
</div> | ||
|
||
<!-- button selection --> | ||
<div flex="~ gap4 wrap" py4> | ||
<button | ||
v-for="{ name, label, icon } in availableNavButtons" | ||
:key="name" | ||
btn-text flex="~ gap-2" items-center p2 border="~ base rounded" bg-base ws-nowrap | ||
:class="isAdded(name) ? 'text-secondary hover:text-second bg-auto' : ''" | ||
type="button" | ||
role="switch" | ||
:aria-checked="isAdded(name)" | ||
@click="isAdded(name) ? remove(name) : append(name)" | ||
> | ||
<span :class="icon" /> | ||
{{ label ? $t(label) : 'More menu' }} | ||
</button> | ||
</div> | ||
|
||
<div flex="~ col" gap-y-4 gap-x-2 py-1 sm="~ justify-end flex-row"> | ||
<button | ||
btn-outline font-bold py2 full-w sm-wa flex="~ gap2 center" | ||
type="button" | ||
:disabled="selectedNavButtonNames.length === 0" | ||
@click="clear" | ||
> | ||
<span aria-hidden="true" class="block i-ri:delete-bin-line" /> | ||
{{ $t('action.clear') }} | ||
</button> | ||
<button | ||
btn-outline font-bold py2 full-w sm-wa flex="~ gap2 center" | ||
type="button" | ||
@click="reset" | ||
> | ||
<span aria-hidden="true" class="block i-ri:repeat-line" /> | ||
{{ $t('action.reset') }} | ||
</button> | ||
<button | ||
btn-solid font-bold py2 full-w sm-wa flex="~ gap2 center" | ||
:disabled="!canSave" | ||
@click="save" | ||
> | ||
<span aria-hidden="true" i-ri:save-2-fill /> | ||
{{ $t('action.save') }} | ||
</button> | ||
</div> | ||
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters