From f79b4df2517323f14dbc92daaf2131b8ba59da68 Mon Sep 17 00:00:00 2001 From: Euigyom Kim Date: Sun, 18 Jan 2026 20:49:23 +0900 Subject: [PATCH] =?UTF-8?q?Revert=20"=EB=AF=B8=EC=8A=A4=ED=82=A4=20?= =?UTF-8?q?=EC=A6=90=EA=B2=A8=EC=B0=BE=EA=B8=B0=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EB=B0=8F=20=ED=94=8C=EB=9E=AB=ED=8F=BC=EB=B3=84=20=EB=B6=81?= =?UTF-8?q?=EB=A7=88=ED=81=AC=20=EB=B6=84=EB=A6=AC=20=EA=B5=AC=ED=98=84"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 4 +- src/infra/MastodonHttpClient.ts | 27 ++-------- src/infra/MisskeyHttpClient.ts | 50 +++++------------ src/infra/UnifiedApiClient.ts | 4 -- src/services/MastodonApi.ts | 1 - src/ui/components/ProfileModal.tsx | 2 +- src/ui/components/TimelineItem.tsx | 87 ++++-------------------------- 7 files changed, 30 insertions(+), 145 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 2cfcfda..9f6ff0c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -560,9 +560,9 @@ const TimelineSection = ({ : await services.api.bookmark(account, status.id); timeline.updateItem(updated); if (isBookmarking) { - showToast("북마크했습니다.", { tone: "success" }); + showToast("북마크했습니다."); } else { - showToast("북마크를 취소했습니다.", { tone: "success" }); + showToast("북마크를 취소했습니다."); } } catch (err) { onError(err instanceof Error ? err.message : "북마크 처리에 실패했습니다."); diff --git a/src/infra/MastodonHttpClient.ts b/src/infra/MastodonHttpClient.ts index 9280c78..4072968 100644 --- a/src/infra/MastodonHttpClient.ts +++ b/src/infra/MastodonHttpClient.ts @@ -398,12 +398,12 @@ export class MastodonHttpClient implements MastodonApi { } } - async favourite(_account: Account, _statusId: string): Promise { - throw new Error("즐겨찾기는 미스키 계정에서만 사용할 수 있습니다."); + async favourite(account: Account, statusId: string): Promise { + return this.postAction(account, statusId, "favourite"); } - async unfavourite(_account: Account, _statusId: string): Promise { - throw new Error("즐겨찾기는 미스키 계정에서만 사용할 수 있습니다."); + async unfavourite(account: Account, statusId: string): Promise { + return this.postAction(account, statusId, "unfavourite"); } async bookmark(account: Account, statusId: string): Promise { @@ -422,25 +422,6 @@ export class MastodonHttpClient implements MastodonApi { throw new Error("리액션은 미스키 계정에서만 사용할 수 있습니다."); } - async fetchNoteState(account: Account, noteId: string): Promise<{ isFavourited: boolean; isReblogged: boolean; bookmarked: boolean }> { - // 마스토돈은 이미 Status 객체에 즐겨찾기, 리블로그, 북마크 상태가 포함되어 있음 - const response = await fetch(`${account.instanceUrl}/api/v1/statuses/${noteId}`, { - headers: { - "Authorization": `Bearer ${account.accessToken}` - } - }); - if (!response.ok) { - throw new Error("게시물 상태를 불러오지 못했습니다."); - } - const data = (await response.json()) as unknown; - const status = mapStatus(data); - return { - isFavourited: status.favourited, - isReblogged: status.reblogged, - bookmarked: status.bookmarked - }; - } - async reblog(account: Account, statusId: string): Promise { return this.postAction(account, statusId, "reblog"); } diff --git a/src/infra/MisskeyHttpClient.ts b/src/infra/MisskeyHttpClient.ts index 0a12121..9230568 100644 --- a/src/infra/MisskeyHttpClient.ts +++ b/src/infra/MisskeyHttpClient.ts @@ -421,12 +421,23 @@ export class MisskeyHttpClient implements MastodonApi { } async favourite(account: Account, statusId: string): Promise { - await this.postSimple(account, "/api/notes/favorites/create", { noteId: statusId }); + try { + await this.postSimple(account, "/api/notes/reactions/create", { + noteId: statusId, + reaction: DEFAULT_REACTION + }); + } catch { + await this.postSimple(account, "/api/notes/favorites/create", { noteId: statusId }); + } return this.fetchNote(account, statusId); } async unfavourite(account: Account, statusId: string): Promise { - await this.postSimple(account, "/api/notes/favorites/delete", { noteId: statusId }); + try { + await this.postSimple(account, "/api/notes/reactions/delete", { noteId: statusId }); + } catch { + await this.postSimple(account, "/api/notes/favorites/delete", { noteId: statusId }); + } return this.fetchNote(account, statusId); } @@ -515,41 +526,6 @@ export class MisskeyHttpClient implements MastodonApi { return data.map((item) => mapMisskeyStatusWithInstance(item, account.instanceUrl)); } - async fetchNoteState(account: Account, noteId: string): Promise<{ isFavourited: boolean; isReblogged: boolean; bookmarked: boolean }> { - const response = await fetch(`${normalizeInstanceUrl(account.instanceUrl)}/api/notes/state`, { - method: "POST", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify(buildBody(account, { noteId })) - }); - if (!response.ok) { - throw new Error("게시물 상태를 불러오지 못했습니다."); - } - const data = (await response.json()) as Record; - return { - isFavourited: Boolean(data.isFavorited ?? data.isFavourited ?? false), - isReblogged: Boolean(data.isRenoted ?? false), - bookmarked: Boolean(data.bookmarked ?? false) - }; - } - - async bookmark(_account: Account, _statusId: string): Promise { - throw new Error("북마크는 마스토돈 계정에서만 사용할 수 있습니다."); - } - - async unbookmark(_account: Account, _statusId: string): Promise { - throw new Error("북마크는 마스토돈 계정에서만 사용할 수 있습니다."); - } - - async fetchBookmarks(_account: Account, _limit?: number, _maxId?: string): Promise { - throw new Error("북마크는 마스토돈 계정에서만 사용할 수 있습니다."); - } - - async fetchThreadContext(account: Account, statusId: string): Promise { - return this.fetchConversation(account, statusId); - } - private async fetchNotifications( account: Account, limit: number, diff --git a/src/infra/UnifiedApiClient.ts b/src/infra/UnifiedApiClient.ts index 03558a0..cc7aa29 100644 --- a/src/infra/UnifiedApiClient.ts +++ b/src/infra/UnifiedApiClient.ts @@ -123,10 +123,6 @@ export class UnifiedApiClient implements MastodonApi { return this.getClient(account).unreblog(account, statusId); } - fetchNoteState(account: Account, noteId: string) { - return this.getClient(account).fetchNoteState(account, noteId); - } - async fetchThreadContext(account: Account, statusId: string): Promise { if (account.platform === "misskey") { // MisskeyHttpClient에는 fetchConversation 메서드가 있음 diff --git a/src/services/MastodonApi.ts b/src/services/MastodonApi.ts index f09c5d8..2f63add 100644 --- a/src/services/MastodonApi.ts +++ b/src/services/MastodonApi.ts @@ -40,5 +40,4 @@ export interface MastodonApi { unblockAccount(account: Account, accountId: string): Promise; fetchAccountStatuses(account: Account, accountId: string, limit: number, maxId?: string): Promise; fetchThreadContext(account: Account, statusId: string): Promise; - fetchNoteState(account: Account, noteId: string): Promise<{ isFavourited: boolean; isReblogged: boolean; bookmarked: boolean }>; } diff --git a/src/ui/components/ProfileModal.tsx b/src/ui/components/ProfileModal.tsx index 8106315..f65c606 100644 --- a/src/ui/components/ProfileModal.tsx +++ b/src/ui/components/ProfileModal.tsx @@ -378,7 +378,7 @@ export const ProfileModal = ({ ? await api.unbookmark(account, target.id) : await api.bookmark(account, target.id); updateItem(updated); - showToast(isBookmarking ? "북마크했습니다." : "북마크를 취소했습니다.", { tone: "success" }); + showToast(isBookmarking ? "북마크했습니다." : "북마크를 취소했습니다."); } catch (error) { setItemsError(error instanceof Error ? error.message : "북마크 처리에 실패했습니다."); } diff --git a/src/ui/components/TimelineItem.tsx b/src/ui/components/TimelineItem.tsx index 3f0fcb0..773de1d 100644 --- a/src/ui/components/TimelineItem.tsx +++ b/src/ui/components/TimelineItem.tsx @@ -10,7 +10,6 @@ import { ReactionPicker } from "./ReactionPicker"; import { useClickOutside } from "../hooks/useClickOutside"; import { useImageZoom } from "../hooks/useImageZoom"; import { AccountLabel } from "./AccountLabel"; -import { useToast } from "../state/ToastContext"; const normalizeMentionHandle = (handle: string): string => handle.replace(/^@/, "").trim().toLowerCase(); @@ -72,31 +71,10 @@ export const TimelineItem = ({ const [activeImageIndex, setActiveImageIndex] = useState(null); const [showContent, setShowContent] = useState(() => displayStatus.spoilerText.length === 0); const [menuOpen, setMenuOpen] = useState(false); - const [favouriteState, setFavouriteState] = useState(false); const imageContainerRef = useRef(null); const imageRef = useRef(null); const menuRef = useRef(null); const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); - const { showToast } = useToast(); - - // 메뉴 열 때 즐겨찾기 상태 확인 (미스키만) - const handleMenuToggle = useCallback(async () => { - const willOpen = !menuOpen; - setMenuOpen(willOpen); - - if (willOpen && account && api && account.platform === "misskey") { - // 초기 상태를 null로 설정하여 비활성화 상태로 표시 - setFavouriteState(null); - - try { - const state = await api.fetchNoteState(account, displayStatus.id); - setFavouriteState(state.isFavourited); - } catch (error) { - console.error("즐겨찾기 상태 확인 실패:", error); - setFavouriteState(false); // 실패 시 기본값은 false로 설정 - } - } - }, [menuOpen, account, api, displayStatus.id]); // useImageZoom 사용 const { @@ -874,7 +852,7 @@ export const TimelineItem = ({ className="icon-button" aria-label="게시글 메뉴 열기" aria-haspopup="menu" aria-expanded={menuOpen} - onClick={handleMenuToggle} + onClick={() => setMenuOpen((current) => !current)} >