fix(live-room): 채팅창 얼림 버튼 위치와 안내 문구를 조정한다

This commit is contained in:
Yu Sung
2026-03-20 14:27:10 +09:00
parent 8eca5df62b
commit af31444f0f
8 changed files with 100 additions and 32 deletions

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "ic_ice.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -784,8 +784,9 @@ enum I18n {
static var participants: String { pick(ko: "참여자", en: "Participants", ja: "リスナー") }
static var follow: String { pick(ko: "팔로우", en: "Follow", ja: "フォロー") }
static var following: String { pick(ko: "팔로잉", en: "Following", ja: "フォロー中") }
static var chatFreezeOnStatusMessage: String { pick(ko: "채팅창을 얼렸습니다.", en: "Chat has been frozen.", ja: "チャット凍結されました。") }
static var chatFreezeOffStatusMessage: String { pick(ko: "채팅창 얼림이 해제되었습니다.", en: "Chat freeze has been lifted.", ja: "チャット凍結が解除されました。") }
static var chatFreezeOnStatusMessageForCreator: String { pick(ko: "“🧊 모두들 얼음!” 채팅창을 얼렸습니다.", en: "\"🧊 Freeze, everyone!\" The chat has been frozen.", ja: "「🧊 みんなフリーズ!」チャット凍結ました。") }
static var chatFreezeOnStatusMessageForListener: String { pick(ko: "“🧊 모두들 얼음!” 채팅창이 얼었습니다.", en: "\"🧊 Freeze, everyone!\" The chat is now frozen.", ja: "「🧊 みんなフリーズ!」チャット凍結されました。") }
static var chatFreezeOffStatusMessage: String { pick(ko: "“💧땡! “ 채팅창 얼리기가 해제되었습니다.", en: "\"💧 Ding!\" Chat freeze has been lifted.", ja: "「💧 たん!」チャット凍結が解除されました。") }
static var chatFreezeBlockedMessage: String { pick(ko: "채팅창이 얼려져 있어 채팅할 수 없습니다.", en: "You cannot chat while chat is frozen.", ja: "チャットが凍結中のため送信できません。") }
static var chatDeleteTitle: String { pick(ko: "채팅 삭제", en: "Delete chat", ja: "チャット削除") }
}

View File

@@ -2133,9 +2133,16 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
}
private func appendChatFreezeStatusMessage(isChatFrozen: Bool) {
let statusMessage = isChatFrozen
? I18n.LiveRoom.chatFreezeOnStatusMessage
: I18n.LiveRoom.chatFreezeOffStatusMessage
let statusMessage: String
if isChatFrozen {
statusMessage = isCreator
? I18n.LiveRoom.chatFreezeOnStatusMessageForCreator
: I18n.LiveRoom.chatFreezeOnStatusMessageForListener
} else {
statusMessage = I18n.LiveRoom.chatFreezeOffStatusMessage
}
messages.append(LiveRoomJoinChat(nickname: "", statusMessage: statusMessage))
}

View File

@@ -11,17 +11,20 @@ struct LiveRoomRightBottomButton: View {
let imageName: String
let onClick: () -> Void
let backgroundColor: Color?
let onLongPress: (() -> Void)?
let longPressDuration: Double
init(
imageName: String,
onClick: @escaping () -> Void,
backgroundColor: Color? = nil,
onLongPress: (() -> Void)? = nil,
longPressDuration: Double = 2.0
) {
self.imageName = imageName
self.onClick = onClick
self.backgroundColor = backgroundColor
self.onLongPress = onLongPress
self.longPressDuration = longPressDuration
}
@@ -31,7 +34,15 @@ struct LiveRoomRightBottomButton: View {
.resizable()
.frame(width: 24, height: 24)
.padding(10)
.background(Color.gray52.opacity(0.6))
.background(
backgroundColor ?? Color(
.sRGB,
red: 82 / 255,
green: 82 / 255,
blue: 82 / 255,
opacity: 0.6
)
)
.cornerRadius(10)
.onTapGesture { onClick() }
.onLongPressGesture(minimumDuration: longPressDuration) {

View File

@@ -6,7 +6,6 @@
//
import SwiftUI
import Kingfisher
struct LiveRoomInfoHostView: View {
@@ -19,7 +18,6 @@ struct LiveRoomInfoHostView: View {
let isOnNotice: Bool
let isOnMenuPan: Bool
let isOnSignature: Bool
let isOnChatFreeze: Bool
let isShowMenuPanButton: Bool
let creatorId: Int
@@ -41,7 +39,6 @@ struct LiveRoomInfoHostView: View {
let onClickTotalHeart: () -> Void
let onClickTotalDonation: () -> Void
let onClickParticipants: () -> Void
let onClickToggleChatFreeze: () -> Void
let onClickToggleSignature: () -> Void
var body: some View {
@@ -58,18 +55,6 @@ struct LiveRoomInfoHostView: View {
Spacer()
LiveRoomOverlayStrokeTextToggleButton(
isOn: isOnChatFreeze,
onText: I18n.LiveRoom.chatFreezeOn,
onTextColor: Color.button,
onStrokeColor: Color.button,
offText: I18n.LiveRoom.chatFreezeOff,
offTextColor: Color.graybb,
offStrokeColor: Color.graybb,
strokeWidth: 1,
strokeCornerRadius: 5.3
) { onClickToggleChatFreeze() }
LiveRoomOverlayStrokeTextToggleButton(
isOn: isOnSignature,
onText: I18n.LiveRoom.signatureOn,
@@ -254,7 +239,6 @@ struct LiveRoomInfoHostView_Previews: PreviewProvider {
isOnNotice: true,
isOnMenuPan: false,
isOnSignature: false,
isOnChatFreeze: false,
isShowMenuPanButton: false,
creatorId: 1,
creatorNickname: "도화",
@@ -286,7 +270,6 @@ struct LiveRoomInfoHostView_Previews: PreviewProvider {
onClickTotalHeart: {},
onClickTotalDonation: {},
onClickParticipants: {},
onClickToggleChatFreeze: {},
onClickToggleSignature: {}
)
}

View File

@@ -74,7 +74,6 @@ struct LiveRoomViewV2: View {
isOnNotice: viewModel.isShowNotice,
isOnMenuPan: viewModel.isShowMenuPan,
isOnSignature: viewModel.isSignatureOn,
isOnChatFreeze: viewModel.isChatFrozen,
isShowMenuPanButton: !liveRoomInfo.menuPan.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty,
creatorId: liveRoomInfo.creatorId,
creatorNickname: liveRoomInfo.creatorNickname,
@@ -115,9 +114,6 @@ struct LiveRoomViewV2: View {
onClickParticipants: {
viewModel.isShowProfileList = true
},
onClickToggleChatFreeze: {
viewModel.setChatFreeze(isChatFrozen: !viewModel.isChatFrozen)
},
onClickToggleSignature: {
viewModel.isSignatureOn.toggle()
}
@@ -249,10 +245,24 @@ struct LiveRoomViewV2: View {
VStack(alignment: .trailing, spacing: 0) {
Spacer()
LiveRoomRightBottomButton(
imageName: viewModel.isSpeakerMute ? "ic_speaker_off" : "ic_speaker_on",
onClick: { viewModel.toggleSpeakerMute() }
)
VStack(spacing: 13.3) {
if liveRoomInfo.creatorId == UserDefaults.int(forKey: .userId) {
LiveRoomRightBottomButton(
imageName: "ic_ice",
onClick: {
viewModel.setChatFreeze(isChatFrozen: !viewModel.isChatFrozen)
},
backgroundColor: viewModel.isChatFrozen
? Color(hex: "3bb9f1").opacity(0.5)
: nil
)
}
LiveRoomRightBottomButton(
imageName: viewModel.isSpeakerMute ? "ic_speaker_off" : "ic_speaker_on",
onClick: { viewModel.toggleSpeakerMute() }
)
}
.padding(.bottom, 40)
.padding(.trailing, 13.3)

View File

@@ -0,0 +1,35 @@
# 20260320 채팅창 얼림 버튼 및 문구 수정
## 작업 체크리스트
- [x] `LiveRoomViewV2`에서 방장 전용 얼림 버튼을 스피커 음소거 버튼 위에 배치한다.
- [x] 얼림 버튼 OFF/ON 상태별 배경 스타일을 요구사항(기본 배경 / `#3bb9f1` 50%, corner radius 10)로 반영한다.
- [x] 얼림 ON/OFF 시 채팅 문구를 방장/리스너 조건으로 각각 지정된 문구로 수정한다.
- [x] 수정 파일 진단 및 빌드를 실행하고 결과를 기록한다.
## 완료 기준 (Pass/Fail)
- [x] Pass: 방장 계정에서만 `ic_ice` 버튼이 보이고, 버튼이 스피커 음소거 버튼 바로 위에 위치한다. (QA: 화면 렌더링 코드 조건/배치 확인)
- [x] Pass: OFF 상태 배경은 우측 하단 기존 버튼과 동일하고, ON 상태는 `#3bb9f1` 50% + radius 10으로 적용된다. (QA: 버튼 스타일 코드 확인)
- [x] Pass: 얼림 ON/OFF 채팅 문구가 방장/리스너 조건에 맞게 정확히 분기된다. (QA: 얼림 메시지 생성 코드 확인)
- [x] Pass: 수정 파일 LSP 진단 에러 0, 빌드 명령 종료 코드 0. (QA: `lsp_diagnostics`, `xcodebuild`)
## 검증 기록
- 2026-03-20 (채팅창 얼림 버튼/문구 수정)
- 무엇/왜/어떻게: 얼림 토글을 상단 호스트 토글 영역에서 우측 하단 스피커 음소거 버튼 위로 이동하고, ON/OFF 배경 스펙 및 방장 전용 노출 조건을 반영했다. 동시에 얼림 상태 채팅 문구를 방장/리스너 역할 기준으로 분기되도록 `LiveRoomViewModel` + `I18n` 경로를 수정했다.
- 실행 명령/도구:
- `lsp_diagnostics`:
- `SodaLive/Sources/Live/Room/V2/Component/Button/LiveRoomRightBottomButton.swift`
- `SodaLive/Sources/Live/Room/V2/Component/View/LiveRoomInfoHostView.swift`
- `SodaLive/Sources/Live/Room/V2/LiveRoomViewV2.swift`
- `SodaLive/Sources/Live/Room/LiveRoomViewModel.swift`
- `SodaLive/Sources/I18n/I18n.swift`
- `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build`
- `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build`
- `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" test`
- `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" test`
- `python3` 코드 QA 스크립트(버튼 위치/호스트 노출/문구 분기 문자열 정합성 PASS 체크)
- 결과:
- `lsp_diagnostics` 대상 5개 파일 모두 `No diagnostics found` 확인.
- `SodaLive` Debug build: `** BUILD SUCCEEDED **`.
- `SodaLive-dev` Debug build: `** BUILD SUCCEEDED **`.
- 테스트: 두 스킴 모두 `Scheme ... is not currently configured for the test action`으로 자동 테스트 실행 불가.
- 코드 QA 스크립트: `host_only_ice_button`, `ice_above_speaker`, `host_header_toggle_removed`, `on_message_creator`, `on_message_listener`, `off_message_common`, `viewmodel_role_branch` 전 항목 `PASS`.