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
1 change: 1 addition & 0 deletions front_end/messages/cs.json
Original file line number Diff line number Diff line change
Expand Up @@ -1761,5 +1761,6 @@
"noPrivateNotes": "Žádné soukromé poznámky",
"privateNotes": "Soukromé poznámky",
"justNow": "právě teď",
"cmmButtonShort": "Mysl",
"othersCount": "Ostatní ({count})"
}
1 change: 1 addition & 0 deletions front_end/messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1755,5 +1755,6 @@
"noPrivateNotes": "No private notes yet",
"privateNotes": "Private Notes",
"justNow": "just now",
"cmmButtonShort": "Mind",
"none": "none"
}
1 change: 1 addition & 0 deletions front_end/messages/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -1761,5 +1761,6 @@
"noPrivateNotes": "Aún no hay notas privadas",
"privateNotes": "Notas privadas",
"justNow": "justo ahora",
"cmmButtonShort": "Mente",
"othersCount": "Otros ({count})"
}
1 change: 1 addition & 0 deletions front_end/messages/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -1759,5 +1759,6 @@
"noPrivateNotes": "Ainda não há notas privadas",
"privateNotes": "Notas Privadas",
"justNow": "agora mesmo",
"cmmButtonShort": "Mente",
"othersCount": "Outros ({count})"
}
1 change: 1 addition & 0 deletions front_end/messages/zh-TW.json
Original file line number Diff line number Diff line change
Expand Up @@ -1758,5 +1758,6 @@
"noPrivateNotes": "尚無私人筆記",
"privateNotes": "私人筆記",
"justNow": "剛剛",
"cmmButtonShort": "心智",
"withdrawAfterPercentSetting2": "問題總生命周期後撤回"
}
1 change: 1 addition & 0 deletions front_end/messages/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -1763,5 +1763,6 @@
"noPrivateNotes": "尚無私人筆記",
"privateNotes": "私人筆記",
"justNow": "刚刚",
"cmmButtonShort": "心情",
"othersCount": "其他({count})"
}
61 changes: 20 additions & 41 deletions front_end/src/components/comment_feed/comment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -241,15 +241,12 @@ const Comment: FC<CommentProps> = ({
const userCanPredict = postData && canPredictQuestion(postData);
const userForecast =
postData?.question?.my_forecasts?.latest?.forecast_values[1] ?? 0.5;
const isCommentAuthor = comment.author.id === user?.id;
const isCmmButtonVisible =
user?.id !== comment.author.id &&
(!!postData?.question ||
!!postData?.group_of_questions ||
!!postData?.conditional);
const isCmmButtonDisabled = !user || !userCanPredict;
// TODO: find a better way to dedect whether on mobile or not. For now we need to know in JS
// too and can't use tw classes
const isMobileScreen = window.innerWidth < 640;
!!postData?.question ||
!!postData?.group_of_questions ||
!!postData?.conditional;
const isCmmButtonDisabled = !user || !userCanPredict || isCommentAuthor;

const {
draftReady: editDraftReady,
Expand Down Expand Up @@ -372,8 +369,6 @@ const Comment: FC<CommentProps> = ({
].includes(postData?.status ?? PostStatus.CLOSED);

const limitNotReached = factorsLimit > 0;
const isCommentAuthor = comment.author.id === user?.id;

const canShowAddKeyFactorsButton =
isCommentAuthor && questionNotClosed && canListKeyFactors;

Expand Down Expand Up @@ -497,22 +492,6 @@ const Comment: FC<CommentProps> = ({
}, [comment.id]);

const menuItems: MenuItemProps[] = [
{
hidden: !isMobileScreen || !isCmmButtonVisible,
id: "cmm",
element: (
<div>
<CmmToggleButton
cmmContext={cmmContext}
comment_id={comment.id}
disabled={isCmmButtonDisabled}
/>
</div>
),
onClick: () => {
return null; // handled by the button element
},
},
{
hidden: !(user?.id === comment.author.id),
id: "edit",
Expand Down Expand Up @@ -839,7 +818,7 @@ const Comment: FC<CommentProps> = ({

<div className="mb-2 mt-1 h-7 overflow-visible">
<div className="flex items-center justify-between text-sm leading-4 text-gray-900 dark:text-gray-900-dark">
<div className="inline-flex items-center gap-2.5">
<div className="flex min-w-0 flex-1 items-center gap-2.5">
<CommentVoter
voteData={{
commentAuthorId: comment.author.id,
Expand Down Expand Up @@ -883,21 +862,15 @@ const Comment: FC<CommentProps> = ({
icon={isKeyfactorsFormOpen ? faXmark : faPlus}
className="size-4 p-1"
/>
{t("addKeyFactor")}
<span className="hidden sm:inline">
{t("addKeyFactor")}
</span>
<span className="sm:hidden">{t("add")}</span>
</div>
</>
</Button>
)}

{isCmmButtonVisible && !isMobileScreen && (
<CmmToggleButton
cmmContext={cmmContext}
comment_id={comment.id}
disabled={isCmmButtonDisabled}
ref={cmmContext.setAnchorRef}
/>
)}

{!onProfile &&
(isReplying ? (
<Button
Expand Down Expand Up @@ -928,12 +901,18 @@ const Comment: FC<CommentProps> = ({
{t("reply")}
</Button>
))}

{isCmmButtonVisible && (
<CmmToggleButton
cmmContext={cmmContext}
comment_id={comment.id}
disabled={isCmmButtonDisabled}
ref={cmmContext.setAnchorRef}
/>
)}
</div>

<div
ref={isMobileScreen ? cmmContext.setAnchorRef : null}
className={cn(treeDepth > 0 && "pr-1.5 md:pr-2")}
>
<div className={cn(treeDepth > 0 && "pr-1.5 md:pr-2")}>
<DropdownMenu items={menuItems} />
</div>
</div>
Expand Down
144 changes: 125 additions & 19 deletions front_end/src/components/comment_feed/comment_cmm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,17 @@ import {
import {
faChevronLeft,
faChevronRight,
faCaretUp,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { isNil } from "lodash";
import { useTranslations } from "next-intl";
import React, { useState, forwardRef, FC } from "react";
import React, {
useState,
forwardRef,
FC,
useLayoutEffect,
useCallback,
} from "react";

import { toggleCMMComment } from "@/app/(main)/questions/actions";
import ForecastTextInput from "@/components/forecast_maker/forecast_text_input";
Expand Down Expand Up @@ -292,6 +297,48 @@ const CmmOverlay = ({
);
};

type LabelVariant = "full" | "mid" | "tiny";

function useFittingLabel<
TContainer extends HTMLElement,
TFull extends HTMLElement,
TMid extends HTMLElement,
TTiny extends HTMLElement,
>(params: {
containerRef: React.RefObject<TContainer | null>;
fullRef: React.RefObject<TFull | null>;
midRef: React.RefObject<TMid | null>;
tinyRef: React.RefObject<TTiny | null>;
}) {
const [variant, setVariant] = useState<LabelVariant>("full");

const recompute = useCallback(() => {
const container = params.containerRef.current;
if (!container) return;

const available = container.clientWidth;
const wFull = params.fullRef.current?.offsetWidth ?? 0;
const wMid = params.midRef.current?.offsetWidth ?? 0;

const next =
wFull <= available ? "full" : wMid <= available ? "mid" : "tiny";

setVariant((prev) => (prev === next ? prev : next));
}, [params.containerRef, params.fullRef, params.midRef]);

useLayoutEffect(() => {
recompute();
const el = params.containerRef.current;
if (!el) return;

const ro = new ResizeObserver(recompute);
ro.observe(el);
return () => ro.disconnect();
}, [recompute, params.containerRef]);

return variant;
}

interface CmmToggleButtonProps {
comment_id: number;
disabled?: boolean;
Expand Down Expand Up @@ -320,34 +367,72 @@ const CmmToggleButton = forwardRef<HTMLButtonElement, CmmToggleButtonProps>(
}
};

const isDisabled = isLoading || !!disabled;

const count = cmmContext.count;

const fullLabel = `${t("cmmButton")} (${count})`;
const midLabel = `${t("cmmButtonShort")} (${count})`;
const tinyLabel = `(${count})`;

const labelBoxRef = React.useRef<HTMLSpanElement>(null);
const fullMeasureRef = React.useRef<HTMLSpanElement>(null);
const midMeasureRef = React.useRef<HTMLSpanElement>(null);
const tinyMeasureRef = React.useRef<HTMLSpanElement>(null);

const variant = useFittingLabel({
containerRef: labelBoxRef,
fullRef: fullMeasureRef,
midRef: midMeasureRef,
tinyRef: tinyMeasureRef,
});

return (
<Button
size="xxs"
variant="tertiary"
onClick={onChangedMyMind}
aria-label="Changed my mind"
className="whitespace-nowrap border border-blue-400 hover:bg-gray-100 dark:hover:bg-gray-100-dark"
disabled={isLoading || disabled}
aria-label={t("cmmButton")}
disabled={isDisabled}
ref={ref}
{...cmmContext.getReferenceProps()}
className={cn(
"group relative inline-flex min-w-0 items-center gap-1 whitespace-nowrap rounded-sm border px-2 py-1 text-sm font-normal leading-[16px] tracking-tight transition-colors disabled:cursor-not-allowed",
!isDisabled &&
(cmmContext.cmmEnabled
? "hover:bg-olive-50 dark:hover:bg-olive-400/10"
: "hover:bg-blue-50 dark:hover:bg-blue-600/20"),
isDisabled &&
"border-gray-300 bg-gray-100 text-gray-400 hover:bg-gray-100 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-500"
)}
>
<FontAwesomeIcon
icon={faCaretUp}
<DeltaBadge enabled={cmmContext.cmmEnabled} disabled={isDisabled} />

<span
ref={labelBoxRef}
className={cn(
"size-4 rounded-full",
{
"bg-gradient-to-b p-1 text-blue-700 group-hover:from-blue-400 group-hover:to-blue-100 dark:text-blue-700-dark dark:group-hover:from-blue-400-dark dark:group-hover:to-blue-100-dark":
!cmmContext.cmmEnabled,
},
{
"bg-gradient-to-b from-olive-400 to-blue-100 p-1 text-olive-700 group-hover:from-olive-500 group-hover:to-blue-100 dark:from-olive-300-dark dark:to-blue-100-dark dark:text-olive-700-dark dark:group-hover:from-olive-500-dark dark:group-hover:to-blue-100-dark":
cmmContext.cmmEnabled,
}
"min-w-0 flex-1 overflow-hidden whitespace-nowrap",
"text-blue-700 dark:text-blue-700-dark",
isDisabled && "text-current"
)}
/>
>
{variant === "full"
? fullLabel
: variant === "mid"
? midLabel
: tinyLabel}
</span>

<span className="text-blue-700 dark:text-blue-700-dark">
{t("cmmButton")} ({cmmContext.count})
<span className="pointer-events-none absolute -z-10 opacity-0">
<span ref={fullMeasureRef} className="whitespace-nowrap">
{fullLabel}
</span>
<span ref={midMeasureRef} className="whitespace-nowrap">
{midLabel}
</span>
<span ref={tinyMeasureRef} className="whitespace-nowrap">
{tinyLabel}
</span>
</span>
</Button>
// <button
Expand Down Expand Up @@ -381,6 +466,27 @@ const CmmToggleButton = forwardRef<HTMLButtonElement, CmmToggleButtonProps>(
}
);

const DeltaBadge: FC<{ enabled: boolean; disabled?: boolean }> = ({
enabled,
disabled,
}) => {
return (
<span
aria-hidden="true"
className={cn(
"flex size-4 items-center justify-center rounded-full font-semibold leading-none",
"text-[12px] transition-colors",
disabled && "opacity-60",
!enabled && "bg-transparent text-blue-700 dark:text-blue-700-dark",
enabled &&
"bg-gradient-to-b from-olive-400 to-blue-100 p-1 text-olive-700 dark:from-olive-300-dark dark:to-blue-100-dark dark:text-olive-700-dark"
)}
>
</span>
);
};

CmmToggleButton.displayName = "CmmToggleButton";

export { CmmToggleButton, CmmOverlay };