Skip to content

Commit

Permalink
[UI/UX]Fix discrepancies between Screensharing/Spotlight
Browse files Browse the repository at this point in the history
Pull Request: #209

# Conflicts:
#	StreamVideo.xcodeproj/project.pbxproj
  • Loading branch information
ipavlidakis committed Oct 20, 2023
1 parent 22c5425 commit 2fabfdd
Show file tree
Hide file tree
Showing 22 changed files with 451 additions and 134 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
//
// Copyright © 2023 Stream.io Inc. All rights reserved.
//

import StreamVideo
import SwiftUI

/// `BottomParticipantsBarLayoutComponent` represents a horizontally scrollable view of participant thumbnails.
/// This component lays out participant thumbnails in a bar at the bottom of the associated view.
public struct BottomParticipantsBarLayoutComponent<Factory: ViewFactory>: View {

// MARK: - Properties

/// Factory for creating views.
public var viewFactory: Factory

/// List of participants to display.
public var participants: [CallParticipant]

/// Frame in which the component will be laid out.
public var frame: CGRect

/// Information about the call (if available).
public var call: Call?

/// Size of each participant thumbnail.
public var thumbnailSize: CGFloat

/// Flag to determine if all participant information should be shown.
public var showAllInfo: Bool

/// Closure called to change visibility of a participant's track.
public var onChangeTrackVisibility: @MainActor(CallParticipant, Bool) -> Void

// Private computed properties for laying out the view.
private let barFrame: CGRect
private let itemFrame: CGRect

// MARK: - Initialization

/// Creates a new instance of `BottomParticipantsBarLayoutComponent`.
public init(
viewFactory: Factory,
participants: [CallParticipant],
frame: CGRect,
call: Call?,
thumbnailSize: CGFloat = 120,
showAllInfo: Bool = false,
onChangeTrackVisibility: @escaping (CallParticipant, Bool) -> Void
) {
self.viewFactory = viewFactory
self.participants = participants
self.frame = frame
self.call = call
self.thumbnailSize = thumbnailSize
self.showAllInfo = showAllInfo
self.onChangeTrackVisibility = onChangeTrackVisibility

// Calculate the frame for the bar at the bottom.
self.barFrame = .init(
origin: .init(x: frame.origin.x, y: frame.maxY - thumbnailSize),
size: CGSize(width: frame.size.width, height: thumbnailSize)
)

// Calculate the frame for each item in the bar.
self.itemFrame = .init(
origin: .zero,
size: .init(
width: barFrame.height,
height: barFrame.height
)
)
}

// MARK: - View Body

/// Defines the structure and layout of the view.
public var body: some View {
// Scroll view to accommodate multiple participant thumbnails.
ScrollView(.horizontal) {
// Container for horizontal alignment.
HorizontalContainer {
// Loop through each participant and display their thumbnail.
ForEach(participants) { participant in
viewFactory.makeVideoParticipantView(
participant: participant,
id: participant.id,
availableFrame: itemFrame,
contentMode: .scaleAspectFill,
customData: [:],
call: call
)
.modifier(
viewFactory.makeVideoCallParticipantModifier(
participant: participant,
call: call,
availableFrame: itemFrame,
ratio: itemFrame.width / itemFrame.height,
showAllInfo: showAllInfo
)
)
// Observe visibility changes.
.visibilityObservation(in: barFrame) { onChangeTrackVisibility(participant, $0) }
.cornerRadius(8)
.accessibility(identifier: "bottomParticipantsBarParticipipant")
}
}
.frame(height: barFrame.height)
.cornerRadius(8)
}
.padding()
.accessibility(identifier: "bottomParticipantsBar")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
//
// Copyright © 2023 Stream.io Inc. All rights reserved.
//

import StreamVideo
import SwiftUI

/// `DominantSpeakerLayoutComponent` represents a view for the dominant speaker's thumbnail.
public struct DominantSpeakerLayoutComponent<Factory: ViewFactory>: View {

// MARK: - Properties

/// Factory for creating views.
public var viewFactory: Factory

/// The dominant speaker participant whose thumbnail will be displayed.
public var participant: CallParticipant

/// Suffix for constructing the view ID.
public var viewIdSuffix: String

/// Information about the call (if available).
public var call: Call?

/// Frame in which the dominant speaker's thumbnail will be displayed.
public var availableFrame: CGRect

/// Closure called to change visibility of the dominant speaker's track.
public var onChangeTrackVisibility: (CallParticipant, Bool) -> Void

/// Computed property to generate a unique view ID for the dominant speaker.
var viewId: String { participant.id + (!viewIdSuffix.isEmpty ? "-\(viewIdSuffix)" : "") }

// MARK: - Initialization

/// Creates a new instance of `DominantSpeakerLayoutComponent`.
public init(
viewFactory: Factory,
participant: CallParticipant,
viewIdSuffix: String,
call: Call?,
availableFrame: CGRect,
onChangeTrackVisibility: @escaping (CallParticipant, Bool) -> Void
) {
self.viewFactory = viewFactory
self.participant = participant
self.viewIdSuffix = viewIdSuffix
self.call = call
self.availableFrame = availableFrame
self.onChangeTrackVisibility = onChangeTrackVisibility
}

// MARK: - View Body

/// Defines the structure and layout of the view.
public var body: some View {
// Creates the video view for the dominant speaker.
viewFactory.makeVideoParticipantView(
participant: participant,
id: viewId,
availableFrame: availableFrame,
contentMode: .scaleAspectFill,
customData: [:],
call: call
)
// Modifies the video view based on the participant's details and the call's state.
.modifier(
viewFactory.makeVideoCallParticipantModifier(
participant: participant,
call: call,
availableFrame: availableFrame,
ratio: availableFrame.width / availableFrame.height,
showAllInfo: true
)
)
// Applies changes to the participant's video track.
.modifier(ParticipantChangeModifier(
participant: participant,
onChangeTrackVisibility: onChangeTrackVisibility)
)
// Observes visibility changes of the dominant speaker's video track.
.visibilityObservation(in: availableFrame) { onChangeTrackVisibility(participant, $0) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@

import StreamVideo
import SwiftUI
import WebRTC

public struct ParticipantsSpotlightLayout<Factory: ViewFactory>: View {

private let thumbnailSize: CGFloat = 120

var viewFactory: Factory
var participant: CallParticipant
var participants: [CallParticipant]
Expand All @@ -34,70 +32,30 @@ public struct ParticipantsSpotlightLayout<Factory: ViewFactory>: View {
}

public var body: some View {
VStack {
viewFactory.makeVideoParticipantView(
participant: participant,
id: "\(participant.id)-spotlight",
availableFrame: topParticipantFrame,
contentMode: .scaleAspectFill,
customData: [:],
call: call
)
.modifier(
viewFactory.makeVideoCallParticipantModifier(
VStack(spacing: 0) {
GeometryReader { proxy in
viewFactory.makeDominantSpeakerLayoutComponent(
participant: participant,
viewIdSuffix: "spotlight",
availableFrame: proxy.frame(in: .global),
call: call,
availableFrame: topParticipantFrame,
ratio: topParticipantRatio,
showAllInfo: true
onChangeTrackVisibility: { onChangeTrackVisibility($0, $1) }
)
)
.modifier(ParticipantChangeModifier(
participant: participant,
onChangeTrackVisibility: onChangeTrackVisibility)
)
.visibilityObservation(in: topParticipantFrame) {
onChangeTrackVisibility(participant, $0)
}

ScrollView(.horizontal) {
HorizontalContainer {
ForEach(participants) { participant in
viewFactory.makeVideoParticipantView(
participant: participant,
id: participant.id,
availableFrame: participantStripItemFrame,
contentMode: .scaleAspectFill,
customData: [:],
call: call
)
.modifier(
viewFactory.makeVideoCallParticipantModifier(
participant: participant,
call: call,
availableFrame: participantStripItemFrame,
ratio: participantsStripItemRatio,
showAllInfo: true
)
)
.visibilityObservation(in: participantsStripFrame) { onChangeTrackVisibility(participant, $0) }
.cornerRadius(8)
.accessibility(identifier: "spotlightParticipantView")
}
}
.frame(height: participantStripItemFrame.height)
.cornerRadius(8)
}
.padding()
.padding(.bottom)
.accessibility(identifier: "spotlightScrollView")
viewFactory.makeBottomParticipantsBarLayoutComponent(
participants: participants,
availableFrame: participantsStripFrame,
call: call,
onChangeTrackVisibility: { onChangeTrackVisibility($0, $1) }
)
}
}

private var topParticipantFrame: CGRect {
.init(
origin: frame.origin,
size: CGSize(width: frame.size.width, height: frame.size.height - thumbnailSize - 64)
size: CGSize(width: frame.size.width, height: frame.size.height - thumbnailSize)
)
}

Expand All @@ -111,12 +69,4 @@ public struct ParticipantsSpotlightLayout<Factory: ViewFactory>: View {
private var topParticipantRatio: CGFloat {
topParticipantFrame.size.width / topParticipantFrame.size.height
}

private var participantsStripItemRatio: CGFloat {
participantsStripFrame.size.width / participantsStripFrame.size.height
}

private var participantStripItemFrame: CGRect {
.init(origin: .zero, size: .init(width: participantsStripFrame.height, height: participantsStripFrame.height))
}
}
Loading

0 comments on commit 2fabfdd

Please sign in to comment.