Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GAL-5440] add Suggested Profiles in curated Feed web #2429

Merged
merged 12 commits into from
Apr 19, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions apps/web/src/components/Badge/Badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { graphql, useFragment } from 'react-relay';
import styled from 'styled-components';

import GalleryLink from '~/components/core/GalleryLink/GalleryLink';
import IconContainer from '~/components/core/IconContainer';
import IconContainer, { IconSize } from '~/components/core/IconContainer';
import Tooltip from '~/components/Tooltip/Tooltip';
import { BadgeFragment$key } from '~/generated/BadgeFragment.graphql';
import TopActivityBadgeIcon from '~/icons/TopActivityBadgeIcon';
Expand All @@ -16,9 +16,10 @@ import { getUrlForCommunityDangerously } from '~/utils/getCommunityUrl';
type Props = {
badgeRef: BadgeFragment$key;
eventContext: GalleryElementTrackingProps['eventContext'];
size?: IconSize;
};

export default function Badge({ badgeRef, eventContext }: Props) {
export default function Badge({ badgeRef, eventContext, size = 'md' as IconSize }: Props) {
const [showTooltip, setShowTooltip] = useState(false);

const badge = useFragment(
Expand Down Expand Up @@ -88,7 +89,7 @@ export default function Badge({ badgeRef, eventContext }: Props) {
<>
<StyledTooltip text={name || ''} showTooltip={showTooltip} />
<IconContainer
size="md"
size={size}
variant="default"
icon={
<StyledBadge
Expand Down
1 change: 1 addition & 0 deletions apps/web/src/components/Feed/CuratedFeed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export default function CuratedFeed({ queryRef }: Props) {
loadNextPage={loadNextPage}
hasNext={hasPrevious}
feedMode={'WORLDWIDE'}
showSuggestedProfiles={true}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can just pass in showSuggestedProfiles without setting to true

  feedMode={'WORLDWIDE'}
  showSuggestedProfiles
  ...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

made the change!

/>
);
}
92 changes: 82 additions & 10 deletions apps/web/src/components/Feed/FeedList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import {
} from 'react-virtualized';
import { MeasuredCellParent } from 'react-virtualized/dist/es/CellMeasurer';

import FeedSuggestedProfileSection from '~/components/Feed/FeedSuggestedProfileSection';
import { FeedMode } from '~/components/Feed/types';
import { FeedListEventDataFragment$key } from '~/generated/FeedListEventDataFragment.graphql';
import { FeedListFragment$key } from '~/generated/FeedListFragment.graphql';
import { useIsMobileOrMobileLargeWindowWidth } from '~/hooks/useWindowSize';

import FeedEventItem from './FeedEventItem';
import { PostItemWithBoundary as PostItem } from './PostItem';
Expand All @@ -23,6 +25,7 @@ type Props = {
queryRef: FeedListFragment$key;
feedEventRefs: FeedListEventDataFragment$key;
feedMode?: FeedMode;
showSuggestedProfiles?: boolean;
};

export default function FeedList({
Expand All @@ -31,12 +34,14 @@ export default function FeedList({
hasNext,
queryRef,
feedMode,
showSuggestedProfiles = false,
}: Props) {
const query = useFragment(
graphql`
fragment FeedListFragment on Query {
...PostItemWithErrorBoundaryQueryFragment
...FeedEventItemWithErrorBoundaryQueryFragment
...FeedSuggestedProfileSectionWithBoundaryFragment
}
`,
queryRef
Expand All @@ -59,11 +64,36 @@ export default function FeedList({
feedEventRefs
);

const isMobileOrMobileLargeWindowWidth = useIsMobileOrMobileLargeWindowWidth();
const suggestedProfileSectionHeight = useMemo(
() => (isMobileOrMobileLargeWindowWidth ? 320 : 360),
[isMobileOrMobileLargeWindowWidth]
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: don't need to memoize primitive types; memoization mostly meant for complex types like computed values, arrays, objects.

can just do:

const suggestedProfileSectionHeight = isMobileOrMobileLargeWindowWidth ? 320 : 360


// insert suggested profiles in between posts if showSuggestedProfiles is true
const finalFeedData = useMemo(() => {
const suggestedProfileSectionIdx = 8;
if (showSuggestedProfiles && feedData?.length >= suggestedProfileSectionIdx) {
const suggestedProfileSectionData = {
__typename: 'SuggestedProfileSection',
dbid: '12345',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's use uuid(), we use it in a couple other places in the repo

};

const insertAt = feedData.length - suggestedProfileSectionIdx;
return [
...feedData.slice(0, insertAt),
suggestedProfileSectionData,
...feedData.slice(insertAt),
];
}
return feedData;
}, [feedData, showSuggestedProfiles]);

// Keep the current feed data in a ref so we can access it below in the
// CellMeasurerCache's keyMapper without having to create a new cache
// every time the feed data changes.
const feedDataRef = useRef(feedData);
feedDataRef.current = feedData;
const feedDataRef = useRef(finalFeedData);
feedDataRef.current = finalFeedData;

const measurerCache = useMemo(() => {
return new CellMeasurerCache({
Expand All @@ -83,8 +113,8 @@ export default function FeedList({

// Function responsible for tracking the loaded state of each row.
const isRowLoaded = useCallback(
({ index }: { index: number }) => !hasNext || Boolean(feedData[index]),
[feedData, hasNext]
({ index }: { index: number }) => !hasNext || Boolean(finalFeedData[index]),
[finalFeedData, hasNext]
);

const virtualizedListRef = useRef<List | null>(null);
Expand Down Expand Up @@ -114,7 +144,7 @@ export default function FeedList({
return <div />;
}
// graphql returns the oldest event at the top of the list, so display in opposite order
const content = feedData[feedData.length - index - 1];
const content = finalFeedData[finalFeedData.length - index - 1];

// Better safe than sorry :)
if (!content) {
Expand Down Expand Up @@ -179,9 +209,28 @@ export default function FeedList({
);
}

if (content.__typename === 'SuggestedProfileSection') {
return (
<CellMeasurer
cache={measurerCache}
columnIndex={0}
rowIndex={index}
key={key}
parent={parent}
>
{({ registerChild }) => (
// @ts-expect-error: this is the suggested usage of registerChild
<div ref={registerChild} style={style} key={key}>
<FeedSuggestedProfileSection queryRef={query} />
</div>
)}
</CellMeasurer>
);
}

return null;
},
[feedData, feedMode, handlePotentialLayoutShift, isRowLoaded, measurerCache, query]
[finalFeedData, feedMode, handlePotentialLayoutShift, isRowLoaded, measurerCache, query]
);

const [, setIsLoading] = useState(false);
Expand All @@ -196,10 +245,31 @@ export default function FeedList({
function recalculateHeightsWhenEventsChange() {
virtualizedListRef.current?.recomputeRowHeights();
},
[feedData, measurerCache]
[finalFeedData, measurerCache]
);

const rowCount = hasNext ? feedData.length + 1 : feedData.length;
const rowCount = hasNext ? finalFeedData.length + 1 : finalFeedData.length;

const rowHeight = useCallback(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice work figuring this out!

({ index }) => {
if (!finalFeedData) {
return DEFAULT_ROW_HEIGHT;
}

// Determine the actual data index based on reverse order logic used in rowRenderer
const dataIndex = finalFeedData.length - index - 1;
const item = finalFeedData[dataIndex];

// Return static height for SuggestedProfileSection
if (item?.__typename === 'SuggestedProfileSection') {
return suggestedProfileSectionHeight;
}

// Return dynamic height for other types of content
return measurerCache.rowHeight({ index });
},
[finalFeedData, suggestedProfileSectionHeight, measurerCache]
);

return (
<WindowScroller>
Expand All @@ -224,8 +294,8 @@ export default function FeedList({
width={width}
height={height}
rowRenderer={rowRenderer}
rowCount={feedData.length}
rowHeight={measurerCache.rowHeight}
rowCount={finalFeedData.length}
rowHeight={rowHeight}
scrollTop={scrollTop}
overscanRowCount={2}
onRowsRendered={onRowsRendered}
Expand All @@ -247,3 +317,5 @@ export default function FeedList({
</WindowScroller>
);
}

const DEFAULT_ROW_HEIGHT = 100;
Loading
Loading