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
9 changes: 6 additions & 3 deletions react/features/filmstrip/actions.any.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,19 @@ export function setRemoteParticipants(participants: Array<string>) {
*
* @param {number} startIndex - The start index from the remote participants array.
* @param {number} endIndex - The end index from the remote participants array.
* @param {number} fullyVisibleCount - The number of fully visible participants (excluding partially visible).
* @returns {{
* type: SET_VISIBLE_REMOTE_PARTICIPANTS,
* startIndex: number,
* endIndex: number
* endIndex: number,
* fullyVisibleCount: number
* }}
*/
export function setVisibleRemoteParticipants(startIndex: number, endIndex: number) {
export function setVisibleRemoteParticipants(startIndex: number, endIndex: number, fullyVisibleCount?: number) {
return {
type: SET_VISIBLE_REMOTE_PARTICIPANTS,
startIndex,
endIndex
endIndex,
fullyVisibleCount
};
}
23 changes: 21 additions & 2 deletions react/features/filmstrip/components/native/Filmstrip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { ASPECT_RATIO_NARROW } from '../../../base/responsive-ui/constants';
import { getHideSelfView } from '../../../base/settings/functions.any';
import { isToolboxVisible } from '../../../toolbox/functions.native';
import { setVisibleRemoteParticipants } from '../../actions.native';
import { calculateFullyVisibleParticipantsCount } from '../../functions.any';
import {
getFilmstripDimensions,
isFilmstripVisible,
Expand Down Expand Up @@ -190,7 +191,7 @@ class Filmstrip extends PureComponent<IProps> {
* @returns {void}
*/
_onViewableItemsChanged({ viewableItems = [] }: { viewableItems: ViewToken[]; }) {
const { _disableSelfView } = this.props;
const { _aspectRatio, _clientWidth, _clientHeight, _disableSelfView } = this.props;

if (!this._separateLocalThumbnail && !_disableSelfView && viewableItems[0]?.index === 0) {
// Skip the local thumbnail.
Expand All @@ -205,13 +206,31 @@ class Filmstrip extends PureComponent<IProps> {
let startIndex = Number(viewableItems[0].index);
let endIndex = Number(viewableItems[viewableItems.length - 1].index);

// Calculate fully visible count (excluding partially visible tiles)
const isNarrowAspectRatio = _aspectRatio === ASPECT_RATIO_NARROW;
const { height: thumbnailHeight, width: thumbnailWidth, margin } = styles.thumbnail;
const { height, width } = this._getDimensions();

// Calculate item size and container size based on layout orientation
const itemSize = isNarrowAspectRatio
? thumbnailWidth + (2 * margin) // Horizontal layout
: thumbnailHeight + (2 * margin); // Vertical layout
const containerSize = isNarrowAspectRatio ? width : height;

const fullyVisibleCount = calculateFullyVisibleParticipantsCount(
startIndex,
endIndex,
containerSize,
itemSize
);

if (!this._separateLocalThumbnail && !_disableSelfView) {
// We are off by one in the remote participants array.
startIndex -= 1;
endIndex -= 1;
}

this.props.dispatch(setVisibleRemoteParticipants(startIndex, endIndex));
this.props.dispatch(setVisibleRemoteParticipants(startIndex, endIndex, fullyVisibleCount));
}

/**
Expand Down
28 changes: 26 additions & 2 deletions react/features/filmstrip/components/web/Filmstrip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import {
TOP_FILMSTRIP_HEIGHT,
TOUCH_DRAG_HANDLE_PADDING
} from '../../constants';
import { calculateFullyVisibleParticipantsCount } from '../../functions.any';
import {
getVerticalViewMaxWidth,
isFilmstripDisabled,
Expand Down Expand Up @@ -944,10 +945,33 @@ class Filmstrip extends PureComponent <IProps, IState> {
*/
_onListItemsRendered({ visibleStartIndex, visibleStopIndex }: {
visibleStartIndex: number; visibleStopIndex: number; }) {
const { dispatch } = this.props;
const {
dispatch,
_currentLayout,
_filmstripWidth,
_filmstripHeight,
_thumbnailWidth,
_thumbnailHeight,
_isVerticalFilmstrip
} = this.props;

// Calculate fully visible count (excluding partially visible tiles)
const isHorizontal = _currentLayout === LAYOUTS.HORIZONTAL_FILMSTRIP_VIEW;
const itemSize = isHorizontal
? _thumbnailWidth + TILE_HORIZONTAL_MARGIN
: _thumbnailHeight + TILE_VERTICAL_MARGIN;
const containerSize = isHorizontal ? _filmstripWidth : _filmstripHeight;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jallamsetty1 Don't we have very similar logic somewhere?

Maybe we can reuse it?

const fullyVisibleCount = calculateFullyVisibleParticipantsCount(
visibleStartIndex,
visibleStopIndex,
containerSize,
itemSize
);

const { startIndex, stopIndex } = this._calculateIndices(visibleStartIndex, visibleStopIndex);

dispatch(setVisibleRemoteParticipants(startIndex, stopIndex));
dispatch(setVisibleRemoteParticipants(startIndex, stopIndex, fullyVisibleCount));
}

/**
Expand Down
38 changes: 35 additions & 3 deletions react/features/filmstrip/functions.any.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export function updateRemoteParticipants(store: IStore, force?: boolean, partici
? [ ...sortedRemoteVirtualScreenshareParticipants.keys() ] : [];
const sharedVideos = fakeParticipants ? Array.from(fakeParticipants.keys()) : [];
const speakers = new Array<string>();
const { visibleRemoteParticipants } = state['features/filmstrip'];
const { fullyVisibleRemoteParticipantsCount } = state['features/filmstrip'];

const participantsWithScreenShare = screenShareParticipants.reduce<string[]>((acc, screenshare) => {
const ownerId = getVirtualScreenshareParticipantOwnerId(screenshare);
Expand All @@ -69,9 +69,13 @@ export function updateRemoteParticipants(store: IStore, force?: boolean, partici
speakers.push(dominant.id);
}

// Find the number of slots available for speakers.
// Find the number of slots available for speakers. Use fullyVisibleRemoteParticipantsCount to exclude partially
// visible tiles, ensuring dominant speaker is placed on a fully visible tile.
const slotsForSpeakers
= visibleRemoteParticipants.size - (screenShareParticipants.length * 2) - sharedVideos.length - dominantSpeakerSlot;
= fullyVisibleRemoteParticipantsCount
- (screenShareParticipants.length * 2)
- sharedVideos.length
- dominantSpeakerSlot;

// Construct the list of speakers to be shown.
if (slotsForSpeakers > 0) {
Expand Down Expand Up @@ -127,3 +131,31 @@ export function isTileViewModeDisabled(state: IReduxState) {

return tileView.disabled;
}

/**
* Calculates the count of fully visible participants, excluding any partially visible tiles.
* This respects the actual rendered items from the list component while accounting for
* container padding/gaps.
*
* @param {number} visibleStartIndex - The start index of visible items.
* @param {number} visibleEndIndex - The end index of visible items.
* @param {number} containerSize - The width or height of the filmstrip container.
* @param {number} itemSize - The width or height of each item including margin.
* @returns {number} - The count of fully visible participants (at least 1).
*/
export function calculateFullyVisibleParticipantsCount(
visibleStartIndex: number,
visibleEndIndex: number,
containerSize: number,
itemSize: number
): number {
// Current visible count from the list component (includes any partially visible tile)
const currentVisibleCount = visibleEndIndex - visibleStartIndex + 1;

// Theoretical max that can fit fully in the container
const maxFullyVisible = Math.floor(containerSize / itemSize);

// Fully visible count is the minimum of actual visible and max that can fit fully
// Ensure at least 1 if there are any visible items
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jallamsetty1 This is not quite true!

I would say you either have maxFullyVisible (in this case currentVisibleCount=maxFullyVisible) or maxFullyVisible - 1 (in this case currentVisibleCount=maxFullyVisible+1) full tiles.

It looks like that on some cases you are counting the full tiles with one more (in the second case above).

return Math.max(1, Math.min(currentVisibleCount, maxFullyVisible));
}
15 changes: 13 additions & 2 deletions react/features/filmstrip/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,15 @@ const DEFAULT_STATE = {
*/
visibleRemoteParticipants: new Set<string>(),

/**
* The number of fully visible remote participants (excluding partially visible ones).
* Used for calculating speaker slots to avoid placing dominant speaker on partially visible tiles.
*
* @public
* @type {number}
*/
fullyVisibleRemoteParticipantsCount: 0,

/**
* The width of the resizable filmstrip.
*
Expand Down Expand Up @@ -201,6 +210,7 @@ export interface IFilmstripState {
pinned?: boolean;
}>;
enabled: boolean;
fullyVisibleRemoteParticipantsCount: number;
horizontalViewDimensions: {
hasScroll?: boolean;
local?: IDimensions;
Expand Down Expand Up @@ -301,15 +311,16 @@ ReducerRegistry.register<IFilmstripState>(
}
};
case SET_VISIBLE_REMOTE_PARTICIPANTS: {
const { endIndex, startIndex } = action;
const { endIndex, startIndex, fullyVisibleCount } = action;
const { remoteParticipants } = state;
const visibleRemoteParticipants = new Set(remoteParticipants.slice(startIndex, endIndex + 1));

return {
...state,
visibleParticipantsStartIndex: startIndex,
visibleParticipantsEndIndex: endIndex,
visibleRemoteParticipants
visibleRemoteParticipants,
fullyVisibleRemoteParticipantsCount: fullyVisibleCount ?? visibleRemoteParticipants.size
};
}
case PARTICIPANT_LEFT: {
Expand Down
Loading