Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@ import { useCommentsFeed } from "@/app/(main)/components/comments_feed_provider"
import { openKeyFactorsSectionAndScrollTo } from "@/app/(main)/questions/[id]/components/key_factors/utils";
import { PostStatus, PostWithForecasts } from "@/types/post";
import { sendAnalyticsEvent } from "@/utils/analytics";
import { getQuestionForecastAvailability } from "@/utils/questions/forecastAvailability";
import { isQuestionPost } from "@/utils/questions/helpers";

import {
MAX_TOP_KEY_FACTORS,
useTopKeyFactorsCarouselItems,
} from "./hooks/use_top_key_factors_carousel_items";
import KeyFactorDetailOverlay from "./key_factor_detail_overlay";
import KeyFactorsCarousel from "./key_factors_carousel";
import KeyFactorsConsumerCarousel from "./key_factors_consumer_carousel";
import { useShouldHideKeyFactors } from "./use_should_hide_key_factors";
import { useQuestionLayout } from "../question_layout/question_layout_context";
Expand Down Expand Up @@ -41,14 +44,21 @@ const KeyFactorsQuestionConsumerSection: FC<Props> = ({ post }) => {

if (post.status === PostStatus.RESOLVED) return null;

const postForecastAvailability = isQuestionPost(post)
? getQuestionForecastAvailability(post.question)
: null;
const forecastIsEmpty =
!!postForecastAvailability?.isEmpty &&
!postForecastAvailability?.cpRevealsOn;

if (topItems.length === 0 && !forecastIsEmpty) return null;

const openKeyFactorsElement = (selector: string) => {
requestKeyFactorsExpand?.();
openKeyFactorsSectionAndScrollTo({ selector, mobileOnly: false });
sendAnalyticsEvent("KeyFactorClick", { event_label: "fromTopList" });
};

if (topItems.length === 0) return null;

return (
<div
className="-ml-4 flex w-[calc(100%+32px)] flex-col pb-4 sm:ml-0 sm:mt-8 sm:w-full"
Expand All @@ -58,18 +68,33 @@ const KeyFactorsQuestionConsumerSection: FC<Props> = ({ post }) => {
<div className="text-sm text-blue-800 dark:text-blue-800-dark">
{t("topKeyFactors")}
</div>
<button
onClick={() => {
openKeyFactorsElement("[id='key-factors']");
sendAnalyticsEvent("KeyFactorViewAllClick");
}}
className="text-center text-sm font-normal leading-5 text-blue-600 hover:text-blue-700 dark:text-blue-600-dark dark:hover:text-blue-700-dark"
>
{t("viewAll", { count: totalCount })}
</button>
{!forecastIsEmpty && (
<button
onClick={() => {
openKeyFactorsElement("[id='key-factors']");
sendAnalyticsEvent("KeyFactorViewAllClick");
}}
className="text-center text-sm font-normal leading-5 text-blue-600 hover:text-blue-700 dark:text-blue-600-dark dark:hover:text-blue-700-dark"
>
{t("viewAll", { count: totalCount })}
</button>
)}
</div>

<KeyFactorsConsumerCarousel post={post} items={topItems} />
{forecastIsEmpty ? (
<KeyFactorsCarousel
listClassName="pb-0 [&>:first-child]:pl-4 [&>:last-child]:pr-4 sm:[&>:first-child]:pl-0 sm:[&>:last-child]:pr-0"
items={Array.from({ length: 5 })}
renderItem={(_, i) => (
<div
key={i}
className="h-[196px] w-[160px] shrink-0 rounded-xl bg-blue-200 dark:bg-blue-200-dark sm:w-[200px]"
/>
)}
/>
) : (
<KeyFactorsConsumerCarousel post={post} items={topItems} />
)}

{keyFactorOverlay?.kind === "keyFactor" && (
<KeyFactorDetailOverlay
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,10 +319,11 @@ const MultiChoicesChartView: FC<Props> = ({
)}

{isTooltipActive &&
!hideCP &&
!forecastAvailability?.cpRevealsOn &&
!activeTimelineMarkerId &&
(tooltipChoices.length > 0 ||
!!tooltipUserChoices?.length ||
!!forecastAvailability?.cpRevealsOn ||
!!forecastAvailability?.isEmpty) && (
<FloatingPortal>
<div
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import { FC, Fragment, ReactNode, useEffect } from "react";

import useCoherenceLinksContext from "@/app/(main)/components/coherence_links_provider";
import { PostStatusBox } from "@/app/(main)/questions/[id]/components/post_status_box";
import UpcomingCP from "@/components/consumer_post_card/upcoming_cp";
import DetailedGroupCard from "@/components/detailed_question_card/detailed_group_card";
import DetailedQuestionCard from "@/components/detailed_question_card/detailed_question_card";
import ForecastMaker from "@/components/forecast_maker";
import CommunityDisclaimer from "@/components/post_card/community_disclaimer";
import { ContinuousChartCursorProvider } from "@/contexts/continuous_chart_cursor_context";
import { useHideCP } from "@/contexts/cp_context";
import { useContentTranslatedBannerContext } from "@/contexts/translations_banner_context";
import {
GroupOfQuestionsGraphType,
Expand All @@ -20,6 +22,7 @@ import {
import { TournamentType } from "@/types/projects";
import { QuestionType } from "@/types/question";
import cn from "@/utils/core/cn";
import { getQuestionForecastAvailability } from "@/utils/questions/forecastAvailability";
import {
checkGroupOfQuestionsPostType,
isContinuousQuestion,
Expand All @@ -40,6 +43,7 @@ import ActionRow from "../question_view/action_row";
import ConsumerQuestionPrediction from "../question_view/consumer_question_view/prediction";
import QuestionTimeline from "../question_view/consumer_question_view/timeline";
import QuestionHeaderCPStatus from "../question_view/forecaster_question_view/question_header/question_header_cp_status";
import RevealCPButton from "../reveal_cp_button";

const baseSectionClassName =
"relative z-10 flex w-[59rem] max-w-full flex-col gap-6 overflow-x-clip rounded border border-blue-400 p-4 text-gray-900 dark:border-blue-200-dark dark:text-gray-900-dark lg:p-8";
Expand Down Expand Up @@ -130,6 +134,7 @@ export const ConsumerShell: FC<{
}> = ({ postData, preselectedGroupQuestionId, mobileSidebar }) => {
const t = useTranslations();
const { aggregateCoherenceLinks } = useCoherenceLinksContext();
const { hideCP } = useHideCP();

const isFanGraph =
postData.group_of_questions?.graph_type ===
Expand All @@ -153,6 +158,11 @@ export const ConsumerShell: FC<{
!isContinuousSingleQuestion &&
!isMultipleChoice;

const binaryForecastAvailability =
isBinarySingleQuestion && isQuestionPost(postData)
? getQuestionForecastAvailability(postData.question)
: null;

const showSideBySide =
isMultipleChoice ||
isNonFanGroup ||
Expand All @@ -170,7 +180,14 @@ export const ConsumerShell: FC<{
aggregateCoherenceLinks?.data.filter(isDisplayableQuestionLink) ?? [];
const hasKeyFactors = (postData.key_factors?.length ?? 0) > 0;
const hasQuestionLinks = questionLinkAggregates.length > 0;
const shouldShowKeyFactorsSection = hasKeyFactors || hasQuestionLinks;
const questionForecastAvailability = isQuestionPost(postData)
? getQuestionForecastAvailability(postData.question)
: null;
const isForecastEmpty =
!!questionForecastAvailability?.isEmpty &&
!questionForecastAvailability?.cpRevealsOn;
const shouldShowKeyFactorsSection =
hasKeyFactors || hasQuestionLinks || isForecastEmpty;

return (
<div className="flex flex-col gap-1.5 md:gap-4">
Expand Down Expand Up @@ -217,19 +234,39 @@ export const ConsumerShell: FC<{
>
{isBinarySingleQuestion && isQuestionPost(postData) ? (
<div className="order-1 flex w-64 flex-col items-center justify-center gap-[18px] self-center sm:self-stretch">
<QuestionHeaderCPStatus
question={postData.question}
size="lg"
/>
{hideCP ? (
<RevealCPButton />
) : binaryForecastAvailability?.cpRevealsOn ? (
<UpcomingCP
cpRevealsOn={binaryForecastAvailability.cpRevealsOn}
/>
) : (
<QuestionHeaderCPStatus
question={postData.question}
size="lg"
/>
)}
</div>
) : (
<div
className={cn(
showSideBySide && !isDateGroup ? "order-1" : undefined,
isContinuousSingleQuestion && "md:hidden"
isContinuousSingleQuestion && "md:hidden",
showSideBySide &&
!isDateGroup &&
!isContinuousSingleQuestion &&
"sm:max-w-[200px]",
hideCP &&
!isContinuousSingleQuestion &&
(isDateGroup || isFanGraph) &&
"flex w-full justify-center"
)}
>
<ConsumerQuestionPrediction postData={postData} />
{hideCP && !isContinuousSingleQuestion ? (
<RevealCPButton />
) : (
<ConsumerQuestionPrediction postData={postData} />
)}
</div>
)}
{!isFanGraph && !isDateGroup && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ import { FC } from "react";

import QuestionHeaderCPStatus from "@/app/(main)/questions/[id]/components/question_view/forecaster_question_view/question_header/question_header_cp_status";
import QuestionTitle from "@/app/(main)/questions/[id]/components/question_view/shared/question_title";
import RevealCPButton from "@/app/(main)/questions/[id]/components/reveal_cp_button";
import ConditionalTile from "@/components/conditional_tile";
import { useHideCP } from "@/contexts/cp_context";
import { PostWithForecasts } from "@/types/post";
import { QuestionWithForecasts } from "@/types/question";
import { QuestionType, QuestionWithForecasts } from "@/types/question";
import cn from "@/utils/core/cn";
import {
isConditionalPost,
isContinuousQuestion,
isGroupOfQuestionsPost,
isQuestionPost,
} from "@/utils/questions/helpers";

Expand All @@ -23,6 +26,8 @@ type Props = {
};

const TitleRow: FC<Props> = ({ post, variant, className }) => {
const { hideCP } = useHideCP();

if (isConditionalPost(post)) {
return (
<div className={className}>
Expand All @@ -32,33 +37,83 @@ const TitleRow: FC<Props> = ({ post, variant, className }) => {
}

if (variant === "forecaster" && isQuestionPost(post)) {
const isMultipleChoice = post.question.type === QuestionType.MultipleChoice;
const isContinuous = isContinuousQuestion(post.question);

return (
<div
className={cn(
"flex w-full items-stretch justify-between gap-2 xs:gap-4 sm:gap-8",
className
)}
>
<div className="flex flex-1 flex-col">
<div className="lg:order-0 order-1 flex items-center">
<QuestionTitle className="text-xl font-bold leading-tight tracking-[-0.4px] text-blue-800 dark:text-blue-800-dark sm:text-3xl sm:tracking-tight lg:text-4xl">
<div className="flex min-w-0 flex-1 flex-col">
<div
className={cn(
"lg:order-0 order-1 flex gap-2",
hideCP ? "flex-col" : "items-center"
)}
>
<QuestionTitle className="min-w-0 break-words text-xl font-bold leading-tight tracking-[-0.4px] text-blue-800 dark:text-blue-800-dark sm:text-3xl sm:tracking-tight lg:text-4xl">
{post.title}
</QuestionTitle>
<div className="md:hidden">
<div className="shrink-0 self-center md:hidden">
{isMultipleChoice ? (
hideCP && <RevealCPButton className="whitespace-nowrap" />
) : (
<QuestionHeaderCPStatus
question={post.question as QuestionWithForecasts}
size="md"
hideLabel={isContinuous}
/>
)}
</div>
</div>
</div>
{!isContinuous && (
<div className="hidden shrink-0 md:block">
{isMultipleChoice && hideCP ? (
<RevealCPButton className="whitespace-nowrap" />
) : (
<QuestionHeaderCPStatus
question={post.question as QuestionWithForecasts}
size="md"
hideLabel={isContinuousQuestion(post.question)}
size="lg"
/>
</div>
)}
</div>
)}
</div>
);
}

if (variant === "forecaster" && isGroupOfQuestionsPost(post)) {
return (
<div
className={cn(
"flex w-full items-stretch justify-between gap-2 xs:gap-4 sm:gap-8",
className
)}
>
<div className="flex min-w-0 flex-1 flex-col">
<div
className={cn(
"lg:order-0 order-1 flex gap-2",
hideCP ? "flex-col" : "items-center"
)}
>
<QuestionTitle className="min-w-0 break-words text-xl font-bold leading-tight tracking-[-0.4px] text-blue-800 dark:text-blue-800-dark sm:text-3xl sm:tracking-tight lg:text-4xl">
{post.title}
</QuestionTitle>
{hideCP && (
<div className="shrink-0 self-center md:hidden">
<RevealCPButton className="whitespace-nowrap" />
</div>
)}
</div>
</div>
{!isContinuousQuestion(post.question) && (
<div className="hidden md:block">
<QuestionHeaderCPStatus
question={post.question as QuestionWithForecasts}
size="lg"
/>
{hideCP && (
<div className="hidden shrink-0 md:block">
<RevealCPButton className="whitespace-nowrap" />
</div>
)}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

import { useMemo } from "react";

import RevealCPButton from "@/app/(main)/questions/[id]/components/reveal_cp_button";
import {
ContinuousAreaGraphInput,
getContinuousAreaChartData,
} from "@/components/charts/continuous_area_chart";
import MinifiedContinuousAreaChart from "@/components/charts/minified_continuous_area_chart";
import ConsumerContinuousTile from "@/components/consumer_post_card/consumer_question_tile/consumer_continuous_tile";
import { useContinuousChartCursor } from "@/contexts/continuous_chart_cursor_context";
import { useHideCP } from "@/contexts/cp_context";
import { ContinuousAreaType } from "@/types/charts";
import { QuestionStatus } from "@/types/post";
import { QuestionWithNumericForecasts } from "@/types/question";
Expand All @@ -27,8 +29,8 @@ const ContinuousQuestionPrediction: React.FC<Props> = ({
const forecastAvailability = getQuestionForecastAvailability(question);
const cursorCtx = useContinuousChartCursor();
const cursorForecast = cursorCtx?.activeForecast ?? null;
const { hideCP } = useHideCP();

// Hide chart if no forecasts or CP not yet revealed
const shouldHideChart =
forecastAvailability.isEmpty || !!forecastAvailability.cpRevealsOn;

Expand All @@ -53,6 +55,14 @@ const ContinuousQuestionPrediction: React.FC<Props> = ({
];
}, [cursorForecastValues, question.status]);

if (hideCP) {
return (
<div className="mx-auto mb-7 flex max-w-[340px] flex-col items-center justify-center gap-2.5">
<RevealCPButton />
</div>
);
}

return (
<div className="mx-auto mb-7 flex max-w-[340px] flex-col items-center gap-2.5">
<ConsumerContinuousTile
Expand Down
Loading
Loading