mirror of
https://github.com/mastodon/mastodon.git
synced 2026-09-13 15:35:54 -05:00
Redesign: Add "custom feeds" and "followed hashtags" sections to main navigation (#40248)
This commit is contained in:
@@ -43,6 +43,8 @@
|
||||
|
||||
// Override default anchor styles
|
||||
a.base {
|
||||
text-decoration: none;
|
||||
|
||||
&:focus {
|
||||
border-radius: var(--radius-round);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
@use '@/styles/mastodon/mixins';
|
||||
|
||||
.root {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
.root {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: var(--space-xl) var(--space-lg);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
|
||||
@@ -6,20 +6,25 @@ import {
|
||||
PenNibIcon,
|
||||
HouseIcon,
|
||||
MagnifyingGlassIcon,
|
||||
RssSimpleIcon,
|
||||
BellIcon,
|
||||
ChatCircleIcon,
|
||||
BookmarkSimpleIcon,
|
||||
} from '@phosphor-icons/react';
|
||||
|
||||
import FediIcon from '@/images/icons/icon_fediverse.svg?react';
|
||||
import { fetchLists } from '@/mastodon/actions/lists';
|
||||
import { fetchFollowedHashtags } from '@/mastodon/actions/tags_typed';
|
||||
import { useIdentity } from '@/mastodon/identity_context';
|
||||
import { openNewComposer } from '@/mastodon/reducers/slices/composer';
|
||||
import { getOrderedLists } from '@/mastodon/selectors/lists';
|
||||
import { selectUnreadNotificationGroupsCount } from '@/mastodon/selectors/notifications';
|
||||
import { useAppDispatch, useAppSelector } from '@/mastodon/store';
|
||||
|
||||
import { NavigationAccountCardAndMenu } from './account_card_and_menu';
|
||||
import { NavigationFooterLinks } from './footer_links';
|
||||
import { NavigationHeader } from './header';
|
||||
import { ListSection } from './list_section';
|
||||
import { NavigationLink } from './navigation_link';
|
||||
import classes from './styles.module.scss';
|
||||
|
||||
@@ -32,6 +37,32 @@ const messages = defineMessages({
|
||||
},
|
||||
});
|
||||
|
||||
function useCustomFeeds() {
|
||||
const dispatch = useAppDispatch();
|
||||
const customFeeds = useAppSelector((state) => getOrderedLists(state));
|
||||
|
||||
useEffect(() => {
|
||||
void dispatch(fetchLists());
|
||||
}, [dispatch]);
|
||||
|
||||
return {
|
||||
customFeeds,
|
||||
};
|
||||
}
|
||||
|
||||
function useFollowedHashtags() {
|
||||
const dispatch = useAppDispatch();
|
||||
const { tags, stale } = useAppSelector((state) => state.followedTags);
|
||||
|
||||
useEffect(() => {
|
||||
if (stale) {
|
||||
void dispatch(fetchFollowedHashtags());
|
||||
}
|
||||
}, [dispatch, stale]);
|
||||
|
||||
return { followedHashtags: tags };
|
||||
}
|
||||
|
||||
export const RedesignNavigationPanel: React.FC<{ siteName?: string }> = ({
|
||||
siteName,
|
||||
}) => {
|
||||
@@ -46,6 +77,9 @@ export const RedesignNavigationPanel: React.FC<{ siteName?: string }> = ({
|
||||
dispatch(openNewComposer({ type: 'post' }));
|
||||
}, [dispatch]);
|
||||
|
||||
const { customFeeds } = useCustomFeeds();
|
||||
const { followedHashtags } = useFollowedHashtags();
|
||||
|
||||
return (
|
||||
<nav
|
||||
className={classes.root}
|
||||
@@ -75,12 +109,75 @@ export const RedesignNavigationPanel: React.FC<{ siteName?: string }> = ({
|
||||
defaultMessage='Explore'
|
||||
/>
|
||||
</NavigationLink>
|
||||
<NavigationLink to='/public/local' iconComponent={FediIcon}>
|
||||
<NavigationLink
|
||||
withSpaceAfter
|
||||
to='/public/local'
|
||||
iconComponent={FediIcon}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='tabs_bar.fediverse_feeds'
|
||||
defaultMessage='Fediverse Feeds'
|
||||
/>
|
||||
</NavigationLink>
|
||||
<ListSection
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='tabs_bar.custom_feeds'
|
||||
defaultMessage='Custom feeds'
|
||||
/>
|
||||
}
|
||||
action={{
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id='tabs_bar.create_custom_feed'
|
||||
defaultMessage='Create feed'
|
||||
/>
|
||||
),
|
||||
link: '/lists/new',
|
||||
}}
|
||||
emptyMessage={
|
||||
<FormattedMessage
|
||||
id='tabs_bar.custom_feeds_empty'
|
||||
defaultMessage='You have no custom feeds yet.'
|
||||
/>
|
||||
}
|
||||
>
|
||||
{customFeeds.map((feed) => (
|
||||
<NavigationLink
|
||||
key={feed.id}
|
||||
to={`/lists/${feed.id}`}
|
||||
iconComponent={RssSimpleIcon}
|
||||
>
|
||||
{feed.title}
|
||||
</NavigationLink>
|
||||
))}
|
||||
</ListSection>
|
||||
|
||||
{followedHashtags.length > 0 && (
|
||||
<ListSection
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='navigation_bar.followed_tags'
|
||||
defaultMessage='Followed hashtags'
|
||||
/>
|
||||
}
|
||||
action={{
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id='navigation_bar.followed_tags_view_all'
|
||||
defaultMessage='View all'
|
||||
/>
|
||||
),
|
||||
link: '/followed_tags',
|
||||
}}
|
||||
>
|
||||
{followedHashtags.slice(0, 4).map((tag) => (
|
||||
<NavigationLink key={tag.name} to={`/tags/${tag.name}`}>
|
||||
#{tag.name}
|
||||
</NavigationLink>
|
||||
))}
|
||||
</ListSection>
|
||||
)}
|
||||
</ul>
|
||||
<footer className={classes.footer}>
|
||||
<ul className={classes.footerNav}>
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
@use '@/styles/mastodon/mixins';
|
||||
|
||||
.root {
|
||||
&:not(:last-child) {
|
||||
margin-bottom: var(--space-md);
|
||||
}
|
||||
}
|
||||
|
||||
.titleWrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-inline: var(--space-xs);
|
||||
padding-block: calc(var(--space-xs) - var(--space-3xs));
|
||||
}
|
||||
|
||||
.title {
|
||||
@include mixins.type-label-sm;
|
||||
|
||||
color: var(--color-text-secondary);
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.action {
|
||||
margin-inline-start: auto;
|
||||
}
|
||||
|
||||
.emptyState {
|
||||
@include mixins.type-label-sm;
|
||||
|
||||
padding-inline: var(--space-xs);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
import { Button } from '@/mastodon/components/button/redesign';
|
||||
import type { MastodonLocationDescriptor } from '@/mastodon/components/router';
|
||||
import { hasReactChildren } from '@/mastodon/utils/has_react_children';
|
||||
|
||||
import classes from './list_section.module.scss';
|
||||
|
||||
export const ListSection: React.FC<{
|
||||
title: ReactNode;
|
||||
action?: {
|
||||
label: ReactNode;
|
||||
link: MastodonLocationDescriptor;
|
||||
};
|
||||
children: ReactNode;
|
||||
emptyMessage?: ReactNode;
|
||||
}> = ({ title, action, children, emptyMessage }) => {
|
||||
const hasContent = hasReactChildren(children);
|
||||
|
||||
return (
|
||||
<li className={classes.root}>
|
||||
<div className={classes.titleWrapper}>
|
||||
<span className={classes.title}>{title}</span>
|
||||
{action && (
|
||||
<Button
|
||||
as='link'
|
||||
to={action.link}
|
||||
size='xs'
|
||||
className={classes.action}
|
||||
>
|
||||
{action.label}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
{hasContent ? (
|
||||
<ul>{children}</ul>
|
||||
) : (
|
||||
emptyMessage && <div className={classes.emptyState}>{emptyMessage}</div>
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
};
|
||||
@@ -12,7 +12,7 @@ import classes from './navigation_link.module.scss';
|
||||
|
||||
type NavigationLinkProps = {
|
||||
stacked?: boolean;
|
||||
iconComponent: Icon | React.FC<SVGProps<SVGSVGElement>>;
|
||||
iconComponent?: Icon | React.FC<SVGProps<SVGSVGElement>>;
|
||||
badgeCount?: number;
|
||||
withSpaceAfter?: boolean;
|
||||
} & (
|
||||
@@ -55,9 +55,11 @@ export const NavigationLink: React.FC<NavigationLinkProps> = ({
|
||||
{...otherProps}
|
||||
className={classNames(classes.link, stacked && classes.linkStacked)}
|
||||
>
|
||||
<span className={classes.icon}>
|
||||
<IconComp size={20} weight={isActive ? 'fill' : undefined} />
|
||||
</span>
|
||||
{IconComp && (
|
||||
<span className={classes.icon}>
|
||||
<IconComp size={20} weight={isActive ? 'fill' : undefined} />
|
||||
</span>
|
||||
)}
|
||||
<span className={classes.label}>{children}</span>
|
||||
{badgeCount > 0 && (
|
||||
<Badge
|
||||
|
||||
@@ -986,6 +986,7 @@
|
||||
"navigation_bar.filters": "Muted words",
|
||||
"navigation_bar.follow_requests": "Follow requests",
|
||||
"navigation_bar.followed_tags": "Followed hashtags",
|
||||
"navigation_bar.followed_tags_view_all": "View all",
|
||||
"navigation_bar.followers_and_following": "Followers & Following",
|
||||
"navigation_bar.follows_and_followers": "Follows and followers",
|
||||
"navigation_bar.import_export": "Import and export",
|
||||
@@ -1398,6 +1399,9 @@
|
||||
"subscribed_languages.lead": "Only posts in selected languages will appear on your home and list timelines after the change. Select none to receive posts in all languages.",
|
||||
"subscribed_languages.save": "Save changes",
|
||||
"subscribed_languages.target": "Change subscribed languages for {target}",
|
||||
"tabs_bar.create_custom_feed": "Create feed",
|
||||
"tabs_bar.custom_feeds": "Custom feeds",
|
||||
"tabs_bar.custom_feeds_empty": "You have no custom feeds yet.",
|
||||
"tabs_bar.explore": "Explore",
|
||||
"tabs_bar.fediverse_feeds": "Fediverse Feeds",
|
||||
"tabs_bar.home": "Home",
|
||||
|
||||
Reference in New Issue
Block a user