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 { TextWithCoords } from "./TextWithCoords";
const PREVIEW_LENGTH = 280;
interface Props {
questName: string;
questUrl: string | null;
@ -146,40 +144,70 @@ export default function QuestDetailPanel({ questName, questUrl, profileId, onClo
</div>
)}
{/* Quest info header (first step) */}
{!loading && headerStep && (
<QuestHeader step={headerStep} />
)}
{/* Action steps */}
{!loading && actionSteps.map((step) => {
const done = completedSteps.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
? step.text.slice(0, PREVIEW_LENGTH).trimEnd() + "…"
? lines.slice(0, 4).join('\n')
: step.text;
return (
<div key={step.index} style={{
marginBottom: "8px",
background: done ? "rgba(74,222,128,0.04)" : "rgba(255,255,255,0.02)",
border: `1px solid ${done ? "rgba(74,222,128,0.2)" : "#2d3748"}`,
background: "rgba(255,255,255,0.02)",
border: "1px solid #2d3748",
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" }}>
<input
type="checkbox"
checked={done}
checked={false}
onChange={() => toggleStep(step.index)}
style={{ marginTop: "2px", flexShrink: 0 }}
style={{ marginTop: "2px", flexShrink: 0, cursor: "pointer" }}
/>
<div style={{ flex: 1, minWidth: 0 }}>
<div style={{
fontSize: "12px", color: done ? "#4a5568" : "#94a3b8",
fontSize: "12px", color: "#94a3b8",
lineHeight: 1.6, whiteSpace: "pre-wrap", wordBreak: "break-word",
textDecoration: done ? "line-through" : "none",
}}>
<TextWithCoords text={displayText} />
</div>
@ -198,21 +226,18 @@ export default function QuestDetailPanel({ questName, questUrl, profileId, onClo
)}
{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) => (
<button
<img
key={j}
src={src}
onClick={() => openUrl(src)}
style={{
background: "rgba(74,158,255,0.08)", border: "1px solid rgba(74,158,255,0.25)",
borderRadius: "4px", color: "#4a9eff", fontSize: "10px",
padding: "2px 8px", cursor: "pointer",
maxWidth: "100%", height: "auto",
borderRadius: "6px", display: "block",
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>
)}
@ -249,21 +274,18 @@ function QuestHeader({ step }: { step: QuestStep }) {
</div>
{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) => (
<button
<img
key={j}
src={src}
onClick={() => openUrl(src)}
style={{
background: "rgba(240,192,64,0.08)", border: "1px solid rgba(240,192,64,0.2)",
borderRadius: "4px", color: "#f0c040", fontSize: "10px",
padding: "2px 8px", cursor: "pointer",
maxWidth: "100%", height: "auto",
borderRadius: "6px", display: "block",
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>
)}