mirror of
https://github.com/mastodon/mastodon.git
synced 2026-09-27 17:07:48 -05:00
Redesign: Allow dismissing announcements panel (#40688)
This commit is contained in:
@@ -23,6 +23,8 @@ export const ANNOUNCEMENTS_REACTION_REMOVE_FAIL = 'ANNOUNCEMENTS_REACTION_REM
|
||||
export const ANNOUNCEMENTS_REACTION_UPDATE = 'ANNOUNCEMENTS_REACTION_UPDATE';
|
||||
|
||||
export const ANNOUNCEMENTS_TOGGLE_SHOW = 'ANNOUNCEMENTS_TOGGLE_SHOW';
|
||||
export const ANNOUNCEMENTS_SHOW = 'ANNOUNCEMENTS_TOGGLE_SHOW';
|
||||
export const ANNOUNCEMENTS_HIDE = 'ANNOUNCEMENTS_TOGGLE_SHOW';
|
||||
|
||||
const noOp = () => {};
|
||||
|
||||
@@ -175,6 +177,14 @@ export const toggleShowAnnouncements = () => ({
|
||||
type: ANNOUNCEMENTS_TOGGLE_SHOW,
|
||||
});
|
||||
|
||||
export const hideAnnouncements = () => ({
|
||||
type: ANNOUNCEMENTS_HIDE,
|
||||
});
|
||||
|
||||
export const showAnnouncements = () => ({
|
||||
type: ANNOUNCEMENTS_SHOW,
|
||||
});
|
||||
|
||||
export const deleteAnnouncement = id => ({
|
||||
type: ANNOUNCEMENTS_DELETE,
|
||||
id,
|
||||
|
||||
@@ -57,6 +57,7 @@ export const Announcement: FC<AnnouncementProps> = ({
|
||||
<span>
|
||||
{' · '}
|
||||
<Timestamp announcement={announcement} />
|
||||
{!isVisuallyRead && <span className='announcements__unread' />}
|
||||
</span>
|
||||
</strong>
|
||||
|
||||
@@ -67,8 +68,6 @@ export const Announcement: FC<AnnouncementProps> = ({
|
||||
/>
|
||||
|
||||
<ReactionsBar reactions={announcement.reactions} id={announcement.id} />
|
||||
|
||||
{!isVisuallyRead && <span className='announcements__unread' />}
|
||||
</AnimateEmojiProvider>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import type {
|
||||
List as ImmutableList,
|
||||
Record as ImmutableRecord,
|
||||
} from 'immutable';
|
||||
|
||||
import { fetchAnnouncements } from '@/mastodon/actions/announcements';
|
||||
import {
|
||||
fetchAnnouncements,
|
||||
showAnnouncements,
|
||||
} from '@/mastodon/actions/announcements';
|
||||
import type { ApiAnnouncementJSON } from '@/mastodon/api_types/announcements';
|
||||
import { useAppDispatch, useAppSelector } from '@/mastodon/store';
|
||||
import { isRedesignEnabled } from '@/mastodon/utils/environment';
|
||||
|
||||
type AnnouncementItem = ImmutableRecord<{
|
||||
id: string;
|
||||
starts_at: string;
|
||||
published_at: string;
|
||||
read: boolean;
|
||||
}>;
|
||||
type AnnouncementItem = ImmutableRecord<ApiAnnouncementJSON>;
|
||||
|
||||
type AnnouncementsState = ImmutableRecord<{
|
||||
items: ImmutableList<AnnouncementItem>;
|
||||
@@ -39,17 +39,11 @@ export function useHasAnnouncements({
|
||||
);
|
||||
const hasUnreadAnnouncements = !!unreadAnnouncementCount;
|
||||
|
||||
// Announcements display is transient – it's toggled on when
|
||||
// there are unread announcements or the `show` Redux state is
|
||||
// enabled, but resets to hidden when the page is left (by virtue
|
||||
// of React simply losing this state).
|
||||
const [showAnnouncements, setShowAnnouncements] = useState(false);
|
||||
if (
|
||||
(hasUnreadAnnouncements || shouldShowAnnouncements) &&
|
||||
!showAnnouncements
|
||||
) {
|
||||
setShowAnnouncements(true);
|
||||
}
|
||||
useEffect(() => {
|
||||
if (hasUnreadAnnouncements && isRedesignEnabled()) {
|
||||
dispatch(showAnnouncements());
|
||||
}
|
||||
}, [hasUnreadAnnouncements, dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
const timeout = setTimeout(() => {
|
||||
@@ -66,7 +60,7 @@ export function useHasAnnouncements({
|
||||
return {
|
||||
hasAnnouncements,
|
||||
hasUnreadAnnouncements,
|
||||
showAnnouncements,
|
||||
shouldShowAnnouncements,
|
||||
unreadAnnouncementCount,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,15 +1,26 @@
|
||||
import { useCallback } from 'react';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import type { Map, List } from 'immutable';
|
||||
|
||||
import { XIcon } from '@phosphor-icons/react';
|
||||
|
||||
import elephantUIPlane from '@/images/elephant_ui_plane.svg';
|
||||
import { hideAnnouncements } from '@/mastodon/actions/announcements';
|
||||
import { IconButton } from '@/mastodon/components/button/redesign';
|
||||
import type { RenderSlideFn } from '@/mastodon/components/carousel';
|
||||
import { Carousel } from '@/mastodon/components/carousel';
|
||||
import { CustomEmojiProvider } from '@/mastodon/components/emoji/context';
|
||||
import { useCustomEmojis } from '@/mastodon/hooks/useCustomEmojis';
|
||||
import { mascot } from '@/mastodon/initial_state';
|
||||
import { createAppSelector, useAppSelector } from '@/mastodon/store';
|
||||
import {
|
||||
createAppSelector,
|
||||
useAppDispatch,
|
||||
useAppSelector,
|
||||
} from '@/mastodon/store';
|
||||
import { isRedesignEnabled } from '@/mastodon/utils/environment';
|
||||
|
||||
import type { IAnnouncement } from './announcement';
|
||||
import { Announcement } from './announcement';
|
||||
@@ -23,9 +34,14 @@ const announcementSelector = createAppSelector(
|
||||
);
|
||||
|
||||
export const Announcements: FC = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const announcements = useAppSelector(announcementSelector);
|
||||
const emojis = useCustomEmojis();
|
||||
|
||||
const closeAnnouncements = useCallback(() => {
|
||||
dispatch(hideAnnouncements());
|
||||
}, [dispatch]);
|
||||
|
||||
const renderSlide: RenderSlideFn<{
|
||||
id: string;
|
||||
announcement: IAnnouncement;
|
||||
@@ -53,6 +69,18 @@ export const Announcements: FC = () => {
|
||||
src={mascot ?? elephantUIPlane}
|
||||
/>
|
||||
|
||||
{isRedesignEnabled() && (
|
||||
<IconButton
|
||||
icon={XIcon}
|
||||
onClick={closeAnnouncements}
|
||||
size='sm'
|
||||
variant='ghost'
|
||||
className='announcements__close-button'
|
||||
>
|
||||
<FormattedMessage id='lightbox.close' defaultMessage='Close' />
|
||||
</IconButton>
|
||||
)}
|
||||
|
||||
<CustomEmojiProvider emojis={emojis}>
|
||||
<Carousel
|
||||
classNamePrefix='announcements'
|
||||
|
||||
@@ -4,7 +4,7 @@ import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { NewspaperIcon } from '@phosphor-icons/react';
|
||||
|
||||
import { toggleShowAnnouncements } from '@/mastodon/actions/announcements';
|
||||
import { showAnnouncements } from '@/mastodon/actions/announcements';
|
||||
import { closeModal } from '@/mastodon/actions/modal';
|
||||
import { Button } from '@/mastodon/components/button/redesign';
|
||||
import { useAppDispatch } from '@/mastodon/store';
|
||||
@@ -14,18 +14,18 @@ import { useHasAnnouncements } from '../../announcements/hooks';
|
||||
|
||||
export const ShowAnnouncementsButton: React.FC = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { hasAnnouncements, showAnnouncements } = useHasAnnouncements({
|
||||
const { hasAnnouncements, shouldShowAnnouncements } = useHasAnnouncements({
|
||||
fetch: false,
|
||||
});
|
||||
|
||||
const handleClick = useCallback(() => {
|
||||
dispatch(toggleShowAnnouncements());
|
||||
dispatch(showAnnouncements());
|
||||
dispatch(
|
||||
closeModal({ modalType: 'NOTIFICATION_SETTINGS', ignoreFocus: false }),
|
||||
);
|
||||
}, [dispatch]);
|
||||
|
||||
if (!isRedesignEnabled() || !hasAnnouncements || showAnnouncements) {
|
||||
if (!isRedesignEnabled() || !hasAnnouncements || shouldShowAnnouncements) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -222,12 +222,12 @@ export const Notifications: React.FC<{
|
||||
);
|
||||
}, [notifications, isLoading, hasMore, lastReadId, handleLoadGap]);
|
||||
|
||||
const { showAnnouncements } = useHasAnnouncements();
|
||||
const { shouldShowAnnouncements } = useHasAnnouncements();
|
||||
|
||||
const prepend = (
|
||||
<>
|
||||
{needsNotificationPermission && <NotificationsPermissionBanner />}
|
||||
{showAnnouncements && <Announcements />}
|
||||
{shouldShowAnnouncements && <Announcements />}
|
||||
<FilteredNotificationsBanner />
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -13,6 +13,8 @@ import {
|
||||
ANNOUNCEMENTS_TOGGLE_SHOW,
|
||||
ANNOUNCEMENTS_DELETE,
|
||||
ANNOUNCEMENTS_DISMISS_SUCCESS,
|
||||
ANNOUNCEMENTS_SHOW,
|
||||
ANNOUNCEMENTS_HIDE,
|
||||
} from '../actions/announcements';
|
||||
|
||||
const initialState = ImmutableMap({
|
||||
@@ -64,6 +66,10 @@ export default function announcementsReducer(state = initialState, action) {
|
||||
return state.withMutations(map => {
|
||||
map.set('show', !map.get('show'));
|
||||
});
|
||||
case ANNOUNCEMENTS_SHOW:
|
||||
return state.set('show', true);
|
||||
case ANNOUNCEMENTS_HIDE:
|
||||
return state.set('show', false);
|
||||
case ANNOUNCEMENTS_FETCH_REQUEST:
|
||||
return state.set('isLoading', true);
|
||||
case ANNOUNCEMENTS_FETCH_SUCCESS:
|
||||
|
||||
@@ -8543,6 +8543,13 @@ noscript {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&__close-button {
|
||||
position: absolute;
|
||||
top: var(--space-2);
|
||||
inset-inline-end: var(--space-2);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&__root {
|
||||
background: var(--color-bg-brand-softest);
|
||||
font-size: 13px;
|
||||
@@ -8584,18 +8591,17 @@ noscript {
|
||||
display: block;
|
||||
font-weight: 500;
|
||||
margin-bottom: 10px;
|
||||
padding-inline-end: 18px;
|
||||
padding-inline-end: 28px;
|
||||
}
|
||||
|
||||
&__unread {
|
||||
position: absolute;
|
||||
top: 19px;
|
||||
inset-inline-end: 19px;
|
||||
display: block;
|
||||
background: var(--color-text-brand);
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
width: 0.625rem;
|
||||
height: 0.625rem;
|
||||
margin-inline-start: 8px;
|
||||
border-radius: 50%;
|
||||
vertical-align: 0.05em;
|
||||
background: var(--color-text-brand);
|
||||
}
|
||||
|
||||
&__pagination {
|
||||
|
||||
Reference in New Issue
Block a user