feat: add preview of images in the quest

This commit is contained in:
2026-04-23 09:32:47 +02:00
parent fb06def5aa
commit a397c86bc3

View File

@ -4,8 +4,6 @@ import { openUrl } from "@tauri-apps/plugin-opener";
import { QuestStep } from "../types"; import { QuestStep } from "../types";
import { TextWithCoords } from "./TextWithCoords"; import { TextWithCoords } from "./TextWithCoords";
const PREVIEW_LENGTH = 280;
interface Props { interface Props {
questName: string; questName: string;
questUrl: string | null; questUrl: string | null;
@ -146,40 +144,70 @@ export default function QuestDetailPanel({ questName, questUrl, profileId, onClo
</div> </div>
)} )}
{/* Quest info header (first step) */}
{!loading && headerStep && ( {!loading && headerStep && (
<QuestHeader step={headerStep} /> <QuestHeader step={headerStep} />
)} )}
{/* Action steps */}
{!loading && actionSteps.map((step) => { {!loading && actionSteps.map((step) => {
const done = completedSteps.has(step.index); const done = completedSteps.has(step.index);
const expanded = expandedSteps.has(step.index); const expanded = expandedSteps.has(step.index);
const needsTruncate = step.text.length > PREVIEW_LENGTH;
if (done) {
const firstLine = step.text.split('\n').find(l => l.trim().length > 0) ?? step.text;
return (
<div key={step.index} style={{
marginBottom: "4px",
padding: "6px 12px",
border: "1px solid rgba(74,222,128,0.1)",
borderRadius: "7px",
background: "rgba(74,222,128,0.03)",
opacity: 0.5,
}}>
<div style={{ display: "flex", gap: "10px", alignItems: "center" }}>
<input
type="checkbox"
checked={true}
onChange={() => toggleStep(step.index)}
style={{ flexShrink: 0, cursor: "pointer" }}
/>
<span style={{
fontSize: "12px", color: "#4a5568",
textDecoration: "line-through",
overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap",
flex: 1, minWidth: 0,
}}>
{firstLine}
</span>
</div>
</div>
);
}
const lines = step.text.split('\n').filter(l => l.trim().length > 0);
const needsTruncate = lines.length > 4;
const displayText = needsTruncate && !expanded const displayText = needsTruncate && !expanded
? step.text.slice(0, PREVIEW_LENGTH).trimEnd() + "…" ? lines.slice(0, 4).join('\n')
: step.text; : step.text;
return ( return (
<div key={step.index} style={{ <div key={step.index} style={{
marginBottom: "8px", marginBottom: "8px",
background: done ? "rgba(74,222,128,0.04)" : "rgba(255,255,255,0.02)", background: "rgba(255,255,255,0.02)",
border: `1px solid ${done ? "rgba(74,222,128,0.2)" : "#2d3748"}`, border: "1px solid #2d3748",
borderRadius: "7px", padding: "10px 12px", borderRadius: "7px", padding: "10px 12px",
opacity: done ? 0.65 : 1, transition: "all 0.15s", transition: "all 0.15s",
}}> }}>
<div style={{ display: "flex", gap: "10px", alignItems: "flex-start" }}> <div style={{ display: "flex", gap: "10px", alignItems: "flex-start" }}>
<input <input
type="checkbox" type="checkbox"
checked={done} checked={false}
onChange={() => toggleStep(step.index)} onChange={() => toggleStep(step.index)}
style={{ marginTop: "2px", flexShrink: 0 }} style={{ marginTop: "2px", flexShrink: 0, cursor: "pointer" }}
/> />
<div style={{ flex: 1, minWidth: 0 }}> <div style={{ flex: 1, minWidth: 0 }}>
<div style={{ <div style={{
fontSize: "12px", color: done ? "#4a5568" : "#94a3b8", fontSize: "12px", color: "#94a3b8",
lineHeight: 1.6, whiteSpace: "pre-wrap", wordBreak: "break-word", lineHeight: 1.6, whiteSpace: "pre-wrap", wordBreak: "break-word",
textDecoration: done ? "line-through" : "none",
}}> }}>
<TextWithCoords text={displayText} /> <TextWithCoords text={displayText} />
</div> </div>
@ -198,21 +226,18 @@ export default function QuestDetailPanel({ questName, questUrl, profileId, onClo
)} )}
{step.images.length > 0 && ( {step.images.length > 0 && (
<div style={{ marginTop: "6px", display: "flex", flexWrap: "wrap", gap: "4px" }}> <div style={{ marginTop: "8px", display: "flex", flexDirection: "column", gap: "6px" }}>
{step.images.map((src, j) => ( {step.images.map((src, j) => (
<button <img
key={j} key={j}
src={src}
onClick={() => openUrl(src)} onClick={() => openUrl(src)}
style={{ style={{
background: "rgba(74,158,255,0.08)", border: "1px solid rgba(74,158,255,0.25)", maxWidth: "100%", height: "auto",
borderRadius: "4px", color: "#4a9eff", fontSize: "10px", borderRadius: "6px", display: "block",
padding: "2px 8px", cursor: "pointer", border: "1px solid #2d3748", cursor: "pointer",
}} }}
onMouseEnter={e => (e.currentTarget.style.background = "rgba(74,158,255,0.18)")} />
onMouseLeave={e => (e.currentTarget.style.background = "rgba(74,158,255,0.08)")}
>
🖼 Image {j + 1}
</button>
))} ))}
</div> </div>
)} )}
@ -249,21 +274,18 @@ function QuestHeader({ step }: { step: QuestStep }) {
</div> </div>
{step.images.length > 0 && ( {step.images.length > 0 && (
<div style={{ padding: "0 14px 10px", display: "flex", flexWrap: "wrap", gap: "4px" }}> <div style={{ padding: "0 14px 10px", display: "flex", flexDirection: "column", gap: "6px" }}>
{step.images.map((src, j) => ( {step.images.map((src, j) => (
<button <img
key={j} key={j}
src={src}
onClick={() => openUrl(src)} onClick={() => openUrl(src)}
style={{ style={{
background: "rgba(240,192,64,0.08)", border: "1px solid rgba(240,192,64,0.2)", maxWidth: "100%", height: "auto",
borderRadius: "4px", color: "#f0c040", fontSize: "10px", borderRadius: "6px", display: "block",
padding: "2px 8px", cursor: "pointer", border: "1px solid #2d3748", cursor: "pointer",
}} }}
onMouseEnter={e => (e.currentTarget.style.background = "rgba(240,192,64,0.15)")} />
onMouseLeave={e => (e.currentTarget.style.background = "rgba(240,192,64,0.08)")}
>
🖼 Image {j + 1}
</button>
))} ))}
</div> </div>
)} )}