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
23 changes: 23 additions & 0 deletions src/apis/aquarium.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,3 +174,26 @@ export async function getMyFishes(): Promise<UserFish[]> {
});
}
}

/**
* Aquarium Embed Code를 가져옵니다.
* @returns Embed code 정보 (html, markdown, img_url)
*/
export interface EmbedCode {
ok: boolean;
img_url: string;
html: string;
markdown: string;
}

export async function getAquariumEmbedCode(): Promise<EmbedCode> {
try {
const res = await api.get<EmbedCode>("/aquatics/embed/aquarium/");
return res.data;
} catch (e) {
throwMapped(e, {
401: "로그인이 필요합니다.",
500: "서버 오류로 embed 코드를 불러오지 못했습니다.",
});
}
}
26 changes: 26 additions & 0 deletions src/apis/fishtank.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,29 @@ export async function getSelectableFish(repoId: string): Promise<SelectableFish[
});
}
}

/**
* Fishtank Embed Code를 가져옵니다.
* @param repoId 레포지토리 ID
* @returns Embed code 정보 (html, markdown, img_url)
*/
export interface EmbedCode {
ok: boolean;
img_url: string;
html: string;
markdown: string;
detail?: string;
}

export async function getFishtankEmbedCode(repoId: number): Promise<EmbedCode> {
try {
const res = await api.get<EmbedCode>(`/aquatics/embed/fishtank/${repoId}/`);
return res.data;
} catch (e) {
throwMapped(e, {
401: "로그인이 필요합니다.",
404: "피쉬탱크를 찾을 수 없습니다.",
500: "서버 오류로 embed 코드를 불러오지 못했습니다.",
});
}
}
20 changes: 18 additions & 2 deletions src/components/MyPage/AquariumSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
getAquariumDetail,
updateAquariumFishVisibility,
getMyFishes,
getAquariumEmbedCode,
type MyBackground,
type AquariumDetail,
type UserFish,
Expand Down Expand Up @@ -240,6 +241,21 @@ export default function AquariumSection() {
});
};

// Export 핸들러
const handleExport = async () => {
try {
const embedCode = await getAquariumEmbedCode();
// Markdown 코드를 클립보드에 복사
await navigator.clipboard.writeText(embedCode.markdown);
setMessage("Markdown 코드가 클립보드에 복사되었습니다!");
setTimeout(() => setMessage(null), 3000);
} catch (error) {
console.error("Export failed:", error);
setMessage(error instanceof Error ? error.message : "Export에 실패했습니다.");
setTimeout(() => setMessage(null), 3000);
}
};

const itemCandidates: Item[] = useMemo(
() => [
{ id: "it1", name: "Corals 1", src: "/images/items/coral-1.png" },
Expand Down Expand Up @@ -320,7 +336,7 @@ export default function AquariumSection() {
<div className="flex items-center justify-between">
<div />
<button
onClick={() => console.log("EXPORT clicked")}
onClick={handleExport}
className="font-vt mb-4 ml-auto rounded-full bg-[#3F3F3F]/80 px-8 py-1.5 text-xl text-[#D7B9B9] shadow transition-colors hover:bg-[#CA9B9B]/20 focus:ring-2 focus:ring-[#CA9B9B] focus:outline-none sm:text-2xl"
>
EXPORT
Expand Down Expand Up @@ -447,7 +463,7 @@ export default function AquariumSection() {
{/* 좌: EXPORT 버튼 - 왼쪽 캔버스 영역 오른쪽 끝 */}
<div className="flex justify-end" style={{ width: "700px" }}>
<button
onClick={() => console.log("EXPORT clicked")}
onClick={handleExport}
className="font-vt rounded-full bg-[#3F3F3F]/80 px-8 py-1 text-2xl text-[#D7B9B9] shadow transition-colors hover:bg-[#CA9B9B]/20 focus:ring-2 focus:ring-[#CA9B9B] focus:outline-none"
>
EXPORT
Expand Down
25 changes: 23 additions & 2 deletions src/components/MyPage/FishTankSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
getMyBackgrounds,
getFishtankDetail,
applyFishtankBackground,
getFishtankEmbedCode,
type MyBackground,
} from "@/apis/fishtank";
import { useViewport } from "@/contexts/useViewport";
Expand Down Expand Up @@ -243,6 +244,26 @@ export default function FishTankSection() {
return nameMap[name] || name;
};

const handleExport = async () => {
if (!repo) {
setMessage("레포지토리를 선택해주세요.");
setTimeout(() => setMessage(null), 3000);
return;
}

try {
const embedCode = await getFishtankEmbedCode(repo.id);
// Markdown 코드를 클립보드에 복사
await navigator.clipboard.writeText(embedCode.markdown);
setMessage("Markdown 코드가 클립보드에 복사되었습니다!");
setTimeout(() => setMessage(null), 3000);
} catch (error) {
console.error("Export failed:", error);
setMessage(error instanceof Error ? error.message : "Export에 실패했습니다.");
setTimeout(() => setMessage(null), 3000);
}
};

const handleApply = async () => {
if (tab === "background" && selectedBgId) {
if (selectedBgId === "locked") {
Expand Down Expand Up @@ -327,7 +348,7 @@ export default function FishTankSection() {
<div className="flex items-center justify-between">
<div />
<button
onClick={() => console.log("EXPORT clicked")}
onClick={handleExport}
className="font-vt mb-4 ml-auto rounded-full bg-[#3F3F3F]/80 px-8 py-1.5 text-xl text-[#D7B9B9] shadow transition-colors hover:bg-[#CA9B9B]/20 focus:ring-2 focus:ring-[#CA9B9B] focus:outline-none sm:text-2xl"
>
EXPORT
Expand Down Expand Up @@ -456,7 +477,7 @@ export default function FishTankSection() {
{/* 좌: EXPORT 버튼 - 왼쪽 캔버스 영역 오른쪽 끝 */}
<div className="flex justify-end" style={{ width: "700px" }}>
<button
onClick={() => console.log("EXPORT clicked")}
onClick={handleExport}
className="font-vt rounded-full bg-[#3F3F3F]/80 px-8 py-1 text-2xl text-[#D7B9B9] shadow transition-colors hover:bg-[#CA9B9B]/20 focus:ring-2 focus:ring-[#CA9B9B] focus:outline-none"
>
EXPORT
Expand Down