beam: refine model attachment status icon
This commit is contained in:
parent
3e41c451c1
commit
677916389f
|
|
@ -15,9 +15,9 @@ import type { TIssueServiceType } from "@plane/types";
|
|||
import { EIssueServiceType } from "@plane/types";
|
||||
// ui
|
||||
import type { TContextMenuItem } from "@plane/ui";
|
||||
import { ActionDropdown, EModalPosition, EModalWidth, ModalCore } from "@plane/ui";
|
||||
import { ActionDropdown, EModalPosition, EModalWidth, ModalCore, Spinner } from "@plane/ui";
|
||||
import { convertBytesToSize, getFileExtension, getFileName, getFileURL, renderFormattedDate } from "@plane/utils";
|
||||
import { Box, Download, Eye, ImageIcon, Play, X } from "lucide-react";
|
||||
import { AlertTriangle, Box, Download, Eye, ImageIcon, Play, X } from "lucide-react";
|
||||
// components
|
||||
//
|
||||
import { ButtonAvatars } from "@/components/dropdowns/member/avatar";
|
||||
|
|
@ -90,6 +90,7 @@ export const IssueAttachmentsListItem = observer(function IssueAttachmentsListIt
|
|||
const previewURL = appendSearchParam(fileURL, "preview", "true");
|
||||
const beamViewer = getBeamViewerAttachment(attachment);
|
||||
const [beamConversionStatus, setBeamConversionStatus] = useState<TBeamConversionStatus | null>(null);
|
||||
const [beamConversionError, setBeamConversionError] = useState<string | null>(null);
|
||||
const resolvedArtifactUrl = beamConversionStatus?.status === "ready" ? beamConversionStatus.artifactUrl : undefined;
|
||||
const modelViewerUrl =
|
||||
beamViewer?.viewerUrl ||
|
||||
|
|
@ -102,11 +103,22 @@ export const IssueAttachmentsListItem = observer(function IssueAttachmentsListIt
|
|||
: undefined);
|
||||
const modelDownloadUrl = beamViewer?.downloadUrl || beamViewer?.src;
|
||||
const previewDownloadUrl = modelDownloadUrl || fileURL;
|
||||
const beamConversionStatusLabel = getBeamConversionStatusLabel(
|
||||
beamConversionStatus?.status ?? beamViewer?.conversion?.status
|
||||
);
|
||||
const isBeamAwaitingPreview = !!beamViewer && !modelViewerUrl;
|
||||
const isBeamModel = isBeamModelFile(fullFileName);
|
||||
const beamEffectiveStatus = beamConversionStatus?.status ?? beamViewer?.conversion?.status;
|
||||
const beamConversionStatusLabel = getBeamConversionStatusLabel(beamEffectiveStatus);
|
||||
const beamStatusErrorMessage =
|
||||
beamConversionError ||
|
||||
beamConversionStatus?.error ||
|
||||
(beamConversionStatus?.status === "ready" && !modelViewerUrl
|
||||
? "Beam вернул статус готовности, но не вернул viewer-артефакт."
|
||||
: null);
|
||||
const isBeamConversionFailed = beamEffectiveStatus === "failed" || !!beamStatusErrorMessage;
|
||||
const isBeamAwaitingPreview = !!beamViewer && !modelViewerUrl && !isBeamConversionFailed;
|
||||
const beamStatusTooltipContent = isBeamConversionFailed
|
||||
? beamStatusErrorMessage || beamConversionStatus?.message || "Ошибка подготовки дерева компонентов."
|
||||
: beamConversionStatus?.message ||
|
||||
beamViewer?.conversion?.message ||
|
||||
"Оригинальная модель загружена в Beam. Preview появится после подготовки GLB/XKT и дерева компонентов.";
|
||||
const [isPreviewOpen, setIsPreviewOpen] = useState(false);
|
||||
const [isModelViewerOpen, setIsModelViewerOpen] = useState(false);
|
||||
const [isThumbnailError, setIsThumbnailError] = useState(false);
|
||||
|
|
@ -148,21 +160,23 @@ export const IssueAttachmentsListItem = observer(function IssueAttachmentsListIt
|
|||
const { isMobile } = usePlatformOS();
|
||||
|
||||
useEffect(() => {
|
||||
if (!beamViewer || beamViewer.viewerUrl || !beamViewer.conversion) return;
|
||||
if (!beamViewer || beamViewer.viewerUrl || !beamViewer.src) return;
|
||||
|
||||
let isMounted = true;
|
||||
let timeoutId: ReturnType<typeof window.setTimeout> | undefined;
|
||||
let timeoutId: number | undefined;
|
||||
|
||||
const pollStatus = async () => {
|
||||
try {
|
||||
const status = await fetchBeamConversionStatus(beamViewer);
|
||||
if (!isMounted) return;
|
||||
setBeamConversionError(null);
|
||||
setBeamConversionStatus(status);
|
||||
if (status.status !== "ready" && status.status !== "failed") {
|
||||
timeoutId = window.setTimeout(pollStatus, 5000);
|
||||
}
|
||||
} catch (_error) {
|
||||
} catch (error) {
|
||||
if (!isMounted) return;
|
||||
setBeamConversionError(error instanceof Error ? error.message : "Не удалось получить статус Beam.");
|
||||
timeoutId = window.setTimeout(pollStatus, 8000);
|
||||
}
|
||||
};
|
||||
|
|
@ -211,7 +225,25 @@ export const IssueAttachmentsListItem = observer(function IssueAttachmentsListIt
|
|||
) : (
|
||||
<div className="flex h-full w-full items-center justify-center">
|
||||
{isBeamModel ? (
|
||||
<Box className="size-9 text-[rgb(var(--nodedc-accent-rgb))]" />
|
||||
isBeamConversionFailed ? (
|
||||
<Tooltip tooltipContent={beamStatusTooltipContent} isMobile={isMobile}>
|
||||
<div className="grid size-9 place-items-center text-red-400">
|
||||
<AlertTriangle className="size-9" />
|
||||
</div>
|
||||
</Tooltip>
|
||||
) : isBeamAwaitingPreview ? (
|
||||
<Tooltip tooltipContent={beamStatusTooltipContent} isMobile={isMobile}>
|
||||
<div className="grid size-9 place-items-center">
|
||||
<Spinner
|
||||
height="36px"
|
||||
width="36px"
|
||||
className="fill-[rgb(var(--nodedc-accent-rgb))] text-[rgba(var(--nodedc-accent-rgb),0.18)]"
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Box className="size-9 text-[rgb(var(--nodedc-accent-rgb))]" />
|
||||
)
|
||||
) : previewType === "file" ? (
|
||||
fileIcon
|
||||
) : (
|
||||
|
|
@ -231,11 +263,6 @@ export const IssueAttachmentsListItem = observer(function IssueAttachmentsListIt
|
|||
<span className="size-1 rounded-full bg-layer-1" />
|
||||
<span>{convertBytesToSize(attachment.attributes.size)}</span>
|
||||
</div>
|
||||
{beamConversionStatusLabel && (
|
||||
<div className="mt-2 w-fit whitespace-nowrap rounded-full bg-[rgba(var(--nodedc-accent-rgb),0.12)] px-2 py-0.5 text-10 font-semibold text-[rgb(var(--nodedc-accent-rgb))]">
|
||||
{beamConversionStatusLabel}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{attachment?.created_by && (
|
||||
|
|
@ -311,17 +338,33 @@ export const IssueAttachmentsListItem = observer(function IssueAttachmentsListIt
|
|||
</div>
|
||||
) : previewType === "pdf" && previewURL ? (
|
||||
<IssueAttachmentPdfPreview fileURL={previewURL} />
|
||||
) : isBeamAwaitingPreview ? (
|
||||
) : isBeamAwaitingPreview || isBeamConversionFailed ? (
|
||||
<div className="flex h-full flex-col items-center justify-center gap-4 p-8 text-center">
|
||||
<div className="grid size-20 place-items-center rounded-3xl bg-[rgba(var(--nodedc-accent-rgb),0.12)] text-[rgb(var(--nodedc-accent-rgb))]">
|
||||
<Box className="size-10" />
|
||||
<div
|
||||
className={`grid size-20 place-items-center rounded-3xl ${
|
||||
isBeamConversionFailed
|
||||
? "bg-red-500/10 text-red-400"
|
||||
: "bg-[rgba(var(--nodedc-accent-rgb),0.12)] text-[rgb(var(--nodedc-accent-rgb))]"
|
||||
}`}
|
||||
>
|
||||
{isBeamConversionFailed ? (
|
||||
<AlertTriangle className="size-10" />
|
||||
) : (
|
||||
<Spinner
|
||||
height="40px"
|
||||
width="40px"
|
||||
className="fill-[rgb(var(--nodedc-accent-rgb))] text-[rgba(var(--nodedc-accent-rgb),0.18)]"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="max-w-md">
|
||||
<div className="text-15 font-semibold text-primary">
|
||||
{beamConversionStatusLabel ?? "Ожидает дерево"}
|
||||
{isBeamConversionFailed ? "Ошибка дерева" : beamConversionStatusLabel ?? "Ожидает дерево"}
|
||||
</div>
|
||||
<div className="mt-2 text-14 text-secondary">
|
||||
Оригинальный STEP загружен в Beam. Preview появится после подготовки GLB/XKT и дерева компонентов.
|
||||
{isBeamConversionFailed
|
||||
? beamStatusTooltipContent
|
||||
: "Оригинальный STEP загружен в Beam. Preview появится после подготовки GLB/XKT и дерева компонентов."}
|
||||
</div>
|
||||
</div>
|
||||
{modelDownloadUrl && (
|
||||
|
|
|
|||
Loading…
Reference in New Issue