Skip to content
Open
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
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,10 +5,12 @@ 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 { useHideCP } from "@/contexts/cp_context";
import { useContentTranslatedBannerContext } from "@/contexts/translations_banner_context";
import {
GroupOfQuestionsGraphType,
Expand All @@ -19,6 +21,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 @@ -39,6 +42,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 @@ -129,6 +133,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 @@ -152,6 +157,11 @@ export const ConsumerShell: FC<{
!isContinuousSingleQuestion &&
!isMultipleChoice;

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

const showSideBySide =
isMultipleChoice ||
isNonFanGroup ||
Expand All @@ -169,7 +179,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 @@ -216,19 +233,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
@@ -1,8 +1,10 @@
"use client";

import RevealCPButton from "@/app/(main)/questions/[id]/components/reveal_cp_button";
import { 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 { useHideCP } from "@/contexts/cp_context";
import { QuestionStatus } from "@/types/post";
import { QuestionWithNumericForecasts } from "@/types/question";
import { getQuestionForecastAvailability } from "@/utils/questions/forecastAvailability";
Expand All @@ -17,8 +19,8 @@ const ContinuousQuestionPrediction: React.FC<Props> = ({
chartHeight,
}) => {
const forecastAvailability = getQuestionForecastAvailability(question);
const { hideCP } = useHideCP();

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

Expand All @@ -27,6 +29,14 @@ const ContinuousQuestionPrediction: React.FC<Props> = ({
isClosed: question.status === QuestionStatus.CLOSED,
});

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