From 4ca8b453a80ed5923067acec24963359485b7ab2 Mon Sep 17 00:00:00 2001 From: pitoi Date: Fri, 3 Jul 2026 03:05:01 +0000 Subject: [PATCH] Generated with Hive: Send legal_document content_type for PDF sources on legal skin --- src/components/modals/add-source-form.tsx | 10 +- src/lib/__tests__/add-content-modal.test.tsx | 119 ++++++++++++++++++- 2 files changed, 124 insertions(+), 5 deletions(-) diff --git a/src/components/modals/add-source-form.tsx b/src/components/modals/add-source-form.tsx index 1e8dd3b..f5b9033 100644 --- a/src/components/modals/add-source-form.tsx +++ b/src/components/modals/add-source-form.tsx @@ -41,6 +41,7 @@ export function AddSourceForm() { const { close, open: openModal } = useModalStore() const { budget, setBudget, pubKey, routeHint, isAdmin } = useUserStore() const refreshBalance = useUserStore((s) => s.refreshBalance) + const activeSkin = useAppStore((s) => s.activeSkin) const [sourceUrl, setSourceUrl] = useState("") const [detectedType, setDetectedType] = useState(null) const [detecting, setDetecting] = useState(false) @@ -195,15 +196,20 @@ export function AddSourceForm() { if (!contentType) { throw new Error(`Unsupported source type: ${sourceType}`) } + // Legal skin: submit PDFs as LegalDocument instead of generic Document + const effectiveContentType = + activeSkin === "legal" && contentType === "document" + ? "legal_document" + : contentType const body: Record = { - content_type: contentType, + content_type: effectiveContentType, source_link: source, } if (fullPubkey) body.pubkey = fullPubkey await api.post("/v2/content", body, headers) }, - [pubKey, routeHint, topics, category, weight, cacheStatus, cachedRefId] + [pubKey, routeHint, topics, category, weight, cacheStatus, cachedRefId, activeSkin] ) const handleSubmit = useCallback(async () => { diff --git a/src/lib/__tests__/add-content-modal.test.tsx b/src/lib/__tests__/add-content-modal.test.tsx index 9d68ef0..7e8ff5a 100644 --- a/src/lib/__tests__/add-content-modal.test.tsx +++ b/src/lib/__tests__/add-content-modal.test.tsx @@ -38,15 +38,26 @@ vi.mock("@/stores/user-store", () => ({ const mockSetMyContentOpen = vi.fn() const mockBumpMyContentRefresh = vi.fn() +let mockActiveSkinInContent = "default" + vi.mock("@/stores/app-store", () => { const getState = () => ({ setMyContentOpen: mockSetMyContentOpen, bumpMyContentRefresh: mockBumpMyContentRefresh, + activeSkin: mockActiveSkinInContent, }) return { - useAppStore: { - getState, - }, + useAppStore: Object.assign( + (sel?: (s: unknown) => unknown) => { + const state = { + setMyContentOpen: mockSetMyContentOpen, + bumpMyContentRefresh: mockBumpMyContentRefresh, + activeSkin: mockActiveSkinInContent, + } + return sel ? sel(state) : state + }, + { getState } + ), } }) @@ -458,3 +469,105 @@ describe("AddContentModal — admin category/weight fields", () => { expect(screen.queryByPlaceholderText(/0\.0 – 1\.0/i)).not.toBeInTheDocument() }) }) + +// --------------------------------------------------------------------------- +// AddSourceForm — content_type skin-awareness +// --------------------------------------------------------------------------- + +describe("AddSourceForm — content_type overridden by active skin", () => { + beforeEach(() => { + vi.clearAllMocks() + mockActiveSkinInContent = "default" + mockGetL402.mockResolvedValue(null) + mockGetPrice.mockResolvedValue(0) + mockApiPost.mockResolvedValue({}) + mockRefreshBalance.mockResolvedValue(undefined) + mockIsSubscriptionSource.mockReturnValue(false) + mockCheckNodeExists.mockResolvedValue({ exists: false, ref_id: null, status: null }) + }) + + it("sends content_type 'legal_document' when legal skin is active and source is a PDF URL", async () => { + vi.useFakeTimers({ shouldAdvanceTime: true }) + const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime.bind(vi) }) + + mockActiveSkinInContent = "legal" + mockDetectSourceType.mockResolvedValue("document") + + render() + const input = screen.getByPlaceholderText(/Paste URL/) + await user.type(input, "https://example.com/contract.pdf") + + await waitFor(() => { + expect(screen.getByRole("button", { name: /add source/i })).not.toBeDisabled() + }) + + await user.click(screen.getByRole("button", { name: /add source/i })) + + await waitFor(() => { + expect(mockApiPost).toHaveBeenCalledWith( + "/v2/content", + expect.objectContaining({ content_type: "legal_document" }), + expect.anything() + ) + }) + + vi.useRealTimers() + }) + + it("sends content_type 'document' when default skin is active and source is a PDF URL", async () => { + vi.useFakeTimers({ shouldAdvanceTime: true }) + const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime.bind(vi) }) + + mockActiveSkinInContent = "default" + mockDetectSourceType.mockResolvedValue("document") + + render() + const input = screen.getByPlaceholderText(/Paste URL/) + await user.type(input, "https://example.com/contract.pdf") + + await waitFor(() => { + expect(screen.getByRole("button", { name: /add source/i })).not.toBeDisabled() + }) + + await user.click(screen.getByRole("button", { name: /add source/i })) + + await waitFor(() => { + expect(mockApiPost).toHaveBeenCalledWith( + "/v2/content", + expect.objectContaining({ content_type: "document" }), + expect.anything() + ) + }) + + vi.useRealTimers() + }) + + it("sends content_type 'audio_video' for YouTube URL regardless of legal skin", async () => { + vi.useFakeTimers({ shouldAdvanceTime: true }) + const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime.bind(vi) }) + + mockActiveSkinInContent = "legal" + mockDetectSourceType.mockResolvedValue("youtube_video") + mockCheckNodeExists.mockResolvedValue({ exists: false, ref_id: null, status: null }) + + render() + const input = screen.getByPlaceholderText(/Paste URL/) + await user.type(input, "https://youtube.com/watch?v=abc123") + + await waitFor(() => { + expect(screen.getByRole("button", { name: /add source/i })).not.toBeDisabled() + }) + + await user.click(screen.getByRole("button", { name: /add source/i })) + + await waitFor(() => { + expect(mockApiPost).toHaveBeenCalledWith( + "/v2/content", + expect.objectContaining({ content_type: "audio_video" }), + expect.anything() + ) + }) + + vi.useRealTimers() + }) +})