feat: add preview of images in the quest
This commit is contained in:
@ -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>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user