kopia lustrzana https://gitlab.com/soapbox-pub/soapbox
Add profile control at the bottom
rodzic
4498130f23
commit
f56dabe458
|
@ -23,11 +23,13 @@ import worldIcon from '@tabler/icons/outline/world.svg';
|
||||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
import Account from 'soapbox/components/account.tsx';
|
||||||
import SiteLogo from 'soapbox/components/site-logo.tsx';
|
import SiteLogo from 'soapbox/components/site-logo.tsx';
|
||||||
import Stack from 'soapbox/components/ui/stack.tsx';
|
import Stack from 'soapbox/components/ui/stack.tsx';
|
||||||
import { useStatContext } from 'soapbox/contexts/stat-context.tsx';
|
import { useStatContext } from 'soapbox/contexts/stat-context.tsx';
|
||||||
import Search from 'soapbox/features/compose/components/search.tsx';
|
import Search from 'soapbox/features/compose/components/search.tsx';
|
||||||
import ComposeButton from 'soapbox/features/ui/components/compose-button.tsx';
|
import ComposeButton from 'soapbox/features/ui/components/compose-button.tsx';
|
||||||
|
import ProfileDropdown from 'soapbox/features/ui/components/profile-dropdown.tsx';
|
||||||
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
|
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
|
||||||
import { useFeatures } from 'soapbox/hooks/useFeatures.ts';
|
import { useFeatures } from 'soapbox/hooks/useFeatures.ts';
|
||||||
import { useInstance } from 'soapbox/hooks/useInstance.ts';
|
import { useInstance } from 'soapbox/hooks/useInstance.ts';
|
||||||
|
@ -142,107 +144,119 @@ const SidebarNavigation = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack space={6}>
|
<Stack justifyContent='between' className='min-h-screen py-6'>
|
||||||
<Link key='logo' to='/' data-preview-title-id='column.home' className='ml-4 flex shrink-0 items-center'>
|
<Stack space={6}>
|
||||||
<SiteLogo alt='Logo' className='h-10 w-auto cursor-pointer' />
|
<Link key='logo' to='/' data-preview-title-id='column.home' className='ml-4 flex shrink-0 items-center'>
|
||||||
<span className='hidden'><FormattedMessage id='tabs_bar.home' defaultMessage='Home' /></span>
|
<SiteLogo alt='Logo' className='h-10 w-auto cursor-pointer' />
|
||||||
</Link>
|
<span className='hidden'><FormattedMessage id='tabs_bar.home' defaultMessage='Home' /></span>
|
||||||
|
</Link>
|
||||||
|
|
||||||
<Search openInRoute autosuggest />
|
<Search openInRoute autosuggest />
|
||||||
|
|
||||||
<Stack space={2}>
|
<Stack space={2}>
|
||||||
<SidebarNavigationLink
|
<SidebarNavigationLink
|
||||||
to='/'
|
to='/'
|
||||||
icon={homeIcon}
|
icon={homeIcon}
|
||||||
activeIcon={homeFilledIcon}
|
activeIcon={homeFilledIcon}
|
||||||
text={<FormattedMessage id='tabs_bar.home' defaultMessage='Home' />}
|
text={<FormattedMessage id='tabs_bar.home' defaultMessage='Home' />}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SidebarNavigationLink
|
<SidebarNavigationLink
|
||||||
to='/search'
|
to='/search'
|
||||||
icon={searchIcon}
|
icon={searchIcon}
|
||||||
text={<FormattedMessage id='tabs_bar.search' defaultMessage='Discover' />}
|
text={<FormattedMessage id='tabs_bar.search' defaultMessage='Discover' />}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{account && (
|
||||||
|
<>
|
||||||
|
<SidebarNavigationLink
|
||||||
|
to='/notifications'
|
||||||
|
icon={bellIcon}
|
||||||
|
activeIcon={bellFilledIcon}
|
||||||
|
count={notificationCount}
|
||||||
|
text={<FormattedMessage id='tabs_bar.notifications' defaultMessage='Notifications' />}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{renderMessagesLink()}
|
||||||
|
|
||||||
|
{features.groups && (
|
||||||
|
<SidebarNavigationLink
|
||||||
|
to='/groups'
|
||||||
|
icon={circlesIcon}
|
||||||
|
activeIcon={circlesFilledIcon}
|
||||||
|
text={<FormattedMessage id='tabs_bar.groups' defaultMessage='Groups' />}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<SidebarNavigationLink
|
||||||
|
to={`/@${account.acct}`}
|
||||||
|
icon={userIcon}
|
||||||
|
activeIcon={userFilledIcon}
|
||||||
|
text={<FormattedMessage id='tabs_bar.profile' defaultMessage='Profile' />}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SidebarNavigationLink
|
||||||
|
to='/settings'
|
||||||
|
icon={settingsIcon}
|
||||||
|
activeIcon={settingsFilledIcon}
|
||||||
|
text={<FormattedMessage id='tabs_bar.settings' defaultMessage='Settings' />}
|
||||||
|
count={settingsNotifications.size}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{account.staff && (
|
||||||
|
<SidebarNavigationLink
|
||||||
|
to='/soapbox/admin'
|
||||||
|
icon={dashboardIcon}
|
||||||
|
count={dashboardCount}
|
||||||
|
text={<FormattedMessage id='tabs_bar.dashboard' defaultMessage='Dashboard' />}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{(features.publicTimeline) && (
|
||||||
|
<>
|
||||||
|
{(account || !restrictUnauth.timelines.local) && (
|
||||||
|
<SidebarNavigationLink
|
||||||
|
to='/timeline/local'
|
||||||
|
icon={features.federating ? atIcon : worldIcon}
|
||||||
|
text={features.federating ? instance.domain : <FormattedMessage id='tabs_bar.global' defaultMessage='Global' />}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{(features.federating && (account || !restrictUnauth.timelines.federated)) && (
|
||||||
|
<SidebarNavigationLink
|
||||||
|
to='/timeline/global'
|
||||||
|
icon={worldIcon}
|
||||||
|
text={<FormattedMessage id='tabs_bar.global' defaultMessage='Global' />}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{menu.length > 0 && (
|
||||||
|
<DropdownMenu items={menu} placement='top'>
|
||||||
|
<SidebarNavigationLink
|
||||||
|
icon={dotsCircleHorizontalIcon}
|
||||||
|
text={<FormattedMessage id='tabs_bar.more' defaultMessage='More' />}
|
||||||
|
/>
|
||||||
|
</DropdownMenu>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
|
||||||
{account && (
|
{account && (
|
||||||
<>
|
<ComposeButton />
|
||||||
<SidebarNavigationLink
|
|
||||||
to='/notifications'
|
|
||||||
icon={bellIcon}
|
|
||||||
activeIcon={bellFilledIcon}
|
|
||||||
count={notificationCount}
|
|
||||||
text={<FormattedMessage id='tabs_bar.notifications' defaultMessage='Notifications' />}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{renderMessagesLink()}
|
|
||||||
|
|
||||||
{features.groups && (
|
|
||||||
<SidebarNavigationLink
|
|
||||||
to='/groups'
|
|
||||||
icon={circlesIcon}
|
|
||||||
activeIcon={circlesFilledIcon}
|
|
||||||
text={<FormattedMessage id='tabs_bar.groups' defaultMessage='Groups' />}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<SidebarNavigationLink
|
|
||||||
to={`/@${account.acct}`}
|
|
||||||
icon={userIcon}
|
|
||||||
activeIcon={userFilledIcon}
|
|
||||||
text={<FormattedMessage id='tabs_bar.profile' defaultMessage='Profile' />}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SidebarNavigationLink
|
|
||||||
to='/settings'
|
|
||||||
icon={settingsIcon}
|
|
||||||
activeIcon={settingsFilledIcon}
|
|
||||||
text={<FormattedMessage id='tabs_bar.settings' defaultMessage='Settings' />}
|
|
||||||
count={settingsNotifications.size}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{account.staff && (
|
|
||||||
<SidebarNavigationLink
|
|
||||||
to='/soapbox/admin'
|
|
||||||
icon={dashboardIcon}
|
|
||||||
count={dashboardCount}
|
|
||||||
text={<FormattedMessage id='tabs_bar.dashboard' defaultMessage='Dashboard' />}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{(features.publicTimeline) && (
|
|
||||||
<>
|
|
||||||
{(account || !restrictUnauth.timelines.local) && (
|
|
||||||
<SidebarNavigationLink
|
|
||||||
to='/timeline/local'
|
|
||||||
icon={features.federating ? atIcon : worldIcon}
|
|
||||||
text={features.federating ? instance.domain : <FormattedMessage id='tabs_bar.global' defaultMessage='Global' />}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{(features.federating && (account || !restrictUnauth.timelines.federated)) && (
|
|
||||||
<SidebarNavigationLink
|
|
||||||
to='/timeline/global'
|
|
||||||
icon={worldIcon}
|
|
||||||
text={<FormattedMessage id='tabs_bar.global' defaultMessage='Global' />}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{menu.length > 0 && (
|
|
||||||
<DropdownMenu items={menu} placement='top'>
|
|
||||||
<SidebarNavigationLink
|
|
||||||
icon={dotsCircleHorizontalIcon}
|
|
||||||
text={<FormattedMessage id='tabs_bar.more' defaultMessage='More' />}
|
|
||||||
/>
|
|
||||||
</DropdownMenu>
|
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
{account && (
|
{account && (
|
||||||
<ComposeButton />
|
<div className='mt-12'>
|
||||||
|
<ProfileDropdown account={account} placement='top'>
|
||||||
|
<div className='w-full p-2'>
|
||||||
|
<Account account={account} showProfileHoverCard={false} withLinkToProfile={false} hideActions />
|
||||||
|
</div>
|
||||||
|
</ProfileDropdown>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
|
|
|
@ -31,7 +31,7 @@ const Layout: LayoutComponent = ({ children }) => (
|
||||||
/** Left sidebar container in the UI. */
|
/** Left sidebar container in the UI. */
|
||||||
const Sidebar: React.FC<ISidebar> = ({ children }) => (
|
const Sidebar: React.FC<ISidebar> = ({ children }) => (
|
||||||
<div className='hidden lg:col-span-3 lg:block'>
|
<div className='hidden lg:col-span-3 lg:block'>
|
||||||
<StickyBox className='py-6'>
|
<StickyBox>
|
||||||
{children}
|
{children}
|
||||||
</StickyBox>
|
</StickyBox>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { useFloating } from '@floating-ui/react';
|
import { Placement, useFloating } from '@floating-ui/react';
|
||||||
import logoutIcon from '@tabler/icons/outline/logout.svg';
|
import logoutIcon from '@tabler/icons/outline/logout.svg';
|
||||||
import plusIcon from '@tabler/icons/outline/plus.svg';
|
import plusIcon from '@tabler/icons/outline/plus.svg';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
@ -29,6 +29,7 @@ const messages = defineMessages({
|
||||||
interface IProfileDropdown {
|
interface IProfileDropdown {
|
||||||
account: AccountEntity;
|
account: AccountEntity;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
placement?: Placement;
|
||||||
}
|
}
|
||||||
|
|
||||||
type IMenuItem = {
|
type IMenuItem = {
|
||||||
|
@ -39,13 +40,13 @@ type IMenuItem = {
|
||||||
action?: (event: React.MouseEvent) => void;
|
action?: (event: React.MouseEvent) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ProfileDropdown: React.FC<IProfileDropdown> = ({ account, children }) => {
|
const ProfileDropdown: React.FC<IProfileDropdown> = ({ account, children, placement = 'bottom-end' }) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const features = useFeatures();
|
const features = useFeatures();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const { x, y, strategy, refs } = useFloating<HTMLButtonElement>({ placement: 'bottom-end' });
|
const { x, y, strategy, refs } = useFloating<HTMLButtonElement>({ placement });
|
||||||
|
|
||||||
const getOtherAccounts = useCallback(makeGetOtherAccounts(), []);
|
const getOtherAccounts = useCallback(makeGetOtherAccounts(), []);
|
||||||
const otherAccounts = useAppSelector((state) => getOtherAccounts(state));
|
const otherAccounts = useAppSelector((state) => getOtherAccounts(state));
|
||||||
|
@ -117,7 +118,7 @@ const ProfileDropdown: React.FC<IProfileDropdown> = ({ account, children }) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
className='rounded-full focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 dark:ring-gray-800 dark:ring-offset-0 dark:focus:ring-primary-500'
|
className='w-full rounded-full focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 dark:ring-gray-800 dark:ring-offset-0 dark:focus:ring-primary-500'
|
||||||
type='button'
|
type='button'
|
||||||
ref={refs.setReference}
|
ref={refs.setReference}
|
||||||
onClick={toggleVisible}
|
onClick={toggleVisible}
|
||||||
|
|
Ładowanie…
Reference in New Issue