From 130b8eee40d041eea1738508986688c63ee9f95c Mon Sep 17 00:00:00 2001 From: Yu Sung Date: Sun, 5 Jul 2026 19:34:43 +0900 Subject: [PATCH] =?UTF-8?q?feat(creator):=20=ED=8C=AC=ED=86=A1=20=EB=8B=B5?= =?UTF-8?q?=EA=B8=80=20=EC=97=B0=EA=B2=B0=EC=84=A0=EC=9D=84=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CreatorChannelFanTalkListItem.swift | 38 +++++++++++++++---- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkListItem.swift b/SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkListItem.swift index 414b6a7d..8da86db8 100644 --- a/SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkListItem.swift +++ b/SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkListItem.swift @@ -18,6 +18,15 @@ struct CreatorChannelFanTalkListItem: View { .fixedSize(horizontal: false, vertical: true) .frame(maxWidth: .infinity, alignment: .leading) .padding(.leading, 50) + .background(alignment: .topLeading) { + if fanTalk.creatorReplies.isEmpty == false { + GeometryReader { proxy in + CreatorChannelFanTalkReplyConnector() + .stroke(Color.gray800, style: StrokeStyle(lineWidth: 2, lineCap: .round, lineJoin: .round)) + .frame(height: proxy.size.height + SodaSpacing.s12 + SodaSpacing.s20) + } + } + } if fanTalk.creatorReplies.isEmpty == false { replies @@ -84,13 +93,7 @@ struct CreatorChannelFanTalkListItem: View { private var replies: some View { VStack(alignment: .leading, spacing: SodaSpacing.s8) { ForEach(fanTalk.creatorReplies) { reply in - HStack(alignment: .top, spacing: SodaSpacing.s8) { - Rectangle() - .fill(Color.gray800) - .frame(width: 2) - - replyCard(reply) - } + replyCard(reply) } } } @@ -129,6 +132,27 @@ struct CreatorChannelFanTalkListItem: View { } } +private struct CreatorChannelFanTalkReplyConnector: Shape { + func path(in rect: CGRect) -> Path { + var path = Path() + let avatarCenterX: CGFloat = 21 + let replyCardStartX: CGFloat = 50 + let connectorGap: CGFloat = 14 + let connectorEndX = replyCardStartX - connectorGap + let startY: CGFloat = 0 + let endY = rect.height - 8 + let bendY = max(startY, endY - 14) + + path.move(to: CGPoint(x: avatarCenterX, y: startY)) + path.addLine(to: CGPoint(x: avatarCenterX, y: bendY)) + path.addQuadCurve( + to: CGPoint(x: connectorEndX, y: endY), + control: CGPoint(x: avatarCenterX, y: endY) + ) + return path + } +} + extension CreatorChannelFanTalkItemResponse { func relativeTimeText(now: Date = Date()) -> String { DateParser.relativeTimeText(fromUTC: createdAtUtc, fallback: createdAtUtc, now: now)