feat(share): symbol page share button + distinguish note delete vs failure

- New ShareButton in the page header tries navigator.share() first
  (native sheet on mobile), then falls back to navigator.clipboard, then
  to a hidden textarea + execCommand("copy") for non-secure-context LAN.
  Shows a brief "링크 복사됨" / "복사 안됨" toast; native share success
  shows none (the OS sheet is the feedback).
- Mounted between ⇄ 비교 and the Star button.

Fixes reviewer concern on 442cc38: writeNote() used to return null for
both "user emptied the textarea (intentional delete)" and "localStorage
quota failure," so clearing a note showed "저장 안됨." It now returns a
discriminated { status: "saved" | "deleted" | "failed" } and the panel
shows "저장됨" / "삭제됨" / "저장 안됨" accordingly. Failure keeps the
user's text so they can retry or shorten it.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
claude-owner
2026-05-29 00:08:37 +09:00
parent 442cc38c32
commit f18493fad4
4 changed files with 129 additions and 10 deletions

View File

@@ -7,6 +7,12 @@
import { useEffect, useMemo, useState } from "react";
import { NOTE_MAX_LEN, readNote, writeNote, type Note } from "../lib/notes";
const FEEDBACK_TEXT: Record<"saved" | "deleted" | "failed", string> = {
saved: "저장됨",
deleted: "삭제됨",
failed: "저장 안됨",
};
type Props = {
code: string;
};
@@ -60,10 +66,16 @@ export function InvestmentNote({ code }: Props) {
const remaining = useMemo(() => NOTE_MAX_LEN - text.length, [text]);
const save = () => {
const next = writeNote(code, text);
setSaved(next);
setText(next?.text ?? "");
setFeedback(next ? "저장됨" : "저장 안됨");
const res = writeNote(code, text);
if (res.status === "saved") {
setSaved(res.note);
setText(res.note.text);
} else if (res.status === "deleted") {
setSaved(null);
setText("");
}
// failed: 입력값 유지 — 사용자가 재시도하거나 일부를 줄일 수 있도록.
setFeedback(FEEDBACK_TEXT[res.status]);
};
return (