Skip to content
Open
Show file tree
Hide file tree
Changes from 6 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
1 change: 1 addition & 0 deletions front_end/messages/cs.json
Original file line number Diff line number Diff line change
Expand Up @@ -2169,5 +2169,6 @@
"whyTrustMetaculusLessNoise": "Předpovědi Metaculus, poháněné davem, prořezávají šum tím, že zakládají každou předpověď na transparentních důkazech, odpovědném skóre a desetiletí doložené přesnosti. Metaculus vybavuje politiky, výzkumníky, novináře a korporátní organizace předpověďmi založenými na důkazech, které poskytují jasný, kvantifikovatelný vhled do nejkritičtějších nejistot světa. Prozkoumejte naši nabídku <servicesLink>Business řešení</servicesLink> a zjistěte, jak může Metaculus zlepšit rozhodování vaší organizace.",
"publishTimeLockedDescription": "Čas publikace nelze po vytvoření změnit.",
"nMore": "{count} více...",
"noHistogramData": "Žádná data histogramu nejsou k dispozici",
"thousandsOfOpenQuestions": "20 000+ otevřených otázek"
}
1 change: 1 addition & 0 deletions front_end/messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1185,6 +1185,7 @@
"deselectAll": "deselect all",
"forecastDataIsEmpty": "Forecast data is empty",
"noForecastsYet": "No forecasts yet",
"noHistogramData": "No histogram data available",
"RevealTemporarily": "Reveal Temporarily",
"CPIsHidden": "Community Prediction is hidden",
"createdByUserOnDate": "Created by {user} on {date}",
Expand Down
1 change: 1 addition & 0 deletions front_end/messages/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -2169,5 +2169,6 @@
"whyTrustMetaculusLessNoise": "Los pronósticos impulsados por la multitud de Metaculus cortan el ruido al basar cada predicción en evidencia transparente, puntuaciones responsables y una década de precisión demostrada. Metaculus equipa a los responsables de políticas, investigadores, periodistas y organizaciones corporativas con pronósticos basados en evidencia que ofrecen una visión clara y cuantificable de las incertidumbres más críticas del mundo. Explora nuestra suite de <servicesLink>Soluciones Empresariales</servicesLink> para aprender cómo Metaculus puede mejorar la toma de decisiones de tu organización.",
"publishTimeLockedDescription": "La hora de publicación no puede cambiarse después de la creación.",
"nMore": "{count} más...",
"noHistogramData": "No hay datos de histograma disponibles",
"thousandsOfOpenQuestions": "20,000+ preguntas abiertas"
}
1 change: 1 addition & 0 deletions front_end/messages/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -2167,5 +2167,6 @@
"whyTrustMetaculusLessNoise": "As previsões baseadas na multidão do Metaculus cortam o ruído ao basear cada previsão em evidências transparentes, pontuações responsáveis e uma década de precisão demonstrada. O Metaculus equipa formuladores de políticas, pesquisadores, jornalistas e organizações corporativas com previsões baseadas em evidências que oferecem insights claros e quantificáveis sobre as incertezas mais críticas do mundo. Explore nosso conjunto de <servicesLink>Soluções Empresariais</servicesLink> para saber mais sobre como o Metaculus pode melhorar a tomada de decisões da sua organização.",
"publishTimeLockedDescription": "O horário de publicação não pode ser alterado após a criação.",
"nMore": "{count} a mais...",
"noHistogramData": "Não há dados de histograma disponíveis",
"thousandsOfOpenQuestions": "20.000+ perguntas abertas"
}
1 change: 1 addition & 0 deletions front_end/messages/zh-TW.json
Original file line number Diff line number Diff line change
Expand Up @@ -2166,5 +2166,6 @@
"whyTrustMetaculusLessNoise": "Metaculus 的群眾驅動預測通過將每一個預測植根於透明的證據、負責的計分和十年的已證準確性,抑制了噪音。Metaculus 為政策制定者、研究人員、記者和企業組織提供基於證據的預測,為全球最重要的不確定性提供清晰、可量化的洞察。探索我們的 <servicesLink>企業解決方案</servicesLink>,了解 Metaculus 如何改善貴組織的決策。",
"publishTimeLockedDescription": "建立後將無法更改發佈時間。",
"nMore": "還有 {count} 個...",
"noHistogramData": "沒有可用的直方圖數據",
Comment thread
ncarazon marked this conversation as resolved.
Outdated
"thousandsOfOpenQuestions": "20,000+ 開放問題"
}
1 change: 1 addition & 0 deletions front_end/messages/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -2171,5 +2171,6 @@
"whyTrustMetaculusLessNoise": "Metaculus 的众包预测通过以透明证据、可追溯的评分以及十年证明的准确性为基础来削减预测中的噪音。Metaculus 为政策制定者、研究人员、记者和企业组织提供基于证据的预测,为世界上最重要的不确定性提供清晰、可量化的洞察。了解我们的<servicesLink>企业解决方案</servicesLink>,了解 Metaculus 如何改善贵组织的决策。",
"publishTimeLockedDescription": "创建后将无法更改发布时间。",
"nMore": "{count} 更多...",
"noHistogramData": "没有可用的直方图数据",
"thousandsOfOpenQuestions": "20,000+ 开放问题"
}
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,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 @@ -19,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 @@ -38,6 +42,8 @@ import { QuestionVariantComposer } from "../question_variant_composer";
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 @@ -128,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 @@ -151,9 +158,16 @@ export const ConsumerShell: FC<{
!isContinuousSingleQuestion &&
!isMultipleChoice;

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

const showSideBySide =
(isMultipleChoice || isNonFanGroup || isBinarySingleQuestion) &&
!isContinuousSingleQuestion;
isMultipleChoice ||
isNonFanGroup ||
isBinarySingleQuestion ||
isContinuousSingleQuestion;

const showClosedMessageMultipleChoice =
isMultipleChoicePost(postData) &&
Expand All @@ -166,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 All @@ -191,7 +212,7 @@ export const ConsumerShell: FC<{
<div className="order-2 sm:order-none">
<ActionRow post={postData} variant="consumer" />
</div>
<div className="order-1 mt-3 sm:order-none sm:mt-8 md:-mt-2 lg:-mt-3">
<div className="order-1 mt-3 sm:order-none sm:mt-0 md:-mt-2 lg:-mt-3">
{showClosedMessageMultipleChoice && (
<p className="m-0 mb-8 text-center text-sm leading-[20px] text-gray-700 dark:text-gray-700-dark">
{t("predictionClosedMessage")}
Expand All @@ -204,26 +225,60 @@ export const ConsumerShell: FC<{
!isMultipleChoice &&
!isNonFanGroup &&
"flex-col-reverse",
showSideBySide && "sm:flex-row sm:items-center sm:gap-8"
showSideBySide &&
cn("sm:flex-row sm:items-center", {
"sm:gap-0 md:gap-8": isBinarySingleQuestion,
"sm:gap-8": !isBinarySingleQuestion,
})
)}
>
<div
className={cn(
showSideBySide && !isDateGroup ? "order-1" : undefined,
isContinuousSingleQuestion && "md:hidden"
)}
>
<ConsumerQuestionPrediction postData={postData} />
</div>
{isBinarySingleQuestion && isQuestionPost(postData) ? (
<div className="order-1 flex w-64 flex-col items-center justify-center gap-[18px] self-center sm:self-stretch">
{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",
showSideBySide &&
!isDateGroup &&
!isContinuousSingleQuestion &&
"sm:max-w-[200px]",
hideCP &&
!isContinuousSingleQuestion &&
(isDateGroup || isFanGraph) &&
"flex w-full justify-center"
)}
>
{hideCP && !isContinuousSingleQuestion ? (
<RevealCPButton />
) : (
<ConsumerQuestionPrediction postData={postData} />
)}
</div>
)}
{!isFanGraph && !isDateGroup && (
<QuestionTimeline
postData={postData}
keyFactors={postData.key_factors}
isConsumerView={!isContinuousSingleQuestion}
isConsumerView={true}
preselectedGroupQuestionId={preselectedGroupQuestionId}
className={cn(
"hidden sm:block",
showSideBySide && "order-2 mt-0 flex-1"
showSideBySide && "order-2 mt-0 flex-1",
isContinuousSingleQuestion && "mt-0"
)}
/>
)}
Expand Down Expand Up @@ -263,18 +318,23 @@ const QuestionPageShell: FC<Props> = ({
<QuestionLayoutProvider>
<QuestionVariantComposer
consumer={
<ConsumerShell
postData={postData}
preselectedGroupQuestionId={preselectedGroupQuestionId}
mobileSidebar={mobileSidebar}
/>
<ContinuousChartCursorProvider>
{/* Bridges timeline cursor to the mobile mini chart in consumer view */}
<ConsumerShell
postData={postData}
preselectedGroupQuestionId={preselectedGroupQuestionId}
mobileSidebar={mobileSidebar}
/>
</ContinuousChartCursorProvider>
}
forecaster={
<ForecasterShell
postData={postData}
preselectedGroupQuestionId={preselectedGroupQuestionId}
mobileSidebar={mobileSidebar}
/>
<ContinuousChartCursorProvider>
<ForecasterShell
postData={postData}
preselectedGroupQuestionId={preselectedGroupQuestionId}
mobileSidebar={mobileSidebar}
/>
</ContinuousChartCursorProvider>
}
/>
</QuestionLayoutProvider>
Expand Down
Loading
Loading