Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VIDCS-3340: Add pinning feature #41

Open
wants to merge 29 commits into
base: develop
Choose a base branch
from

Conversation

maikthomas
Copy link
Collaborator

@maikthomas maikthomas commented Feb 4, 2025

What is this PR doing?

Add part 1 of pinning feature.
What this PR includes:

  • Overlay button on subscriber video tile to pin/unpin subscriber
  • pinning limit - 3 on desktop, 1 on mobile, and disabled button when limit reached
  • pinned subscribers are large unless screenshare is present, pinned subscribers are never hidden

NOT INCLUDED IN THIS PR:

  • Pinning from participant list
  • any changes we may make to layout manager proportions for pinned subscribers

How should this be manually tested?

  • run with multiple participants
  • ensure you can pin/unpin up to the maximum number
  • pinned subscribers should never be hidden
  • pinned subscribers should be small when screenshare is present

What are the relevant tickets?

Resolves VIDCS-3340

@maikthomas maikthomas added the WIP Work in progress label Feb 4, 2025
@github-actions github-actions bot changed the base branch from main to develop February 4, 2025 16:19
@maikthomas maikthomas force-pushed the mthomas/VIDCS-3340-pinning branch from c37e048 to a620a98 Compare February 12, 2025 14:05
Comment on lines +8 to +10

# Option to skip the waiting room. Useful when doing development work on the meeting room
VITE_BYPASS_WAITING_ROOM=
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I added this to help speed up development testing. Let me know if you think it's ok to add this

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm okay with adding this option in. Looks like you added it to the file twice so maybe remove one of them?

Copy link
Contributor

Choose a reason for hiding this comment

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

Sounds good to me! Should we give it an initial value?

@@ -126,6 +131,8 @@ export type SessionProviderProps = {
children: ReactNode;
};

const MAX_PIN_COUNT = isMobile() ? 1 : 3;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I know we talked about using small viewport instead of mobile for this stuff, but the number of subscribers we display is currently governed by isMobile too. I would say let's change this in both places in a follow-up PR if that's what we decide to do.

Comment on lines +41 to +42
!isMobile() && (
<div>
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

ignore this, it's to make tests pass after I loaded tailwind into the setup test and it realised the tests were wrong.

Anyway, @cpettet's PR changes this component so when that PR is merged in I'll deal with the conflicts and this will probably not be changed by my PR at all.

Copy link
Contributor

Choose a reason for hiding this comment

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

I was just about to say, wait I remember lots of changes around here in a different PR 😆

return `Pin ${participantName}'s video`;
};

const onClick = (clickEvent: MouseEvent<HTMLButtonElement>) => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

You'll see this here and in the Subscriber component. Trust me when I say I tried everything to avoid adding this hack. In the end it was innevitable, even though we could use tailwind hover states to avoid some of this, the MUI toolbar open logic is not css based. Also tailwind struggled when the tooltip itself was hovered and de-applied the hover css to the area underneath.

At least I can say that this approach works.

Comment on lines +209 to +228
const pinSubscriber = useCallback(
(id: string) => {
setSubscriberWrappers((previousSubscriberWrappers) => {
const subscribers = previousSubscriberWrappers
.map((subscriberWrapper) => {
if (subscriberWrapper.id === id) {
return {
...subscriberWrapper,
isPinned: !subscriberWrapper.isPinned,
};
}
return subscriberWrapper;
})
.sort(sortByDisplayPriority(activeSpeakerId)); // Sorting by display priority will place this pinned participant above the rest
return subscribers;
});
},
[activeSpeakerId]
);

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This logic is untested right now (other than the integration test).
We plan to refactor this context provider in a way that will allow us to better test this internal logic.

Comment on lines -26 to 30
const bypass = searchParams.get('bypass');
const canEnter = !hasAccess && bypass !== 'true';
return canEnter ? (
const bypass =
searchParams.get('bypass') === 'true' || import.meta.env.VITE_BYPASS_WAITING_ROOM === 'true';
const mustEnterWaitingRoom = !hasAccess && !bypass;
return mustEnterWaitingRoom ? (
<Navigate
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Added the env var but also refactored slightly. I found canEnter confusing as it means canEnterWaiting room, whereas can enter IMO makes more sense to be about the room.

Comment on lines +54 to +56
!isMobile() && (
<div>
<Tooltip title={title} aria-label="add">
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Same comment here as for LayoutButton - tests were not working once tailwind was enabled and this will all be overwritten when the mobile menu bar PR is merged and I pull in those changes. Don't worry about this for now.

@@ -61,13 +66,30 @@ const Subscriber = ({
}
}, [subscriberWrapper, isScreenShare]);

const handlePinClick = (clickEvent: MouseEvent<HTMLButtonElement>) => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Again, nothing I could do to avoid this unfortunately.

Copy link
Contributor

Choose a reason for hiding this comment

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

since this is repeated in two components, could it be broken out to a separate hook? 🤔

@@ -1,3 +1,4 @@
import '../index.css';
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This makes vitest understand at least some of the tailwind rules, like className="hidden" means toBeVisible() is false.
It cannot do stuff like hover states though, probably more due to JSDOM not implementing CSS

sessionHasScreenshare: boolean,
layoutMode: LayoutMode,
activeSpeakerId: string | undefined,
index: number
) => {
// We display subscriber as large if it is screenshare or active speaker given that layout mode is active speaker and
// screenshare is not being displayed
return (
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I didn't attempt to simplify this at all, I think it's better to keep it readable and obvious as to which cases do what.

@maikthomas maikthomas removed the WIP Work in progress label Feb 13, 2025
Copy link
Contributor

@behei-vonage behei-vonage left a comment

Choose a reason for hiding this comment

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

impressive job 👏 I left two comments for now, I might take another look again since the PR is just massive 😱


const getTooltipText = () => {
if (isDisabled) {
return `You can't pin any more tiles`;
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: maybe instead of a current message, we could say the Current ${MAX_PIN} pin limit has been reached or something like that?

@@ -61,13 +66,30 @@ const Subscriber = ({
}
}, [subscriberWrapper, isScreenShare]);

const handlePinClick = (clickEvent: MouseEvent<HTMLButtonElement>) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

since this is repeated in two components, could it be broken out to a separate hook? 🤔

Copy link
Contributor

@cpettet cpettet left a comment

Choose a reason for hiding this comment

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

Looks good so far! Just have a few comments/questions. Let me know what you think!

Comment on lines +8 to +10

# Option to skip the waiting room. Useful when doing development work on the meeting room
VITE_BYPASS_WAITING_ROOM=
Copy link
Contributor

Choose a reason for hiding this comment

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

Sounds good to me! Should we give it an initial value?

}, [subscriberWrappers]);

/**
* Marks a subscriber as pinned, and moves it to the top of the display order.
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit, doesn't this function work for unpinning, too? Could we update the jsdoc?

@@ -17,6 +17,12 @@ const sortByDisplayPriority =
if (wrapperB.isScreenshare) {
return 1;
}
if (wrapperA.isPinned) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we add onto the jsdoc that we want pinned users to have priority? Since we have several prioritized subscribers, we should delineate the order, too.

handleClick: (clickEvent: MouseEvent<HTMLButtonElement>) => void;
};

const PinButton = ({
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we get some jsdoc for this component?

@@ -8,4 +8,5 @@ export type SubscriberWrapper = {
subscriber: Subscriber;
isScreenshare: boolean;
id: string;
isPinned: boolean;
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we add to the jsdoc, too?

Comment on lines +212 to +223
const subscribers = previousSubscriberWrappers
.map((subscriberWrapper) => {
if (subscriberWrapper.id === id) {
return {
...subscriberWrapper,
isPinned: !subscriberWrapper.isPinned,
};
}
return subscriberWrapper;
})
.sort(sortByDisplayPriority(activeSpeakerId)); // Sorting by display priority will place this pinned participant above the rest
return subscribers;
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we break the state function into a helper? I know we plan on refactoring later, but it might make sense to separate this logic so we can add the test now. Hopefully, the tests we write for this helper, could still be reused.

const hasVideo = subscriberWrapper.subscriber?.stream?.hasVideo;
const initials = subscriberWrapper.subscriber?.stream?.initials;
const username = subscriberWrapper.subscriber?.stream?.name ?? '';
const hasAudio = subscriberWrapper.subscriber.stream?.hasAudio;
const audioIndicatorStyle =
'rounded-xl absolute top-3 right-3 bg-darkGray-55 h-6 w-6 items-center justify-center flex m-auto';

const pinStyle = `flex rounded-xl absolute top-3 left-3 h-6 w-6 items-center justify-center m-auto`;
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we move this into the PinButton component?

Copy link
Contributor

Choose a reason for hiding this comment

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

If it's forward-thinking for the participant list changes, feel free to resolve this and my other comments.

>
{!isScreenShare && (
<PinButton
color="white"
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we move this to the PinButton directly?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants