feat: account switcher sidebar (#460)
* feat: account switcher sidebar * fix: defer loading sidebar until masto initialised * fix: only show user switcher for 2 or more accounts * chore: use `ofetch` (newer version of `ohymfetch`) * chore: early alpha warning * fix: handle missing user in github preview * refactor: avoid circular auto-import Co-authored-by: Daniel Roe <daniel@roe.dev>
This commit is contained in:
parent
15b59ae9b9
commit
cd85871d01
|
@ -24,6 +24,12 @@ const featureFlags = useFeatureFlags()
|
||||||
>
|
>
|
||||||
{{ $t('feature_flag.github_cards') }}
|
{{ $t('feature_flag.github_cards') }}
|
||||||
</CommonDropdownItem>
|
</CommonDropdownItem>
|
||||||
|
<CommonDropdownItem
|
||||||
|
:checked="featureFlags.experimentalUserSwitcherSidebar"
|
||||||
|
@click="toggleFeatureFlag('experimentalUserSwitcherSidebar')"
|
||||||
|
>
|
||||||
|
{{ $t('feature_flag.user_switcher_sidebar') }}
|
||||||
|
</CommonDropdownItem>
|
||||||
</template>
|
</template>
|
||||||
</CommonDropdown>
|
</CommonDropdown>
|
||||||
</template>
|
</template>
|
||||||
|
|
30
components/user/UserPicker.vue
Normal file
30
components/user/UserPicker.vue
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { UserLogin } from '~/types'
|
||||||
|
|
||||||
|
const all = useUsers()
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const switchUser = (user: UserLogin) => {
|
||||||
|
if (user.account.id === currentUser.value?.account.id)
|
||||||
|
router.push(getAccountRoute(user.account))
|
||||||
|
else
|
||||||
|
loginTo(user)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div flex="~ col" pb8 px4 gap-6 w-20 h-full justify-end>
|
||||||
|
<template v-for="user of all" :key="user.id">
|
||||||
|
<button
|
||||||
|
flex rounded
|
||||||
|
cursor-pointer
|
||||||
|
aria-label="Switch user"
|
||||||
|
:class="user.account.id === currentUser?.account.id ? '' : 'grayscale'"
|
||||||
|
hover:filter-none
|
||||||
|
@click="switchUser(user)"
|
||||||
|
>
|
||||||
|
<AccountAvatar w-12 h-12 :account="user.account" :hover-card="false" />
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
|
@ -4,6 +4,7 @@ export interface FeatureFlags {
|
||||||
experimentalVirtualScroll: boolean
|
experimentalVirtualScroll: boolean
|
||||||
experimentalAvatarOnAvatar: boolean
|
experimentalAvatarOnAvatar: boolean
|
||||||
experimentalGitHubCards: boolean
|
experimentalGitHubCards: boolean
|
||||||
|
experimentalUserSwitcherSidebar: boolean
|
||||||
}
|
}
|
||||||
export type FeatureFlagsMap = Record<string, FeatureFlags>
|
export type FeatureFlagsMap = Record<string, FeatureFlags>
|
||||||
|
|
||||||
|
@ -12,6 +13,7 @@ export function getDefaultFeatureFlags(): FeatureFlags {
|
||||||
experimentalVirtualScroll: false,
|
experimentalVirtualScroll: false,
|
||||||
experimentalAvatarOnAvatar: true,
|
experimentalAvatarOnAvatar: true,
|
||||||
experimentalGitHubCards: true,
|
experimentalGitHubCards: true,
|
||||||
|
experimentalUserSwitcherSidebar: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,3 +33,6 @@ export function toggleFeatureFlag(key: keyof FeatureFlags) {
|
||||||
else
|
else
|
||||||
featureFlags[key] = true
|
featureFlags[key] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const userSwitcherSidebar = eagerComputed(() => useFeatureFlags().experimentalUserSwitcherSidebar)
|
||||||
|
export const showUserSwitcherSidebar = computed(() => useUsers().value.length > 1 && userSwitcherSidebar.value)
|
||||||
|
|
|
@ -1,14 +1,38 @@
|
||||||
<template>
|
<template>
|
||||||
<div h-full :class="{ zen: isZenMode }">
|
<div h-full :class="{ zen: isZenMode }">
|
||||||
<main flex w-full mxa lg:max-w-80rem>
|
<div v-if="isMastoInitialised" v-show="showUserSwitcherSidebar" fixed h-full hidden md:block bg-code border-r-1 border-base>
|
||||||
|
<UserPicker />
|
||||||
|
</div>
|
||||||
|
<main flex w-full mxa lg:max-w-80rem :class="isMastoInitialised && showUserSwitcherSidebar ? 'md:pl-20' : ''">
|
||||||
<aside class="hidden md:block w-1/4 zen-hide" relative>
|
<aside class="hidden md:block w-1/4 zen-hide" relative>
|
||||||
<div sticky top-0 h-screen flex="~ col">
|
<div sticky top-0 h-screen flex="~ col">
|
||||||
<slot name="left">
|
<slot name="left">
|
||||||
<NavTitle mx3 mt4 mb2 self-start />
|
<NavTitle mx3 mt4 mb2 self-start />
|
||||||
<div flex="~ col" overflow-y-auto>
|
<div flex="~ col" overflow-y-auto justify-between h-full>
|
||||||
<NavSide />
|
<div flex flex-col>
|
||||||
<PublishButton v-if="isMastoInitialised && currentUser" m5 />
|
<NavSide />
|
||||||
<div flex-auto />
|
<PublishButton v-if="isMastoInitialised && currentUser" m5 />
|
||||||
|
</div>
|
||||||
|
<div flex flex-col>
|
||||||
|
<UserSignInEntry v-if="isMastoInitialised && !currentUser" />
|
||||||
|
<div v-if="isMastoInitialised && currentUser" py6 px4 w-full flex="~" items-center justify-between>
|
||||||
|
<NuxtLink
|
||||||
|
p2 rounded-full text-start w-full
|
||||||
|
hover:bg-active cursor-pointer transition-100
|
||||||
|
:to="getAccountRoute(currentUser.account)"
|
||||||
|
>
|
||||||
|
<AccountInfo :account="currentUser.account" md:break-words />
|
||||||
|
</NuxtLink>
|
||||||
|
<VDropdown :distance="0" placement="bottom-end">
|
||||||
|
<button btn-action-icon :aria-label="$t('action.switch_account')">
|
||||||
|
<div i-ri:more-2-line />
|
||||||
|
</button>
|
||||||
|
<template #popper="{ hide }">
|
||||||
|
<UserSwitcher @click="hide" />
|
||||||
|
</template>
|
||||||
|
</VDropdown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</slot>
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
|
@ -23,28 +47,9 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<aside class="hidden md:none lg:block w-1/4 zen-hide">
|
<aside class="hidden md:none lg:block w-1/4 zen-hide">
|
||||||
<div sticky top-0 h-screen flex="~ col">
|
<div sticky top-0 h-screen flex="~ col" py3>
|
||||||
<slot name="right">
|
<slot name="right">
|
||||||
<SearchWidget v-if="isMastoInitialised" />
|
<SearchWidget v-if="isMastoInitialised" />
|
||||||
<UserSignInEntry v-if="isMastoInitialised && !currentUser" />
|
|
||||||
<div v-if="isMastoInitialised && currentUser" py6 px4 w-full flex="~" items-center justify-between>
|
|
||||||
<NuxtLink
|
|
||||||
p2 rounded-full text-start w-full
|
|
||||||
hover:bg-active cursor-pointer transition-100
|
|
||||||
:to="getAccountRoute(currentUser.account)"
|
|
||||||
>
|
|
||||||
<AccountInfo :account="currentUser.account" md:break-words />
|
|
||||||
</NuxtLink>
|
|
||||||
<VDropdown :distance="0" placement="bottom-end">
|
|
||||||
<button btn-action-icon :aria-label="$t('action.switch_account')">
|
|
||||||
<div i-ri:more-2-line />
|
|
||||||
</button>
|
|
||||||
<template #popper="{ hide }">
|
|
||||||
<UserSwitcher @click="hide" />
|
|
||||||
</template>
|
|
||||||
</VDropdown>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div flex-auto />
|
<div flex-auto />
|
||||||
<NavFooter />
|
<NavFooter />
|
||||||
</slot>
|
</slot>
|
||||||
|
|
|
@ -82,6 +82,7 @@
|
||||||
"feature_flag": {
|
"feature_flag": {
|
||||||
"avatar_on_avatar": "Avatar on Avatar",
|
"avatar_on_avatar": "Avatar on Avatar",
|
||||||
"github_cards": "GitHub Cards",
|
"github_cards": "GitHub Cards",
|
||||||
|
"user_switcher_sidebar": "User Switcher Sidebar",
|
||||||
"virtual_scroll": "Virtual Scrolling"
|
"virtual_scroll": "Virtual Scrolling"
|
||||||
},
|
},
|
||||||
"help": {
|
"help": {
|
||||||
|
|
|
@ -58,6 +58,7 @@
|
||||||
},
|
},
|
||||||
"feature_flag": {
|
"feature_flag": {
|
||||||
"github_cards": "GitHub Cards",
|
"github_cards": "GitHub Cards",
|
||||||
|
"user_switcher_sidebar": "User Switcher Sidebar",
|
||||||
"virtual_scroll": "Virtuelles Scrollen"
|
"virtual_scroll": "Virtuelles Scrollen"
|
||||||
},
|
},
|
||||||
"menu": {
|
"menu": {
|
||||||
|
|
|
@ -86,6 +86,7 @@
|
||||||
"feature_flag": {
|
"feature_flag": {
|
||||||
"avatar_on_avatar": "Avatar on Avatar",
|
"avatar_on_avatar": "Avatar on Avatar",
|
||||||
"github_cards": "GitHub Cards",
|
"github_cards": "GitHub Cards",
|
||||||
|
"user_switcher_sidebar": "User Switcher Sidebar",
|
||||||
"virtual_scroll": "Virtual Scrolling"
|
"virtual_scroll": "Virtual Scrolling"
|
||||||
},
|
},
|
||||||
"help": {
|
"help": {
|
||||||
|
|
|
@ -83,6 +83,7 @@
|
||||||
"feature_flag": {
|
"feature_flag": {
|
||||||
"avatar_on_avatar": "Avatar en Avatar",
|
"avatar_on_avatar": "Avatar en Avatar",
|
||||||
"github_cards": "GitHub Cards",
|
"github_cards": "GitHub Cards",
|
||||||
|
"user_switcher_sidebar": "User Switcher Sidebar",
|
||||||
"virtual_scroll": "Virtual Scrolling"
|
"virtual_scroll": "Virtual Scrolling"
|
||||||
},
|
},
|
||||||
"help": {
|
"help": {
|
||||||
|
|
|
@ -86,6 +86,7 @@
|
||||||
"feature_flag": {
|
"feature_flag": {
|
||||||
"avatar_on_avatar": "Avatar sur avatar",
|
"avatar_on_avatar": "Avatar sur avatar",
|
||||||
"github_cards": "GitHub Cards",
|
"github_cards": "GitHub Cards",
|
||||||
|
"user_switcher_sidebar": "User Switcher Sidebar",
|
||||||
"virtual_scroll": "Défilement virtuel"
|
"virtual_scroll": "Défilement virtuel"
|
||||||
},
|
},
|
||||||
"help": {
|
"help": {
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
},
|
},
|
||||||
"feature_flag": {
|
"feature_flag": {
|
||||||
"github_cards": "GitHub Cards",
|
"github_cards": "GitHub Cards",
|
||||||
|
"user_switcher_sidebar": "User Switcher Sidebar",
|
||||||
"virtual_scroll": "仮想スクロール"
|
"virtual_scroll": "仮想スクロール"
|
||||||
},
|
},
|
||||||
"menu": {
|
"menu": {
|
||||||
|
|
|
@ -85,6 +85,7 @@
|
||||||
"feature_flag": {
|
"feature_flag": {
|
||||||
"avatar_on_avatar": "头像堆叠",
|
"avatar_on_avatar": "头像堆叠",
|
||||||
"github_cards": "GitHub Cards",
|
"github_cards": "GitHub Cards",
|
||||||
|
"user_switcher_sidebar": "User Switcher Sidebar",
|
||||||
"virtual_scroll": "虚拟滚动"
|
"virtual_scroll": "虚拟滚动"
|
||||||
},
|
},
|
||||||
"help": {
|
"help": {
|
||||||
|
|
Loading…
Reference in a new issue