Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
203 changes: 203 additions & 0 deletions __tests__/components/home/explore-waves/ExploreWaveCard.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
import { render, screen } from "@testing-library/react";
import type { ApiWave } from "@/generated/models/ApiWave";
import { ExploreWaveCard } from "@/components/home/explore-waves/ExploreWaveCard";
import { getTimeAgoShort } from "@/helpers/Helpers";
import type { ImgHTMLAttributes, ReactNode } from "react";

jest.mock("next/link", () => ({
__esModule: true,
default: ({
href,
children,
...rest
}: {
href: string;
children: ReactNode;
}) => (
<a href={href} {...rest}>
{children}
</a>
),
}));

jest.mock("next/image", () => ({
__esModule: true,
default: (props: ImgHTMLAttributes<HTMLImageElement>) => (
// eslint-disable-next-line @next/next/no-img-element
<img alt="" {...props} />
),
}));

const mockContentDisplay = jest.fn();

jest.mock("@/components/waves/drops/ContentDisplay", () => ({
__esModule: true,
default: (props: unknown) => {
mockContentDisplay(props);
return <div data-testid="content-display" />;
},
}));

jest.mock("@/helpers/Helpers", () => ({
getRandomColorWithSeed: jest.fn(() => "#123456"),
getTimeAgoShort: jest.fn(() => "2m"),
}));

describe("ExploreWaveCard", () => {
beforeEach(() => {
jest.clearAllMocks();
mockContentDisplay.mockClear();
});

it("uses ApiWave last_drop_time and description_drop preview content", () => {
render(
<ExploreWaveCard
wave={createWave({
last_drop_time: 2_000,
metrics: {
drops_count: 7,
latest_drop_timestamp: 1_000,
},
description_drop: {
parts: [
{
part_id: 1,
content: "Description preview",
media: [],
quoted_drop: null,
},
],
},
})}
/>
);

expect(getTimeAgoShort).toHaveBeenCalledWith(2_000);
expect(screen.getByText(/2m · 7/i)).toBeInTheDocument();
expect(screen.getByTestId("content-display")).toBeInTheDocument();

const lastContentDisplayProps =
mockContentDisplay.mock.calls[
mockContentDisplay.mock.calls.length - 1
][0];

expect(lastContentDisplayProps).toMatchObject({
linkify: false,
content: {
segments: [{ type: "text", content: "Description preview" }],
apiMedia: [],
},
});
});

it("does not render the preview container when description_drop is empty", () => {
render(
<ExploreWaveCard
wave={createWave({
description_drop: {
parts: [
{
part_id: 1,
content: " ",
media: [],
quoted_drop: null,
},
],
},
})}
/>
);

expect(screen.queryByTestId("content-display")).not.toBeInTheDocument();
});
});

function createWave(overrides: Partial<ApiWave> = {}): ApiWave {
const baseWave = {
id: "wave-1",
serial_no: 1,
author: {
handle: "alice",
banner1_color: null,
banner2_color: null,
},
name: "Wave One",
picture: null,
created_at: 1,
last_drop_time: 1_000,
description_drop: {
id: "drop-1",
serial_no: 1,
drop_type: "CHAT",
rank: null,
wave: {
id: "wave-1",
},
author: {
handle: "alice",
},
created_at: 1,
updated_at: null,
title: null,
parts: [
{
part_id: 1,
content: "Description preview",
media: [],
quoted_drop: null,
},
],
parts_count: 1,
referenced_nfts: [],
mentioned_users: [],
mentioned_waves: [],
metadata: [],
rating: 0,
realtime_rating: 0,
rating_prediction: 0,
top_raters: [],
raters_count: 0,
context_profile_context: null,
subscribed_actions: [],
is_signed: false,
reactions: [],
boosts: 0,
hide_link_preview: false,
},
voting: {},
visibility: {},
participation: {},
chat: {
scope: {
group: {
is_direct_message: false,
},
},
enabled: true,
},
wave: {
type: "CHAT",
},
contributors_overview: [],
subscribed_actions: [],
metrics: {
drops_count: 3,
latest_drop_timestamp: 1_000,
},
pauses: [],
pinned: false,
} as any;

return {
...baseWave,
...overrides,
metrics: {
...baseWave.metrics,
...(overrides.metrics as object | undefined),
},
description_drop: {
...baseWave.description_drop,
...(overrides.description_drop as object | undefined),
},
} as ApiWave;
}
46 changes: 23 additions & 23 deletions components/home/explore-waves/ExploreWaveCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
import type { ApiWave } from "@/generated/models/ApiWave";
import { getRandomColorWithSeed, getTimeAgoShort } from "@/helpers/Helpers";
import ContentDisplay from "@/components/waves/drops/ContentDisplay";
import type { ProcessedContent } from "@/components/waves/drops/media-utils";
import {
buildProcessedContent,
type ProcessedContent,
} from "@/components/waves/drops/media-utils";
import { getScaledImageUri, ImageScale } from "@/helpers/image.helpers";
import { getWaveRoute } from "@/helpers/navigation.helpers";
import Image from "next/image";
import Link from "next/link";
import { ChatBubbleBottomCenterIcon } from "@heroicons/react/24/outline";
import { extractDropPreview, useWaveLatestDrop } from "./useWaveLatestDrop";

interface ExploreWaveCardProps {
readonly wave: ApiWave;
Expand All @@ -33,15 +35,9 @@ export function ExploreWaveCard({ wave }: ExploreWaveCardProps) {
}
: undefined;

const lastMessageTime = wave.metrics.latest_drop_timestamp;
const lastMessageTime = wave.last_drop_time;
const hasDrops = wave.metrics.drops_count > 0;

// Fetch latest drop for message preview
const { data: latestDrop, isLoading: isLoadingDrop } = useWaveLatestDrop(
wave.id,
hasDrops
);
const latestMessagePreview = extractDropPreview(latestDrop ?? null);
const descriptionPreview = getWavePreviewContent(wave);

return (
<Link
Expand Down Expand Up @@ -88,18 +84,15 @@ export function ExploreWaveCard({ wave }: ExploreWaveCardProps) {
</div>
)}

{hasDrops && (
{descriptionPreview && (
<div className="tw-flex tw-items-center tw-gap-1.5 tw-rounded-lg tw-bg-iron-800/60 tw-p-1 tw-shadow-inner sm:tw-gap-3 sm:tw-p-2">
<div className="tw-flex tw-h-5 tw-w-5 tw-shrink-0 tw-items-center tw-justify-center tw-rounded-full tw-bg-iron-700/40 sm:tw-h-7 sm:tw-w-7">
<ChatBubbleBottomCenterIcon
className="tw-size-3 tw-shrink-0 tw-text-iron-400 sm:tw-size-3.5"
aria-hidden="true"
/>
</div>
<MessagePreviewContent
isLoading={isLoadingDrop}
previewContent={latestMessagePreview}
/>
<MessagePreviewContent previewContent={descriptionPreview} />
</div>
Comment thread
GelatoGenesis marked this conversation as resolved.
)}
</div>
Expand All @@ -108,18 +101,10 @@ export function ExploreWaveCard({ wave }: ExploreWaveCardProps) {
}

function MessagePreviewContent({
isLoading,
previewContent,
}: {
readonly isLoading: boolean;
readonly previewContent: ProcessedContent | null;
}) {
if (isLoading) {
return (
<div className="tw-h-6 tw-min-w-0 tw-flex-1 tw-animate-pulse tw-rounded tw-bg-iron-800/60" />
);
}

if (!previewContent) {
return null;
}
Expand All @@ -134,3 +119,18 @@ function MessagePreviewContent({
/>
);
}

function getWavePreviewContent(wave: ApiWave): ProcessedContent | null {
const descriptionParts = wave.description_drop.parts;
const combinedText = descriptionParts
.map((part) => part.content?.trim())
.filter((content): content is string => Boolean(content))
.join("\n\n");
const media = descriptionParts.flatMap((part) => part.media);

if (!combinedText && media.length === 0) {
return null;
}

return buildProcessedContent(combinedText || null, media);
}
46 changes: 0 additions & 46 deletions components/home/explore-waves/useWaveLatestDrop.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export const buildPreviewDrop = ({
name: wave.name,
picture: wave.picture,
description_drop_id: wave.description_drop.id,
last_drop_time: wave.last_drop_time,
Comment thread
GelatoGenesis marked this conversation as resolved.
authenticated_user_eligible_to_vote:
wave.voting.authenticated_user_eligible,
authenticated_user_eligible_to_participate:
Expand Down
1 change: 1 addition & 0 deletions components/waves/utils/getOptimisticDrop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export const getOptimisticDrop = (
pinned: wave.pinned,
picture: wave.picture ?? "",
description_drop_id: wave.description_drop.id,
last_drop_time: wave.last_drop_time,
authenticated_user_eligible_to_participate:
wave.participation.authenticated_user_eligible,
authenticated_user_eligible_to_vote:
Expand Down
10 changes: 10 additions & 0 deletions generated/models/ApiWave.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ export class ApiWave {
*/
'picture': string | null;
'created_at': number;
/**
* Unix timestamp in milliseconds of the most recent drop in this wave
*/
'last_drop_time': number;
'description_drop': ApiDrop;
'voting': ApiWaveVotingConfig;
'visibility': ApiWaveVisibilityConfig;
Expand Down Expand Up @@ -96,6 +100,12 @@ export class ApiWave {
"type": "number",
"format": "int64"
},
{
"name": "last_drop_time",
"baseName": "last_drop_time",
"type": "number",
"format": "int64"
},
{
"name": "description_drop",
"baseName": "description_drop",
Expand Down
Loading
Loading