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
Binary file added apps/web/public/legacy/movie-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/web/public/legacy/movies-banner.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/web/public/legacy/movies-r2-banner.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/web/public/legacy/movies-r3-banner.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 9 additions & 4 deletions apps/web/src/features/sessions/api/getSessions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { graphql } from "@/lib/graphql/generated";

import { IPFS_GATEWAY } from "@/config/ipfs";

import { LISTED_PARENT_MARKETS, legacySessions } from "../utils/curatedListing";
import { boundarySeconds, PER_PAGE, sessionWhere, type SessionFilters } from "../utils/listing";
import { toSessionSummary } from "../utils/toSessionSummary";

Expand All @@ -33,7 +34,8 @@ const SESSIONS = graphql(`
`);

export function useSessions({ page, filters }: { page: number; filters: SessionFilters }) {
const where = sessionWhere(filters, boundarySeconds(Date.now()));
const boundary = boundarySeconds(Date.now());
const where = { ...sessionWhere(filters, boundary), parentMarket: { _in: LISTED_PARENT_MARKETS } };

return useQuery({
queryKey: ["sessions", page, where],
Expand All @@ -45,11 +47,14 @@ export function useSessions({ page, filters }: { page: number; filters: SessionF
});

const rows = result.Session;
const hasNextPage = rows.length > PER_PAGE;
const legacy = legacySessions(filters, boundary);
const sessions = rows.slice(0, PER_PAGE).map((row) => toSessionSummary(row, IPFS_GATEWAY));

return {
sessions: rows.slice(0, PER_PAGE).map((row) => toSessionSummary(row, IPFS_GATEWAY)),
hasNextPage: rows.length > PER_PAGE,
total: Number(result.SessionCounter[0]?.count ?? 0),
sessions: hasNextPage ? sessions : [...sessions, ...legacy],
hasNextPage,
total: LISTED_PARENT_MARKETS.length + legacy.length,
};
},
placeholderData: keepPreviousData,
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/features/sessions/components/SessionCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function SessionCard({ session }: { session: SessionSummary }) {
const { closesAt } = session;

return (
<Link className="fs-session-card" href={paths.market.getHref(session.parentMarket)}>
<Link className="fs-session-card" href={session.href ?? paths.market.getHref(session.parentMarket)}>
<div className="fs-session-card__inner border-gradient-purple-blue">
<div className="fs-session-card__banner" style={bannerStyle(session.heroUri)} />

Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/features/sessions/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,4 +185,6 @@ export interface SessionSummary extends SessionCore {
/** Plural, e.g. "Movies". */
branchNoun: string;
keyword: string;
/** Overrides the market page link, for sessions this indexer does not hold. */
href?: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { describe, expect, it } from "vitest";

import { legacySessions } from "../curatedListing";
import { NO_FILTERS } from "../listing";

/** 2026-08-01, between the Session 2 and Session 3 trading ends. */
const AUG_2026 = Date.UTC(2026, 7, 1) / 1000;

const names = (sessions: { name: string }[]) => sessions.map(({ name }) => name);

describe("legacySessions", () => {
it("lists all three movies sessions, each linking out to the legacy UI", () => {
const sessions = legacySessions(NO_FILTERS, AUG_2026);

expect(names(sessions)).toEqual([
"Movies Experiment - Session 3",
"Movies Experiment - Session 2",
"Movies Experiment",
]);
expect(sessions.map(({ href }) => href)).toEqual([
"https://movies-r3.foresight.kleros.io",
"https://movies-r2.foresight.kleros.io",
"https://movies.foresight.kleros.io",
]);
});

it("splits open from closed on the trading end", () => {
expect(names(legacySessions({ query: "", state: "open" }, AUG_2026))).toEqual(["Movies Experiment - Session 3"]);
expect(names(legacySessions({ query: "", state: "closed" }, AUG_2026))).toEqual([
"Movies Experiment - Session 2",
"Movies Experiment",
]);
});

it("matches a search term case-insensitively", () => {
expect(legacySessions({ query: "SESSION 2", state: "all" }, AUG_2026)).toHaveLength(1);
expect(legacySessions({ query: "devcon", state: "all" }, AUG_2026)).toHaveLength(0);
});
});
70 changes: 70 additions & 0 deletions apps/web/src/features/sessions/utils/curatedListing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* TEMPORARY curation of the homepage listing until test sessions stop landing on the
* production indexer. Deleting this file and its import in getSessions restores the full listing.
*/
import type { Address } from "viem";

import type { SessionSummary } from "../types";
import type { SessionFilters } from "./listing";

/** Indexed sessions the homepage may list, lowercase as the indexer stores them. */
export const LISTED_PARENT_MARKETS: Address[] = [
"0xf094a219f85fc9880362fe096bd489bccb9b5359", // DevCon Side Event - Satisfaction
"0xd1d81ec6c50cac45c9930f31f837b62eedfaefc4", // DevCon Side Event - Attendance
];

const MOVIES_QUESTION = "If watched, what percentile score would Clément give to the movie?";

interface LegacySession extends SessionSummary {
/** Unix seconds; the end of trading on the legacy deployment. */
endTime: number;
}

/** Movies sessions deployed before this indexer; their cards open the legacy futarchy-ui. */
const LEGACY_SESSIONS: LegacySession[] = [
{
id: "legacy-movies-s3",
parentMarket: "0xacc15cfa0f4ae4932b12ab14595941285098436a",
name: "Movies Experiment - Session 3",
branchCount: 20,
heroUri: "/legacy/movies-r3-banner.webp",
href: "https://movies-r3.foresight.kleros.io",
endTime: 1790812740,
},
{
id: "legacy-movies-s2",
parentMarket: "0x6b182ffe23a9df5f5bfb2e9b6b4ce5716e84ab1f",
name: "Movies Experiment - Session 2",
branchCount: 20,
heroUri: "/legacy/movies-r2-banner.webp",
href: "https://movies-r2.foresight.kleros.io",
endTime: 1783209600,
},
{
id: "legacy-movies-s1",
parentMarket: "0x6f7ae2815e7e13c14a6560f4b382ae78e7b1493e",
name: "Movies Experiment",
branchCount: 16,
heroUri: "/legacy/movies-banner.webp",
href: "https://movies.foresight.kleros.io",
endTime: 1775239200,
},
].map((session) => ({
...session,
parentMarket: session.parentMarket as Address,
deployer: "0x0000000000000000000000000000000000000000",
description: MOVIES_QUESTION,
iconUri: "/legacy/movie-icon.png",
closesAt: null,
branchNoun: "Movies",
keyword: `${session.name} ${MOVIES_QUESTION}`.toLowerCase(),
}));

/** @param boundary seconds since the epoch, from `boundarySeconds`. */
export function legacySessions({ query, state }: SessionFilters, boundary: number): SessionSummary[] {
const term = query.trim().toLowerCase();

return LEGACY_SESSIONS.filter(
({ keyword, endTime }) => keyword.includes(term) && (state === "all" || (state === "open") === endTime > boundary),
).map(({ endTime: _endTime, ...session }) => session);
}
Loading