Files
mastodon/app/javascript/flavours/glitch/components/featured_carousel.tsx
Echo 9f8e812c56 [Glitch] Refactor carousel components
Port e7cd5a430e to glitch-soc

Co-authored-by: diondiondion <mail@diondiondion.com>
Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-10-31 18:13:35 +01:00

91 lines
2.7 KiB
TypeScript

import { useCallback, useEffect, useId } from 'react';
import { defineMessages, FormattedMessage } from 'react-intl';
import type { Map as ImmutableMap } from 'immutable';
import { List as ImmutableList } from 'immutable';
import { expandAccountFeaturedTimeline } from '@/flavours/glitch/actions/timelines';
import { Icon } from '@/flavours/glitch/components/icon';
import { StatusQuoteManager } from '@/flavours/glitch/components/status_quoted';
import {
createAppSelector,
useAppDispatch,
useAppSelector,
} from '@/flavours/glitch/store';
import PushPinIcon from '@/material-icons/400-24px/push_pin.svg?react';
import { Carousel } from './carousel';
const pinnedStatusesSelector = createAppSelector(
[
(state, accountId: string, tagged?: string) =>
(state.timelines as ImmutableMap<string, unknown>).getIn(
[`account:${accountId}:pinned${tagged ? `:${tagged}` : ''}`, 'items'],
ImmutableList(),
) as ImmutableList<string>,
],
(items) => items.toArray().map((id) => ({ id })),
);
const messages = defineMessages({
previous: { id: 'lightbox.previous', defaultMessage: 'Previous' },
next: { id: 'lightbox.next', defaultMessage: 'Next' },
current: {
id: 'featured_carousel.current',
defaultMessage: '<sr>Post</sr> {current, number} / {max, number}',
},
slide: {
id: 'featured_carousel.slide',
defaultMessage: 'Post {current, number} of {max, number}',
},
});
export const FeaturedCarousel: React.FC<{
accountId: string;
tagged?: string;
}> = ({ accountId, tagged }) => {
const accessibilityId = useId();
// Load pinned statuses
const dispatch = useAppDispatch();
useEffect(() => {
if (accountId) {
void dispatch(expandAccountFeaturedTimeline(accountId, { tagged }));
}
}, [accountId, dispatch, tagged]);
const pinnedStatuses = useAppSelector((state) =>
pinnedStatusesSelector(state, accountId, tagged),
);
const renderSlide = useCallback(
({ id }: { id: string }) => (
<StatusQuoteManager id={id} contextType='account' withCounters />
),
[],
);
if (!accountId || pinnedStatuses.length === 0) {
return null;
}
return (
<Carousel
items={pinnedStatuses}
renderItem={renderSlide}
aria-labelledby={`${accessibilityId}-title`}
classNamePrefix='featured-carousel'
messages={messages}
>
<h4 className='featured-carousel__title' id={`${accessibilityId}-title`}>
<Icon id='thumb-tack' icon={PushPinIcon} />
<FormattedMessage
id='featured_carousel.header'
defaultMessage='{count, plural, one {Pinned Post} other {Pinned Posts}}'
values={{ count: pinnedStatuses.length }}
/>
</h4>
</Carousel>
);
};