From 5ace796fee9b1cce091ed2ee25c3681978262f31 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 08:19:01 +0000 Subject: [PATCH 1/2] Initial plan From e2835f7a68be350a15ff47fdabbd965526720207 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 08:22:25 +0000 Subject: [PATCH 2/2] merge: resolve conflicts with main for v0.14.0 release Co-authored-by: deholic <712513+deholic@users.noreply.github.com> --- src/infra/MastodonHttpClient.ts | 6 ++-- src/infra/MisskeyHttpClient.ts | 50 ++++++++---------------------- src/infra/UnifiedApiClient.ts | 4 --- src/services/MastodonApi.ts | 5 ++- src/ui/components/ProfileModal.tsx | 2 +- src/ui/components/TimelineItem.tsx | 35 +++++++-------------- 6 files changed, 34 insertions(+), 68 deletions(-) diff --git a/src/infra/MastodonHttpClient.ts b/src/infra/MastodonHttpClient.ts index 02bf5c8..f0625b0 100644 --- a/src/infra/MastodonHttpClient.ts +++ b/src/infra/MastodonHttpClient.ts @@ -422,8 +422,10 @@ export class MastodonHttpClient implements MastodonApi { throw new Error("리액션은 미스키 계정에서만 사용할 수 있습니다."); } - async fetchNoteState(account: Account, noteId: string): Promise<{ isFavourited: boolean; isReblogged: boolean; bookmarked: boolean }> { - // 마스토돈은 이미 Status 객체에 즐겨찾기, 리블로그, 북마크 상태가 포함되어 있음 + async fetchNoteState( + account: Account, + noteId: string + ): Promise<{ isFavourited: boolean; isReblogged: boolean; bookmarked: boolean }> { const response = await fetch(`${account.instanceUrl}/api/v1/statuses/${noteId}`, { headers: { "Authorization": `Bearer ${account.accessToken}` 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 5785554..fd8ba76 100644 --- a/src/infra/UnifiedApiClient.ts +++ b/src/infra/UnifiedApiClient.ts @@ -122,10 +122,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 { return this.getClient(account).fetchThreadContext(account, statusId); } diff --git a/src/services/MastodonApi.ts b/src/services/MastodonApi.ts index f09c5d8..54fa689 100644 --- a/src/services/MastodonApi.ts +++ b/src/services/MastodonApi.ts @@ -40,5 +40,8 @@ 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 }>; + 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 262f3da..54bcf16 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 91c833d..52ff39c 100644 --- a/src/ui/components/TimelineItem.tsx +++ b/src/ui/components/TimelineItem.tsx @@ -366,11 +366,11 @@ export const TimelineItem = ({ 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); @@ -1375,32 +1375,24 @@ export const TimelineItem = ({ <>