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
125 changes: 110 additions & 15 deletions __tests__/components/waves/CreateDrop.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,11 @@ jest.mock("@/contexts/wave/MyStreamContext", () => ({
jest.mock("@/components/waves/CreateDropStormParts", () => () => (
<div data-testid="storm" />
));
const PREFILL_URL =
"https://opensea.io/item/ethereum/0x1234567890abcdef1234567890abcdef12345678/123";

jest.mock("@/components/waves/CreateDropContent", () => (props: any) => (
<button
onClick={() =>
props.submitDrop({
drop: { wave_id: "1" },
dropId: null,
} as DropMutationBody)
}
>
submit
</button>
));
jest.mock("@/components/waves/CreateCurationDropContent", () => ({
__esModule: true,
default: (props: any) => (
<div>
<button
onClick={() =>
props.submitDrop({
Expand All @@ -61,8 +51,35 @@ jest.mock("@/components/waves/CreateCurationDropContent", () => ({
} as DropMutationBody)
}
>
submit curation
submit
</button>
<button
onClick={() =>
props.onSwitchToDropModeWithUrl(
"https://opensea.io/item/ethereum/0x1234567890abcdef1234567890abcdef12345678/123"
)
}
>
switch to drop
</button>
</div>
));
jest.mock("@/components/waves/CreateCurationDropContent", () => ({
__esModule: true,
default: (props: any) => (
<div>
<button
onClick={() =>
props.submitDrop({
drop: { wave_id: "1" },
dropId: null,
} as DropMutationBody)
}
>
submit curation
</button>
<div data-testid="initial-url">{props.initialUrl ?? ""}</div>
</div>
),
}));

Expand Down Expand Up @@ -172,4 +189,82 @@ describe("CreateDrop", () => {
type: "success",
});
});

it("switches to curation mode with a prefilled url seed", async () => {
useWaveMock.mockReturnValue({ isCurationWave: true } as any);

render(
<AuthContext.Provider value={{ setToast: jest.fn() } as any}>
<ReactQueryWrapperContext.Provider
value={{ waitAndInvalidateDrops: jest.fn() } as any}
>
<CreateDrop
activeDrop={null}
onCancelReplyQuote={() => {}}
onDropAddedToQueue={jest.fn()}
wave={wave}
dropId={null}
fixedDropMode={"BOTH" as any}
privileges={{} as any}
/>
</ReactQueryWrapperContext.Provider>
</AuthContext.Provider>
);

await userEvent.click(screen.getByText("switch to drop"));

await waitFor(() =>
expect(screen.getByTestId("initial-url")).toHaveTextContent(PREFILL_URL)
);
});

it("resets to default mode when wave scope changes", async () => {
useWaveMock.mockReturnValue({ isCurationWave: true } as any);
const nextWave = { ...wave, id: "2" };

const { rerender } = render(
<AuthContext.Provider value={{ setToast: jest.fn() } as any}>
<ReactQueryWrapperContext.Provider
value={{ waitAndInvalidateDrops: jest.fn() } as any}
>
<CreateDrop
activeDrop={null}
onCancelReplyQuote={() => {}}
onDropAddedToQueue={jest.fn()}
wave={wave}
dropId={null}
fixedDropMode={"BOTH" as any}
privileges={{} as any}
/>
</ReactQueryWrapperContext.Provider>
</AuthContext.Provider>
);

await userEvent.click(screen.getByText("switch to drop"));
await waitFor(() =>
expect(screen.getByText("submit curation")).toBeInTheDocument()
);

rerender(
<AuthContext.Provider value={{ setToast: jest.fn() } as any}>
<ReactQueryWrapperContext.Provider
value={{ waitAndInvalidateDrops: jest.fn() } as any}
>
<CreateDrop
activeDrop={null}
onCancelReplyQuote={() => {}}
onDropAddedToQueue={jest.fn()}
wave={nextWave}
dropId={null}
fixedDropMode={"BOTH" as any}
privileges={{} as any}
/>
</ReactQueryWrapperContext.Provider>
</AuthContext.Provider>
);

await waitFor(() =>
expect(screen.getByText("switch to drop")).toBeInTheDocument()
);
});
});
8 changes: 5 additions & 3 deletions components/waves/CreateCurationDropContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ interface CreateCurationDropContentProps {
readonly wave: ApiWave;
readonly dropId: string | null;
readonly isDropMode: boolean;
readonly initialUrl: string | null;
readonly submitDrop: (dropRequest: DropMutationBody) => void;
readonly curationComposerVariant?: CurationComposerVariant | undefined;
}
Expand Down Expand Up @@ -148,6 +149,7 @@ const CreateCurationDropContent: React.FC<CreateCurationDropContentProps> = ({
wave,
dropId,
isDropMode,
initialUrl,
submitDrop,
curationComposerVariant = "default",
}) => {
Expand All @@ -159,7 +161,7 @@ const CreateCurationDropContent: React.FC<CreateCurationDropContentProps> = ({
const { processIncomingDrop } = useMyStream();
const { signDrop } = useDropSignature();

const [urlValue, setUrlValue] = useState("");
const [urlValue, setUrlValue] = useState(() => initialUrl ?? "");
const [submitting, setSubmitting] = useState(false);
const [showLiveValidation, setShowLiveValidation] = useState(false);
const [isSupportedUrlsModalOpen, setIsSupportedUrlsModalOpen] =
Expand Down Expand Up @@ -480,7 +482,7 @@ const CreateCurationDropContent: React.FC<CreateCurationDropContentProps> = ({
</div>
</div>
) : (
<div className="tw-flex tw-w-full tw-items-end">
<div className="tw-flex tw-w-full tw-items-start">
<div className="tw-flex tw-w-full tw-items-center tw-gap-x-2 lg:tw-gap-x-3">
<div className="tw-w-full tw-flex-grow">
<CreateCurationDropUrlInput
Expand All @@ -496,7 +498,7 @@ const CreateCurationDropContent: React.FC<CreateCurationDropContentProps> = ({
/>
</div>
</div>
<div className="tw-ml-2 lg:tw-ml-3">
<div className="tw-ml-2 tw-self-start lg:tw-ml-3">
<div className="tw-flex tw-items-center tw-gap-x-3">
<CreateDropSubmit
submitting={submitting}
Expand Down
78 changes: 62 additions & 16 deletions components/waves/CreateDrop.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
"use client";

import {
useEffect,
useRef,
useState,
useCallback,
useContext,
useMemo,
} from "react";
import { useRef, useState, useCallback, useContext, useMemo } from "react";
import type { CreateDropConfig } from "@/entities/IDrop";
import CreateDropStormParts from "./CreateDropStormParts";
import { AnimatePresence, motion } from "framer-motion";
Expand Down Expand Up @@ -67,9 +60,17 @@ export default function CreateDrop({
useKeyPressEvent("Escape", () => onCancelReplyQuote());
const [isStormMode, setIsStormMode] = useState(false);
const [drop, setDrop] = useState<CreateDropConfig | null>(null);
const [dropModeOverride, setDropModeOverride] = useState<{
scopeKey: string;
value: boolean;
} | null>(null);
const [curationPrefillSeed, setCurationPrefillSeed] = useState<{
scopeKey: string;
url: string;
} | null>(null);
const { processDropRemoved, processIncomingDrop } = useMyStream();
const { isCurationWave } = useWave(wave);
const getIsDropMode = () => {
const getDefaultIsDropMode = () => {
if (fixedDropMode === DropMode.CHAT) {
return false;
}
Expand All @@ -82,34 +83,76 @@ export default function CreateDrop({
return false;
};

const [isDropMode, setIsDropMode] = useState(getIsDropMode());
const activeDropScope =
activeDrop === null
? "none"
: `${activeDrop.action}:${activeDrop.drop.id}:${activeDrop.partId}`;
const modeScopeKey = `${wave.id}:${fixedDropMode}:${wave.chat.authenticated_user_eligible}:${wave.participation.authenticated_user_eligible}:${activeDropScope}`;
const modeScopeEpochRef = useRef(0);
const lastModeScopeKeyRef = useRef(modeScopeKey);
if (lastModeScopeKeyRef.current !== modeScopeKey) {
lastModeScopeKeyRef.current = modeScopeKey;
modeScopeEpochRef.current += 1;
}
const modeScopeToken = `${modeScopeKey}:${modeScopeEpochRef.current}`;
const defaultIsDropMode = getDefaultIsDropMode();
const isDropMode =
dropModeOverride?.scopeKey === modeScopeToken
? dropModeOverride.value
: defaultIsDropMode;
const initialCurationUrl =
curationPrefillSeed?.scopeKey === modeScopeToken
? curationPrefillSeed.url
: null;
const isCurationDropMode = isCurationWave && isDropMode;
useEffect(() => setIsDropMode(getIsDropMode()), [wave, activeDrop]);

const onDropModeChange = useCallback(
const canSwitchDropMode = useCallback(
(newIsDropMode: boolean) => {
if (fixedDropMode !== DropMode.BOTH) {
return;
return false;
}

if (newIsDropMode && !wave.participation.authenticated_user_eligible) {
setToast({
message: "You are not eligible to drop in this wave",
type: "error",
});
return;
return false;
}

if (!newIsDropMode && !wave.chat.authenticated_user_eligible) {
setToast({
message: "You are not eligible to chat in this wave",
type: "error",
});
return false;
}

return true;
},
[fixedDropMode, setToast, wave]
);

const onDropModeChange = useCallback(
(newIsDropMode: boolean) => {
if (!canSwitchDropMode(newIsDropMode)) {
return;
}
setCurationPrefillSeed(null);
setDropModeOverride({ scopeKey: modeScopeToken, value: newIsDropMode });
},
[canSwitchDropMode, modeScopeToken]
);

setIsDropMode(newIsDropMode);
const onSwitchToDropModeWithUrl = useCallback(
(url: string) => {
if (!canSwitchDropMode(true)) {
return;
}
setCurationPrefillSeed({ scopeKey: modeScopeToken, url });
setDropModeOverride({ scopeKey: modeScopeToken, value: true });
},
[wave]
[canSwitchDropMode, modeScopeToken]
);

const onRemovePart = useCallback((partIndex: number) => {
Expand Down Expand Up @@ -252,6 +295,7 @@ export default function CreateDrop({
setDrop,
setIsStormMode,
onDropModeChange,
onSwitchToDropModeWithUrl,
submitDrop,
privileges,
}),
Expand All @@ -265,6 +309,7 @@ export default function CreateDrop({
setDrop,
setIsStormMode,
onDropModeChange,
onSwitchToDropModeWithUrl,
submitDrop,
privileges,
]
Expand Down Expand Up @@ -298,6 +343,7 @@ export default function CreateDrop({
wave={wave}
dropId={dropId}
isDropMode={isDropMode}
initialUrl={initialCurationUrl}
submitDrop={submitDrop}
curationComposerVariant={curationComposerVariant}
/>
Expand Down
Loading