diff --git a/app/soapbox/features/feed-filtering/feed-carousel.tsx b/app/soapbox/features/feed-filtering/feed-carousel.tsx
index 481ae138e..23f417959 100644
--- a/app/soapbox/features/feed-filtering/feed-carousel.tsx
+++ b/app/soapbox/features/feed-filtering/feed-carousel.tsx
@@ -1,5 +1,5 @@
import classNames from 'clsx';
-import React, { useEffect, useState } from 'react';
+import React, { useEffect, useMemo, useState } from 'react';
import { FormattedMessage } from 'react-intl';
import { replaceHomeTimeline } from 'soapbox/actions/timelines';
@@ -9,7 +9,10 @@ import { Avatar, useCarouselAvatars, useMarkAsSeen } from 'soapbox/queries/carou
import { Card, HStack, Icon, Stack, Text } from '../../components/ui';
import PlaceholderAvatar from '../placeholder/components/placeholder-avatar';
-const CarouselItem = ({ avatar, seen, onViewed }: { avatar: Avatar, seen: boolean, onViewed: (account_id: string) => void }) => {
+const CarouselItem = React.forwardRef((
+ { avatar, seen, onViewed, onPinned }: { avatar: Avatar, seen: boolean, onViewed: (account_id: string) => void, onPinned?: (avatar: null | Avatar) => void },
+ ref: any,
+) => {
const dispatch = useAppDispatch();
const markAsSeen = useMarkAsSeen();
@@ -28,7 +31,15 @@ const CarouselItem = ({ avatar, seen, onViewed }: { avatar: Avatar, seen: boolea
if (isSelected) {
dispatch(replaceHomeTimeline(null, { maxId: null }, () => setLoading(false)));
+
+ if (onPinned) {
+ onPinned(null);
+ }
} else {
+ if (onPinned) {
+ onPinned(avatar);
+ }
+
onViewed(avatar.account_id);
markAsSeen.mutate(avatar.account_id);
dispatch(replaceHomeTimeline(avatar.account_id, { maxId: null }, () => setLoading(false)));
@@ -37,14 +48,15 @@ const CarouselItem = ({ avatar, seen, onViewed }: { avatar: Avatar, seen: boolea
return (
-
-
+
+
{isSelected && (
@@ -54,7 +66,7 @@ const CarouselItem = ({ avatar, seen, onViewed }: { avatar: Avatar, seen: boolea
);
-};
+});
const FeedCarousel = () => {
const { data: avatars, isFetching, isError } = useCarouselAvatars();
- const [cardRef, setCardRef, { width }] = useDimensions();
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ const [_ref, setContainerRef, { width }] = useDimensions();
const [seenAccountIds, setSeenAccountIds] = useState
([]);
const [pageSize, setPageSize] = useState(0);
const [currentPage, setCurrentPage] = useState(1);
+ const [pinnedAvatar, setPinnedAvatar] = useState(null);
+
+ const avatarsToList = useMemo(() => {
+ const list = avatars.filter((avatar) => avatar.account_id !== pinnedAvatar?.account_id);
+ if (pinnedAvatar) {
+ return [null, ...list];
+ }
+
+ return list;
+ }, [avatars, pinnedAvatar]);
const numberOfPages = Math.ceil(avatars.length / pageSize);
- const widthPerAvatar = (cardRef?.scrollWidth || 0) / avatars.length;
+ const widthPerAvatar = width / (Math.floor(width / 80));
const hasNextPage = currentPage < numberOfPages && numberOfPages > 1;
const hasPrevPage = currentPage > 1 && numberOfPages > 1;
@@ -118,67 +141,100 @@ const FeedCarousel = () => {
);
}
- if (avatars.length === 0) {
- return null;
- }
-
return (
-
-
- {hasPrevPage && (
-
- )}
+
+
+
+
+
-
- {isFetching ? (
- new Array(pageSize).fill(0).map((_, idx) => (
-
- ))
- ) : (
- avatars.map((avatar) => (
+
+ {pinnedAvatar ? (
+
setPinnedAvatar(avatar)}
/>
- ))
- )}
-
-
- {hasNextPage && (
-
- )}
-
-
+ ) : null}
+
+
+ {isFetching ? (
+ new Array(20).fill(0).map((_, idx) => (
+
+ ))
+ ) : (
+ avatarsToList.map((avatar: any, index) => (
+
+ {avatar === null ? (
+
+
+
+ ) : (
+
{
+ setPinnedAvatar(null);
+ setTimeout(() => {
+ setPinnedAvatar(avatar);
+ }, 1);
+ }}
+ onViewed={markAsSeen}
+ />
+ )}
+
+ ))
+ )}
+
+
+
+
+
+
+
+
);
};
diff --git a/app/soapbox/features/placeholder/components/placeholder-avatar.tsx b/app/soapbox/features/placeholder/components/placeholder-avatar.tsx
index 7d9fa62f0..9d2e1a3ec 100644
--- a/app/soapbox/features/placeholder/components/placeholder-avatar.tsx
+++ b/app/soapbox/features/placeholder/components/placeholder-avatar.tsx
@@ -21,14 +21,14 @@ const PlaceholderAvatar: React.FC
= ({ size, withText = fals
}, [size]);
return (
-
+
{withText && (
-
+
)}
);