Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@ public final class ConnectionRepositoryImpl: ConnectionRepository {

public var clients: [ConnectionClient]

private let didEnterNewUserSubject = PassthroughSubject<(UserInfo, UIView), Never>()
public var didEnterNewUserPublisher: AnyPublisher<(UserInfo, UIView), Never> {
didEnterNewUserSubject.eraseToAnyPublisher()
}
private let _localVideoView = CapturableVideoView()
private var localUserInfo: UserInfo?
public private(set) var localUserInfo: UserInfo?

public var localVideoView: UIView { _localVideoView }
public var capturedLocalVideo: UIImage? { _localVideoView.capturedImage }
Expand Down Expand Up @@ -225,7 +229,10 @@ extension ConnectionRepositoryImpl {
}, receiveValue: { [weak self] entity in
guard let self else { return }
let newUser = entity.newUser
let emptyClient = clients.first(where: { $0.remoteUserInfo == nil })
guard let emptyClient = clients.first(where: { $0.remoteUserInfo == nil }) else {
PTGDataLogger.log("방이 가득 찼는데 누군가 입장했어요!")
return
}

guard let viewPosition = UserInfo.ViewPosition(rawValue: newUser.initialPosition),
let roomID = self.localUserInfo?.roomID
Expand All @@ -238,8 +245,9 @@ extension ConnectionRepositoryImpl {
viewPosition: viewPosition,
roomID: roomID
)

emptyClient?.setRemoteUserInfo(newUserInfoEntity)

emptyClient.setRemoteUserInfo(newUserInfoEntity)
didEnterNewUserSubject.send((newUserInfoEntity, emptyClient.remoteVideoView))
PTGDataLogger.log("newUser Entered: \(newUserInfoEntity)")
})
.store(in: &cancellables)
Expand Down Expand Up @@ -274,6 +282,7 @@ extension ConnectionRepositoryImpl {
private func setLocalUserInfo(entity: RoomOwnerEntity) -> RoomOwnerEntity {
guard let localUserInfo = localUserInfo(for: entity) else { return entity }
self.localUserInfo = localUserInfo
self.didEnterNewUserSubject.send((localUserInfo, localVideoView))
return entity
}

Expand Down Expand Up @@ -305,7 +314,7 @@ extension ConnectionRepositoryImpl {
private func localUserInfo(for entity: RoomOwnerEntity) -> UserInfo? {
return UserInfo(
id: entity.hostID,
nickname: "내가 호스트다",
nickname: String(entity.hostID.suffix(4)), // TODO: 서버에서 받아온 닉네임으로 변경 예정
isHost: true,
viewPosition: .topLeading,
roomID: entity.roomID
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import UIKit
import Combine
import PhotoGetherDomainInterface

public final class DidEnterNewUserPublisherUseCaseImpl: DidEnterNewUserPublisherUseCase {
public func publisher() -> AnyPublisher<(UserInfo, UIView), Never> {
return connectionRepository.didEnterNewUserPublisher
}
private let connectionRepository: ConnectionRepository

public init(connectionRepository: ConnectionRepository) {
self.connectionRepository = connectionRepository
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ import UIKit
import PhotoGetherDomainInterface

public final class GetLocalVideoUseCaseImpl: GetLocalVideoUseCase {
public func execute() -> UIView {
// TODO: 리포지토리에서 하나의 localVideoView만 들고 있도록 변경 예정
connectionRepository.localVideoView
public func execute() -> (UserInfo?, UIView) {
return (connectionRepository.localUserInfo, connectionRepository.localVideoView)
}

private let connectionRepository: ConnectionRepository
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,8 @@ import UIKit
import PhotoGetherDomainInterface

public final class GetRemoteVideoUseCaseImpl: GetRemoteVideoUseCase {
public func execute() -> [UIView] {
let sortedClients = connectionRepository.clients
.sorted { lhs, rhs in
let lhsPosition = lhs.remoteUserInfo?.viewPosition.rawValue ?? Int.max
let rhsPosition = rhs.remoteUserInfo?.viewPosition.rawValue ?? Int.max
return lhsPosition < rhsPosition
}
.map { $0.remoteVideoView }

return sortedClients
public func execute() -> [(UserInfo?, UIView)] {
return connectionRepository.clients.map { ($0.remoteUserInfo, $0.remoteVideoView) }
}

private let connectionRepository: ConnectionRepository
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ public struct UserInfo: Identifiable {

public enum ViewPosition: Int {
case topLeading
case bottomTrailing
case topTrailing
case bottomLeading
case bottomTrailing
}

public init(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import UIKit
import Combine

public protocol ConnectionRepository {
var didEnterNewUserPublisher: AnyPublisher<(UserInfo, UIView), Never> { get }

var localUserInfo: UserInfo? { get }

var clients: [ConnectionClient] { get }
var localVideoView: UIView { get }
var capturedLocalVideo: UIImage? { get }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import UIKit
import Combine

public protocol DidEnterNewUserPublisherUseCase {
func publisher() -> AnyPublisher<(UserInfo, UIView), Never>
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ import Foundation
import UIKit

public protocol GetLocalVideoUseCase {
func execute() -> UIView
func execute() -> (UserInfo?, UIView)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ import Foundation
import UIKit

public protocol GetRemoteVideoUseCase {
func execute() -> [UIView]
func execute() -> [(UserInfo?, UIView)]
}
7 changes: 6 additions & 1 deletion PhotoGether/PhotoGether/App/SceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
connectionRepository: connectionRepository
)

let didEnterNewUserPublisherUseCase: DidEnterNewUserPublisherUseCase = DidEnterNewUserPublisherUseCaseImpl(
connectionRepository: connectionRepository
)

let photoRoomViewModel: PhotoRoomViewModel = PhotoRoomViewModel(
captureVideosUseCase: captureVideosUseCase
)
Expand All @@ -97,7 +101,8 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
sendOfferUseCase: sendOfferUseCase,
getLocalVideoUseCase: getLocalVideoUseCase,
getRemoteVideoUseCase: getRemoteVideoUseCase,
createRoomUseCase: createRoomUseCase
createRoomUseCase: createRoomUseCase,
didEnterNewUserPublisherUseCase: didEnterNewUserPublisherUseCase
)

let waitingRoomViewController: WaitingRoomViewController = WaitingRoomViewController(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,17 @@ import UIKit
import DesignSystem

public extension UIViewController {
private struct ToastState {
static var presentedToast: UIView?
}

func showToast(message: String, duration: TimeInterval = 2.0) {
// 이미 토스트가 있다면 제거
if let existingToast = ToastState.presentedToast {
existingToast.removeFromSuperview()
ToastState.presentedToast = nil
}

let padding = UIEdgeInsets(top: 8, left: 20, bottom: 8, right: 20)
let toastLabel: PTGPaddingLabel = {
let lbl = PTGPaddingLabel(padding: padding)
Expand All @@ -23,6 +33,7 @@ public extension UIViewController {
}()

view.addSubview(toastLabel)
ToastState.presentedToast = toastLabel

let maxWidth: CGFloat = view.frame.width > 0 ? view.frame.width * 0.7 : 300

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
import UIKit
import SnapKit

public enum ParticipantPosition {
public enum ParticipantPosition: Int {
case topLeading
case bottomTrailing
case topTrailing
case bottomLeading
case bottomTrailing

public init?(rawValue: Int) {
switch rawValue {
case 0: self = .topLeading
case 1: self = .bottomTrailing
case 2: self = .topTrailing
case 3: self = .bottomLeading
default: return nil
}
}
}

public final class PTGParticipantsGridView: UIView {
Expand Down Expand Up @@ -105,6 +115,3 @@ extension PTGParticipantsGridView {
static let itemSpacing: CGFloat = 11
}
}



Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,7 @@ private extension WaitingRoomView {

func configureUI() {
self.backgroundColor = PTGColor.gray90.color

startButton.setTitle(to: StartButtonTitle.one.rawValue)
startButton.setTitle(to: "촬영시작")
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ public final class EnterLoadingViewController: BaseViewController, ViewControlle
label.snp.makeConstraints {
$0.centerX.equalToSuperview()
$0.top.equalTo(activityIndicator.snp.bottom).offset(10)
$0.horizontalEdges.equalToSuperview().inset(20)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,33 +65,25 @@ public final class WaitingRoomViewController: BaseViewController {
private func bindOutput() {
let output = viewModel.transform(input: input.eraseToAnyPublisher())

output.sink { [weak self] in
output
.receive(on: RunLoop.main)
.sink { [weak self] in
guard let self else { return }
switch $0 {
// MARK: 네비게이션 처리
case .navigateToPhotoRoom:
self.navigateToPhotoRoom()

// MARK: 내 비디오 화면 업데이트
case .localVideo(let localVideoView):
print(localVideoView)
updateParticipantView(view: localVideoView, position: .topLeading)
updateParticipantNickname(nickname: "나는 호스트야", position: .topLeading)

// MARK: 상대방 비디오 화면 업데이트
case .remoteVideos(let remoteVideoViews):
let guestVideoView1 = remoteVideoViews[safe: 0] ?? UIView()
let guestVideoView2 = remoteVideoViews[safe: 1] ?? UIView()
let guestVideoView3 = remoteVideoViews[safe: 2] ?? UIView()

updateParticipantView(view: guestVideoView1, position: .bottomTrailing)
updateParticipantView(view: guestVideoView2, position: .topTrailing)
updateParticipantView(view: guestVideoView3, position: .bottomLeading)

updateParticipantNickname(nickname: "나는 게스트1", position: .bottomTrailing)
updateParticipantNickname(nickname: "나는 게스트2", position: .topTrailing)
updateParticipantNickname(nickname: "나는 게스트3", position: .bottomLeading)

// MARK: 화면 업데이트
case let .shouldUpdateVideoView(videoView, viewPosition):
guard let participantPosition = ParticipantPosition(rawValue: viewPosition) else { return }
updateParticipantView(view: videoView, position: participantPosition)

// MARK: 닉네임 업데이트
case let .shouldUpdateNickname(nickname, viewPosition):
guard let participantPosition = ParticipantPosition(rawValue: viewPosition) else { return }
updateParticipantNickname(nickname: nickname, position: participantPosition)

// MARK: 마이크 음소거 UI 업데이트
case .micMuteState:
return
Expand Down
Loading