feat(live): 라이브룸 게스트 상단에 팔로우 버튼과 알림 옵션을 추가한다

This commit is contained in:
Yu Sung
2026-03-05 10:55:55 +09:00
parent f0763d75c2
commit ca565a2b5f
4 changed files with 84 additions and 2 deletions

View File

@@ -1278,7 +1278,12 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
.store(in: &subscription)
}
func creatorFollow(creatorId: Int? = nil, isGetUserProfile: Bool = false) {
func creatorFollow(
creatorId: Int? = nil,
follow: Bool = true,
notify: Bool = true,
isGetUserProfile: Bool = false
) {
var userId = 0
if let creatorId = creatorId {
@@ -1290,7 +1295,7 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
if userId > 0 {
isLoading = true
userRepository.creatorFollow(creatorId: userId)
userRepository.creatorFollow(creatorId: userId, follow: follow, notify: notify)
.sink { result in
switch result {
case .finished:

View File

@@ -24,6 +24,7 @@ struct LiveRoomInfoGuestView: View {
let creatorId: Int
let creatorNickname: String
let creatorProfileUrl: String
let followButtonType: FollowButtonImageType
let speakerList: [LiveRoomMember]
let muteSpeakerList: [UInt]
let activeSpeakerList: [UInt]
@@ -38,6 +39,7 @@ struct LiveRoomInfoGuestView: View {
let onClickMenuPan: () -> Void
let onClickTotalHeart: () -> Void
let onClickTotalDonation: () -> Void
let onClickFollow: () -> Void
let onClickChangeListener: () -> Void
let onClickToggleV2VCaption: () -> Void
let onClickToggleSignature: () -> Void
@@ -210,6 +212,13 @@ struct LiveRoomInfoGuestView: View {
.stroke(Color.graybb, lineWidth: 1)
)
.onTapGesture { onClickTotalDonation() }
if creatorId != UserDefaults.int(forKey: .userId) {
let asset = FollowButtonImageAsset(type: followButtonType)
asset.imageView(defaultSize: CGSize(width: 83.3, height: 26.7))
.contentShape(Rectangle())
.onTapGesture { onClickFollow() }
}
}
}
@@ -245,6 +254,7 @@ struct LiveRoomInfoGuestView_Previews: PreviewProvider {
creatorId: 1,
creatorNickname: "도화",
creatorProfileUrl: "https://cf.sodalive.net/profile/26/26-profile-ddf78b4d-0300-4c50-9c84-5d8a95fd5fe2-4892-1705256364320",
followButtonType: .follow,
speakerList: [
LiveRoomMember(
id: 1,
@@ -276,6 +286,7 @@ struct LiveRoomInfoGuestView_Previews: PreviewProvider {
onClickMenuPan: {},
onClickTotalHeart: {},
onClickTotalDonation: {},
onClickFollow: {},
onClickChangeListener: {},
onClickToggleV2VCaption: {},
onClickToggleSignature: {}

View File

@@ -24,6 +24,8 @@ struct LiveRoomViewV2: View {
@State private var showWaterHeart: Bool = false
@State private var waterProgress: CGFloat = 0
@State private var wavePhase: CGFloat = 0
@State private var isShowFollowNotifyDialog: Bool = false
@State private var guestFollowButtonTypeOverride: FollowButtonImageType? = nil
let heartWaveTimer = Timer.publish(every: 1/60, on: .main, in: .common).autoconnect()
var body: some View {
@@ -101,6 +103,7 @@ struct LiveRoomViewV2: View {
creatorId: liveRoomInfo.creatorId,
creatorNickname: liveRoomInfo.creatorNickname,
creatorProfileUrl: liveRoomInfo.creatorProfileUrl,
followButtonType: guestFollowButtonType(liveRoomInfo: liveRoomInfo),
speakerList: liveRoomInfo.speakerList,
muteSpeakerList: viewModel.muteSpeakers,
activeSpeakerList: viewModel.activeSpeakers,
@@ -131,6 +134,16 @@ struct LiveRoomViewV2: View {
onClickTotalDonation: {
viewModel.isShowDonationRankingPopup = true
},
onClickFollow: {
let buttonType = guestFollowButtonType(liveRoomInfo: liveRoomInfo)
if buttonType == .follow {
guestFollowButtonTypeOverride = .following
viewModel.creatorFollow(follow: true, notify: true)
} else {
isShowFollowNotifyDialog = true
}
},
onClickChangeListener: {
viewModel.setListener()
},
@@ -735,6 +748,26 @@ struct LiveRoomViewV2: View {
}
}
}
if isShowFollowNotifyDialog,
let liveRoomInfo = viewModel.liveRoomInfo,
liveRoomInfo.creatorId != UserDefaults.int(forKey: .userId) {
CreatorFollowNotifyDialog(
isShowing: $isShowFollowNotifyDialog,
onClickNotifyAll: {
guestFollowButtonTypeOverride = .following
viewModel.creatorFollow(follow: true, notify: true)
},
onClickNotifyNone: {
guestFollowButtonTypeOverride = .followingNoAlarm
viewModel.creatorFollow(follow: true, notify: false)
},
onClickUnFollow: {
guestFollowButtonTypeOverride = .follow
viewModel.creatorFollow(follow: false, notify: false)
}
)
}
}
if viewModel.isShowRouletteSettings {
@@ -890,6 +923,11 @@ struct LiveRoomViewV2: View {
.sheet(isPresented: $viewModel.isShowDonationMessagePopup) {
LiveRoomDonationMessageDialog(viewModel: viewModel, isShowing: $viewModel.isShowDonationMessagePopup)
}
.onChange(of: viewModel.liveRoomInfo?.isFollowing) { isFollowing in
if isFollowing == false {
guestFollowButtonTypeOverride = nil
}
}
}
private func estimatedHeight(for text: String, width: CGFloat) -> CGFloat {
@@ -941,6 +979,14 @@ struct LiveRoomViewV2: View {
}
private extension LiveRoomViewV2 {
func guestFollowButtonType(liveRoomInfo: GetRoomInfoResponse) -> FollowButtonImageType {
if liveRoomInfo.isFollowing {
return guestFollowButtonTypeOverride ?? .following
}
return .follow
}
var isV2VCaptionVisible: Bool {
viewModel.isV2VCaptionOn &&
!viewModel.v2vCaptionText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty