라이브룸 V2V 번역 자막 기능을 추가한다

라이브룸에서 진행자 언어와 기기 언어가 다를 때 자막 토글을 제공한다.
룸 정보 응답에 V2V 워커 토큰과 진행자 언어 코드를 포함한다.
Agora V2V 에이전트 참여와 종료 API 연동을 추가한다
This commit is contained in:
Yu Sung
2026-02-09 21:11:17 +09:00
parent 7f703024d8
commit b796f6d9c5
11 changed files with 816 additions and 2 deletions

View File

@@ -17,7 +17,9 @@ struct LiveRoomInfoGuestView: View {
let isOnNotice: Bool
let isOnMenuPan: Bool
let isOnSignature: Bool
let isOnV2VCaption: Bool
let isShowMenuPanButton: Bool
let isShowV2VCaptionButton: Bool
let creatorId: Int
let creatorNickname: String
@@ -37,6 +39,7 @@ struct LiveRoomInfoGuestView: View {
let onClickTotalHeart: () -> Void
let onClickTotalDonation: () -> Void
let onClickChangeListener: () -> Void
let onClickToggleV2VCaption: () -> Void
let onClickToggleSignature: () -> Void
var body: some View {
@@ -62,6 +65,20 @@ struct LiveRoomInfoGuestView: View {
strokeCornerRadius: 5.3
) { onClickChangeListener() }
}
if isShowV2VCaptionButton {
LiveRoomOverlayStrokeTextToggleButton(
isOn: isOnV2VCaption,
onText: I18n.LiveRoom.captionOn,
onTextColor: Color.button,
onStrokeColor: Color.button,
offText: I18n.LiveRoom.captionOff,
offTextColor: Color.graybb,
offStrokeColor: Color.graybb,
strokeWidth: 1,
strokeCornerRadius: 5.3
) { onClickToggleV2VCaption() }
}
LiveRoomOverlayStrokeTextToggleButton(
isOn: isOnSignature,
@@ -222,7 +239,9 @@ struct LiveRoomInfoGuestView_Previews: PreviewProvider {
isOnNotice: false,
isOnMenuPan: false,
isOnSignature: false,
isOnV2VCaption: false,
isShowMenuPanButton: false,
isShowV2VCaptionButton: true,
creatorId: 1,
creatorNickname: "도화",
creatorProfileUrl: "https://cf.sodalive.net/profile/26/26-profile-ddf78b4d-0300-4c50-9c84-5d8a95fd5fe2-4892-1705256364320",
@@ -258,6 +277,7 @@ struct LiveRoomInfoGuestView_Previews: PreviewProvider {
onClickTotalHeart: {},
onClickTotalDonation: {},
onClickChangeListener: {},
onClickToggleV2VCaption: {},
onClickToggleSignature: {}
)
}

View File

@@ -95,7 +95,9 @@ struct LiveRoomViewV2: View {
isOnNotice: viewModel.isShowNotice,
isOnMenuPan: viewModel.isShowMenuPan,
isOnSignature: viewModel.isSignatureOn,
isOnV2VCaption: viewModel.isV2VCaptionOn,
isShowMenuPanButton: !liveRoomInfo.menuPan.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty,
isShowV2VCaptionButton: viewModel.isV2VAvailable,
creatorId: liveRoomInfo.creatorId,
creatorNickname: liveRoomInfo.creatorNickname,
creatorProfileUrl: liveRoomInfo.creatorProfileUrl,
@@ -132,6 +134,9 @@ struct LiveRoomViewV2: View {
onClickChangeListener: {
viewModel.setListener()
},
onClickToggleV2VCaption: {
viewModel.toggleV2VCaption()
},
onClickToggleSignature: {
viewModel.isSignatureOn.toggle()
}
@@ -182,7 +187,7 @@ struct LiveRoomViewV2: View {
.onPreferenceChange(ScrollOffsetKey.self) {
viewModel.setOffset($0)
}
.padding(.bottom, 70)
.padding(.bottom, v2vCaptionBottomInset)
}
.padding(.top, 16)
@@ -308,6 +313,19 @@ struct LiveRoomViewV2: View {
}
.padding(.trailing, 13.3)
if isV2VCaptionVisible {
Text(viewModel.v2vCaptionText)
.appFont(size: 12, weight: .medium)
.foregroundColor(.white)
.lineLimit(2)
.padding(.horizontal, 12)
.padding(.vertical, 10)
.frame(maxWidth: .infinity, alignment: .center)
.background(Color.black.opacity(0.75))
.cornerRadius(10)
.padding(.horizontal, 13.3)
}
LiveRoomInputChatView {
viewModel.sendMessage(chatMessage: $0) {
viewModel.isShowingNewChat = false
@@ -316,6 +334,7 @@ struct LiveRoomViewV2: View {
return true
}
.padding(.top, isV2VCaptionVisible ? -13.3 : 0)
.padding(.bottom, 10)
}
@@ -323,7 +342,7 @@ struct LiveRoomViewV2: View {
LiveRoomNewChatView{
viewModel.isShowingNewChat = false
proxy.scrollTo(viewModel.messages.count - 1, anchor: .center)
}.padding(.bottom, 70)
}.padding(.bottom, v2vCaptionBottomInset)
}
if viewModel.isSignatureOn && viewModel.signatureImageUrl.trimmingCharacters(in: .whitespacesAndNewlines).count > 0 {
@@ -474,6 +493,7 @@ struct LiveRoomViewV2: View {
.onDisappear {
UIApplication.shared.isIdleTimerDisabled = false
NotificationCenter.default.removeObserver(self)
viewModel.stopV2VTranslationIfJoined()
viewModel.stopPeriodicPlaybackValidation()
}
@@ -744,6 +764,10 @@ struct LiveRoomViewV2: View {
if viewModel.isLoading && viewModel.liveRoomInfo == nil {
LoadingView()
}
if viewModel.isV2VLoading {
LoadingView()
}
}
.overlay(alignment: .center) {
ZStack {
@@ -916,6 +940,17 @@ struct LiveRoomViewV2: View {
}
}
private extension LiveRoomViewV2 {
var isV2VCaptionVisible: Bool {
viewModel.isV2VCaptionOn &&
!viewModel.v2vCaptionText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
}
var v2vCaptionBottomInset: CGFloat {
isV2VCaptionVisible ? 120 : 70
}
}
struct LiveRoomViewV2_Previews: PreviewProvider {
static var previews: some View {
LiveRoomViewV2()