fix(live-room): 채팅창 얼림 버튼 위치와 안내 문구를 조정한다
This commit is contained in:
21
SodaLive/Resources/Assets.xcassets/ic_ice.imageset/Contents.json
vendored
Normal file
21
SodaLive/Resources/Assets.xcassets/ic_ice.imageset/Contents.json
vendored
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
SodaLive/Resources/Assets.xcassets/ic_ice.imageset/ic_ice.png
vendored
Normal file
BIN
SodaLive/Resources/Assets.xcassets/ic_ice.imageset/ic_ice.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
@@ -784,8 +784,9 @@ enum I18n {
|
|||||||
static var participants: String { pick(ko: "참여자", en: "Participants", ja: "リスナー") }
|
static var participants: String { pick(ko: "참여자", en: "Participants", ja: "リスナー") }
|
||||||
static var follow: String { pick(ko: "팔로우", en: "Follow", ja: "フォロー") }
|
static var follow: String { pick(ko: "팔로우", en: "Follow", ja: "フォロー") }
|
||||||
static var following: String { pick(ko: "팔로잉", en: "Following", ja: "フォロー中") }
|
static var following: String { pick(ko: "팔로잉", en: "Following", ja: "フォロー中") }
|
||||||
static var chatFreezeOnStatusMessage: String { pick(ko: "채팅창을 얼렸습니다.", en: "Chat has been frozen.", ja: "チャットが凍結されました。") }
|
static var chatFreezeOnStatusMessageForCreator: String { pick(ko: "“🧊 모두들 얼음!” 채팅창을 얼렸습니다.", en: "\"🧊 Freeze, everyone!\" The chat has been frozen.", ja: "「🧊 みんなフリーズ!」チャットを凍結しました。") }
|
||||||
static var chatFreezeOffStatusMessage: String { pick(ko: "채팅창 얼림이 해제되었습니다.", en: "Chat freeze has been lifted.", 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 chatFreezeBlockedMessage: String { pick(ko: "채팅창이 얼려져 있어 채팅할 수 없습니다.", en: "You cannot chat while chat is frozen.", ja: "チャットが凍結中のため送信できません。") }
|
||||||
static var chatDeleteTitle: String { pick(ko: "채팅 삭제", en: "Delete chat", ja: "チャット削除") }
|
static var chatDeleteTitle: String { pick(ko: "채팅 삭제", en: "Delete chat", ja: "チャット削除") }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2133,9 +2133,16 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func appendChatFreezeStatusMessage(isChatFrozen: Bool) {
|
private func appendChatFreezeStatusMessage(isChatFrozen: Bool) {
|
||||||
let statusMessage = isChatFrozen
|
let statusMessage: String
|
||||||
? I18n.LiveRoom.chatFreezeOnStatusMessage
|
|
||||||
: I18n.LiveRoom.chatFreezeOffStatusMessage
|
if isChatFrozen {
|
||||||
|
statusMessage = isCreator
|
||||||
|
? I18n.LiveRoom.chatFreezeOnStatusMessageForCreator
|
||||||
|
: I18n.LiveRoom.chatFreezeOnStatusMessageForListener
|
||||||
|
} else {
|
||||||
|
statusMessage = I18n.LiveRoom.chatFreezeOffStatusMessage
|
||||||
|
}
|
||||||
|
|
||||||
messages.append(LiveRoomJoinChat(nickname: "", statusMessage: statusMessage))
|
messages.append(LiveRoomJoinChat(nickname: "", statusMessage: statusMessage))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,17 +11,20 @@ struct LiveRoomRightBottomButton: View {
|
|||||||
|
|
||||||
let imageName: String
|
let imageName: String
|
||||||
let onClick: () -> Void
|
let onClick: () -> Void
|
||||||
|
let backgroundColor: Color?
|
||||||
let onLongPress: (() -> Void)?
|
let onLongPress: (() -> Void)?
|
||||||
let longPressDuration: Double
|
let longPressDuration: Double
|
||||||
|
|
||||||
init(
|
init(
|
||||||
imageName: String,
|
imageName: String,
|
||||||
onClick: @escaping () -> Void,
|
onClick: @escaping () -> Void,
|
||||||
|
backgroundColor: Color? = nil,
|
||||||
onLongPress: (() -> Void)? = nil,
|
onLongPress: (() -> Void)? = nil,
|
||||||
longPressDuration: Double = 2.0
|
longPressDuration: Double = 2.0
|
||||||
) {
|
) {
|
||||||
self.imageName = imageName
|
self.imageName = imageName
|
||||||
self.onClick = onClick
|
self.onClick = onClick
|
||||||
|
self.backgroundColor = backgroundColor
|
||||||
self.onLongPress = onLongPress
|
self.onLongPress = onLongPress
|
||||||
self.longPressDuration = longPressDuration
|
self.longPressDuration = longPressDuration
|
||||||
}
|
}
|
||||||
@@ -31,7 +34,15 @@ struct LiveRoomRightBottomButton: View {
|
|||||||
.resizable()
|
.resizable()
|
||||||
.frame(width: 24, height: 24)
|
.frame(width: 24, height: 24)
|
||||||
.padding(10)
|
.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)
|
.cornerRadius(10)
|
||||||
.onTapGesture { onClick() }
|
.onTapGesture { onClick() }
|
||||||
.onLongPressGesture(minimumDuration: longPressDuration) {
|
.onLongPressGesture(minimumDuration: longPressDuration) {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import Kingfisher
|
|
||||||
|
|
||||||
struct LiveRoomInfoHostView: View {
|
struct LiveRoomInfoHostView: View {
|
||||||
|
|
||||||
@@ -19,7 +18,6 @@ struct LiveRoomInfoHostView: View {
|
|||||||
let isOnNotice: Bool
|
let isOnNotice: Bool
|
||||||
let isOnMenuPan: Bool
|
let isOnMenuPan: Bool
|
||||||
let isOnSignature: Bool
|
let isOnSignature: Bool
|
||||||
let isOnChatFreeze: Bool
|
|
||||||
let isShowMenuPanButton: Bool
|
let isShowMenuPanButton: Bool
|
||||||
|
|
||||||
let creatorId: Int
|
let creatorId: Int
|
||||||
@@ -41,7 +39,6 @@ struct LiveRoomInfoHostView: View {
|
|||||||
let onClickTotalHeart: () -> Void
|
let onClickTotalHeart: () -> Void
|
||||||
let onClickTotalDonation: () -> Void
|
let onClickTotalDonation: () -> Void
|
||||||
let onClickParticipants: () -> Void
|
let onClickParticipants: () -> Void
|
||||||
let onClickToggleChatFreeze: () -> Void
|
|
||||||
let onClickToggleSignature: () -> Void
|
let onClickToggleSignature: () -> Void
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
@@ -58,18 +55,6 @@ struct LiveRoomInfoHostView: View {
|
|||||||
|
|
||||||
Spacer()
|
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(
|
LiveRoomOverlayStrokeTextToggleButton(
|
||||||
isOn: isOnSignature,
|
isOn: isOnSignature,
|
||||||
onText: I18n.LiveRoom.signatureOn,
|
onText: I18n.LiveRoom.signatureOn,
|
||||||
@@ -254,7 +239,6 @@ struct LiveRoomInfoHostView_Previews: PreviewProvider {
|
|||||||
isOnNotice: true,
|
isOnNotice: true,
|
||||||
isOnMenuPan: false,
|
isOnMenuPan: false,
|
||||||
isOnSignature: false,
|
isOnSignature: false,
|
||||||
isOnChatFreeze: false,
|
|
||||||
isShowMenuPanButton: false,
|
isShowMenuPanButton: false,
|
||||||
creatorId: 1,
|
creatorId: 1,
|
||||||
creatorNickname: "도화",
|
creatorNickname: "도화",
|
||||||
@@ -286,7 +270,6 @@ struct LiveRoomInfoHostView_Previews: PreviewProvider {
|
|||||||
onClickTotalHeart: {},
|
onClickTotalHeart: {},
|
||||||
onClickTotalDonation: {},
|
onClickTotalDonation: {},
|
||||||
onClickParticipants: {},
|
onClickParticipants: {},
|
||||||
onClickToggleChatFreeze: {},
|
|
||||||
onClickToggleSignature: {}
|
onClickToggleSignature: {}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,7 +74,6 @@ struct LiveRoomViewV2: View {
|
|||||||
isOnNotice: viewModel.isShowNotice,
|
isOnNotice: viewModel.isShowNotice,
|
||||||
isOnMenuPan: viewModel.isShowMenuPan,
|
isOnMenuPan: viewModel.isShowMenuPan,
|
||||||
isOnSignature: viewModel.isSignatureOn,
|
isOnSignature: viewModel.isSignatureOn,
|
||||||
isOnChatFreeze: viewModel.isChatFrozen,
|
|
||||||
isShowMenuPanButton: !liveRoomInfo.menuPan.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty,
|
isShowMenuPanButton: !liveRoomInfo.menuPan.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty,
|
||||||
creatorId: liveRoomInfo.creatorId,
|
creatorId: liveRoomInfo.creatorId,
|
||||||
creatorNickname: liveRoomInfo.creatorNickname,
|
creatorNickname: liveRoomInfo.creatorNickname,
|
||||||
@@ -115,9 +114,6 @@ struct LiveRoomViewV2: View {
|
|||||||
onClickParticipants: {
|
onClickParticipants: {
|
||||||
viewModel.isShowProfileList = true
|
viewModel.isShowProfileList = true
|
||||||
},
|
},
|
||||||
onClickToggleChatFreeze: {
|
|
||||||
viewModel.setChatFreeze(isChatFrozen: !viewModel.isChatFrozen)
|
|
||||||
},
|
|
||||||
onClickToggleSignature: {
|
onClickToggleSignature: {
|
||||||
viewModel.isSignatureOn.toggle()
|
viewModel.isSignatureOn.toggle()
|
||||||
}
|
}
|
||||||
@@ -248,11 +244,25 @@ struct LiveRoomViewV2: View {
|
|||||||
|
|
||||||
VStack(alignment: .trailing, spacing: 0) {
|
VStack(alignment: .trailing, spacing: 0) {
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
LiveRoomRightBottomButton(
|
VStack(spacing: 13.3) {
|
||||||
imageName: viewModel.isSpeakerMute ? "ic_speaker_off" : "ic_speaker_on",
|
if liveRoomInfo.creatorId == UserDefaults.int(forKey: .userId) {
|
||||||
onClick: { viewModel.toggleSpeakerMute() }
|
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(.bottom, 40)
|
||||||
.padding(.trailing, 13.3)
|
.padding(.trailing, 13.3)
|
||||||
|
|
||||||
|
|||||||
35
docs/20260320_채팅창얼림버튼및문구수정.md
Normal file
35
docs/20260320_채팅창얼림버튼및문구수정.md
Normal 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`.
|
||||||
Reference in New Issue
Block a user