From b74ec15de47b1135fb7772455a3b1453b7cc44d0 Mon Sep 17 00:00:00 2001 From: Yu Sung Date: Thu, 4 Sep 2025 00:10:12 +0900 Subject: [PATCH] =?UTF-8?q?feat(chat-room)=20=ED=83=80=EC=9D=B4=ED=95=91?= =?UTF-8?q?=20=EC=A4=91=EC=9D=B8=20=EA=B2=83=EC=9D=84=20=EC=95=8C=EB=A0=A4?= =?UTF-8?q?=EC=A3=BC=EB=8A=94=20=EC=95=A0=EB=8B=88=EB=A9=94=EC=9D=B4?= =?UTF-8?q?=EC=85=98=20=EC=95=84=EC=9D=B4=ED=85=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Message/TypingIndicatorItemView.swift | 70 ++++++++++++++++++- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/SodaLive/Sources/Chat/Talk/Room/Message/TypingIndicatorItemView.swift b/SodaLive/Sources/Chat/Talk/Room/Message/TypingIndicatorItemView.swift index 621ae8e..21e54cd 100644 --- a/SodaLive/Sources/Chat/Talk/Room/Message/TypingIndicatorItemView.swift +++ b/SodaLive/Sources/Chat/Talk/Room/Message/TypingIndicatorItemView.swift @@ -6,13 +6,79 @@ // import SwiftUI +import Kingfisher struct TypingIndicatorItemView: View { + var dotCount: Int = 3 + var size: CGFloat = 6 + var spacing: CGFloat = 6 + var color: Color = .secondary + var period: Double = 1.2 // 초 + + let characterName: String + let characterProfileUrl: String + var body: some View { - Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + HStack(alignment: .top, spacing: 9) { + KFImage(URL(string: characterProfileUrl)) + .placeholder { + Image(systemName: "person.crop.circle") + .resizable() + .scaledToFit() + } + .resizable() + .frame(width: 30, height: 30) + .clipShape(Circle()) + + VStack(alignment: .leading, spacing: 4) { + HStack(spacing: 4) { + Text(characterName) + .font(.custom(Font.preRegular.rawValue, size: 12)) + .foregroundColor(.white) + } + + HStack(spacing: 10) { + TimelineView(.animation) { context in + let t = context.date.timeIntervalSinceReferenceDate + HStack(spacing: spacing) { + ForEach(0.. Double { + // 0...1 구간에서 부드럽게 오르내리는 값(사인파 기반) + let base = (t.truncatingRemainder(dividingBy: period)) / period + let phase = base + Double(index) / Double(max(1, dotCount)) + let wave = 0.5 + 0.5 * sin(2 * .pi * phase) + // 최소/최대 투명도 범위 + return 0.25 + 0.75 * wave } } #Preview { - TypingIndicatorItemView() + TypingIndicatorItemView( + characterName: "보라", + characterProfileUrl: "https://picsum.photos/1000" + ) + .padding() + .background(Color.black) }