From 40fe421a66d05aeae79ab7d45ab66d1f28c95058 Mon Sep 17 00:00:00 2001 From: Euigyom Kim Date: Fri, 9 Jan 2026 09:47:02 +0900 Subject: [PATCH 1/2] fix: align thread popup bg in dark mode --- src/ui/styles/themes/dark.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ui/styles/themes/dark.css b/src/ui/styles/themes/dark.css index b74b449..5195595 100644 --- a/src/ui/styles/themes/dark.css +++ b/src/ui/styles/themes/dark.css @@ -33,6 +33,8 @@ --shadow-timeline-column: inset 0 1px 0 rgba(255, 255, 255, 0.05), 0 12px 24px rgba(0, 0, 0, 0.35); --color-status-bg: #171512; --color-status-border: #302822; + --color-thread-bg: var(--color-status-bg); + --color-thread-border: var(--color-status-border); --color-status-header: #c4b6a6; --color-status-meta: #c4b6a6; --color-status-time: #c4b6a6; @@ -492,6 +494,8 @@ --shadow-timeline-column: inset 0 1px 0 rgba(255, 255, 255, 0.05), 0 12px 24px rgba(0, 0, 0, 0.35); --color-status-bg: #171512; --color-status-border: #302822; + --color-thread-bg: var(--color-status-bg); + --color-thread-border: var(--color-status-border); --color-status-header: #c4b6a6; --color-status-meta: #c4b6a6; --color-status-time: #c4b6a6; From 7f43be590564016696443f6d0422c72677621e9d Mon Sep 17 00:00:00 2001 From: Euigyom Kim Date: Fri, 9 Jan 2026 10:07:12 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20=EB=AF=B8=EC=8A=A4=ED=82=A4=20?= =?UTF-8?q?=EC=8A=A4=EB=A0=88=EB=93=9C=20=ED=9B=84=EC=86=90=20=EB=A1=9C?= =?UTF-8?q?=EB=94=A9=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/infra/MisskeyHttpClient.ts | 70 +++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 6 deletions(-) diff --git a/src/infra/MisskeyHttpClient.ts b/src/infra/MisskeyHttpClient.ts index 6a1bf34..ad1e461 100644 --- a/src/infra/MisskeyHttpClient.ts +++ b/src/infra/MisskeyHttpClient.ts @@ -68,6 +68,59 @@ const mapMisskeyEmojis = (data: unknown): CustomEmoji[] => { }; export class MisskeyHttpClient implements MastodonApi { + private async fetchNoteChildren( + account: Account, + noteId: string, + limit: number + ): Promise { + const response = await fetch(`${normalizeInstanceUrl(account.instanceUrl)}/api/notes/children`, { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify(buildBody(account, { noteId, limit })) + }); + if (!response.ok) { + throw new Error("답글을 불러오지 못했습니다."); + } + const data = (await response.json()) as unknown[]; + return data + .map((item) => mapMisskeyStatusWithInstance(item, account.instanceUrl)) + .filter((status): status is Status => status !== null); + } + + private async fetchDescendants( + account: Account, + noteId: string, + maxNotes: number, + maxDepth: number + ): Promise { + const queue: Array<{ id: string; depth: number }> = [{ id: noteId, depth: 0 }]; + const seen = new Set([noteId]); + const result: Status[] = []; + + while (queue.length > 0 && result.length < maxNotes) { + const current = queue.shift(); + if (!current || current.depth >= maxDepth) { + continue; + } + const children = await this.fetchNoteChildren(account, current.id, 100); + for (const child of children) { + if (seen.has(child.id)) { + continue; + } + seen.add(child.id); + result.push(child); + if (result.length >= maxNotes) { + break; + } + queue.push({ id: child.id, depth: current.depth + 1 }); + } + } + + return result.sort((a, b) => a.createdAt.localeCompare(b.createdAt)); + } + async verifyAccount( account: Account ): Promise<{ accountName: string; handle: string; avatarUrl: string | null }> { @@ -195,14 +248,19 @@ export class MisskeyHttpClient implements MastodonApi { // 미스키는 시간순으로 정렬된 전체 대화를 반환 const conversation = data .map((item) => mapMisskeyStatusWithInstance(item, account.instanceUrl)) - .filter((status): status is Status => status !== null); + .filter((status): status is Status => status !== null) + .sort((a, b) => a.createdAt.localeCompare(b.createdAt)); // 전체 대화에서 현재 노트를 찾아서 ancestors/descendants로 분리 - const currentIndex = conversation.findIndex(status => status.id === noteId); - const ancestors = currentIndex > 0 ? conversation.slice(0, currentIndex) : []; - const descendants = currentIndex >= 0 && currentIndex < conversation.length - 1 - ? conversation.slice(currentIndex + 1) - : []; + const currentIndex = conversation.findIndex((status) => status.id === noteId); + const ancestors = + currentIndex === -1 ? conversation : currentIndex > 0 ? conversation.slice(0, currentIndex) : []; + let descendants: Status[] = []; + try { + descendants = await this.fetchDescendants(account, noteId, 200, 6); + } catch (error) { + console.error("후손 스레드 로딩 실패:", error); + } return { ancestors,