Since the username refactoring, the username in the store could be
undefined when the join input was pre-filled from user.full_name,
because no keystroke was needed to populate the store.
This led to a 400 error on the request-entry endpoint whenever the
user joined without editing the pre-filled name.
Fall back to user.full_name when the store username is missing, so
the endpoint always receives a value.
Acknowledged as a somewhat wobbly fix, but ships as-is until the
underlying flow is reworked.
Memoize the ActiveSpeaker component used in the push-to-talk
component so it does not re-render on unrelated parent updates when
its props have not changed.
The InviteDialog open state was managed very high in the React tree,
which triggered a re-render of the whole conference tree whenever
the dialog was opened or closed.
Push the state down to a narrower component to limit the scope of
re-renders.
Part of a broader effort to isolate as much as possible what should
re-render, so we can then focus on tackling the real performance
issues.
Add a new setting that controls whether an authenticated user can
rename or modify their display name, or if they must use the one
returned by the SSO.
When the setting is disabled, only anonymous users can set a display
name freely; authenticated users always use their SSO display name.
Requested by many self-hosters.
Create a new dedicated store for user choices, starting with the
username, to decouple this part from any LiveKit elements. This is
better for tree-shaking, and depending on LiveKit for storing the
username brings no real value.
The refactoring will be continued later for the other user choices
that can be persisted.
When joining a meeting that already has many participants, new
participants are now muted by default.
This is an empirical change, not directly requested by users but
informed by user experience: a lot of people joining large meetings
arrive with their microphone and/or camera open, which can be
painful for the host, who otherwise has to mute everyone or ask
everyone to mute. Many of these participants are inattentive but
still noisy.
If you are joining a room that is already at a certain size, you are
probably not the one expected to speak; presenters typically join
among the very first participants.
Replace the basic RNN noise processor with a more advanced audio
pipeline powered by Big Blue Better Audio (BBBA), a project
supported by the Prototype Fund.
The new WASM-based pipeline introduced by @falkTX and
@trummerschlunk adds:
- voice isolation
- high-pass filtering
- spectral balancing
- multiband compression
- low-pass filtering
This significantly improves overall audio quality and speech
clarity.
More information about the pipeline is available on the
trummerschlunk/BigBlueBetterAudio repo.
Voice isolation still relies on RNNNoise, a widely used deep
learning-based denoising model. Audio quality could be further
improved in the future if DeepFilterNet becomes usable directly
in the browser.
Their code has been released in an NPM package under GPL license.
Manually upgrade the frontend codebase to ESLint 9.
Update the linting configuration and related code where required to
maintain compatibility with the new major version.
The Valtio actions on the store were initially defined inside a hook,
which was a bad idea: it does not follow Valtio's recommendation of
defining actions at the module level, and the function definitions
were re-created every time the hook re-rendered, which happened on
every state update.
These action has nothing related to React.
Additionally, the snapshot should be used more directly so Valtio can
understand and optimize which parts of the proxy are of interest to
the snapshot being made.
Introduce a component that renders its children only if the logged-in
user is considered Admin or User. This helps avoid mounting the hooks
and logic of components that previously early-returned when the user
was not admin or owner, but were still re-rendering whenever their
internal logic updated.
Mark JS imports as type-only when applicable so they are stripped at
build time and ignored during chunk splitting, ensuring we take full
advantage of Rollup's code-splitting optimizations.
The PiP control bar shares state with the main window's control
bar — most importantly, the reaction toolbar and the mic/camera
toggles, which are fully stateful.
Sharing state works naturally here because the PiP content is
rendered through a portal into the same React tree, so the
controls in both windows read from and write to the same stores.
Co-authored-by: Cyril <c.gromoff@gmail.com>
Add a hook that manages the logic needed to open a document
picture-in-picture window, stores a ref to the window in a global
store, and—once ready—mounts a portal to duplicate content into
the PiP document's container.
Mounting the PiP content via a portal lets us share the same React
tree as the main app, and therefore share application state. This
comes with some trade-offs, particularly around components that
rely on the DOM hierarchy: react-aria popovers and overlays may
not behave correctly across documents.
The logic is kept to a minimum here. This first commit does nothing
more than open a window containing a loading spinner. The topic is
new to us, so plenty is likely to be refined in follow-ups.
Co-authored-by: Cyril <c.gromoff@gmail.com>
Updating connectionObserverSnapshot triggered page re-renders,
causing participants to reconnect due to a race condition.
Read directly from the underlying store instead of using the
snapshot to avoid unnecessary re-renders.
A 1–7 scale is not commonly used in software products. Feedback
from both users and the support team suggests reducing the number
of options to simplify usage and analysis.
Adopting a 1–5 scale improves usability and makes responses
easier to interpret and process.
The scale has to be odd.
Capture selected ICE candidates for both subscriber and publisher
peer connections.
This enables correlation between survey feedback and connectivity
setup, helping identify problematic network configurations.
Replace costly PostHog surveys with basic surveys, which better
fit our headless usage and avoid short data retention limits.
Enhance emitted events with additional metadata, including a
unique session ID and room ID, to correlate survey responses with
specific sessions.
This lays the groundwork for further enrichment with participant
connection data.
Add a custom virtual background feature.
If the backend supports uploading files, backgrounds are stored
in the backend for the user.
Otherwise, only one background image can be selected.
Refactor the duplicateIdentity boolean URL parameter into an extensible string
reason parameter, making it easier to customize the disconnection message
shown to users.
Avoid passing this value via URL parameters, which are easy to manipulate.
Instead, use Wouter’s built-in navigation state to pass data across pages.
This was initially missed because navigateTo is a wrapper around Wouter’s
official navigation function, and its arguments were easy to overlook. This is
now fixed.
This prepares the ground for supporting additional disconnection reasons in
upcoming commits.
While creating a meeting on mobile, the dialog was opening,
and when its width exceeds the mobile width, users are unable
to close the dialog.
Prevent the dialog opening on mobile as a hot fix.
Mark unnecessary decorative icons as aria-hidden following feedback
from @cyberbaloo to eliminate redundant screen reader announcements
that create noisy and annoying experience for users relying on
assistive technologies.
Adjust permission modal dimensions to properly fit mobile viewports
and prevent poor responsive user experience. Ensures modal content
remains accessible and readable across different screen sizes.
Resolve issue where users with disabled track preferences in local
storage wouldn't receive permission prompts in subsequent sessions,
causing app deadlock. Toggle tracks when permissions are disabled to
re-trigger permission requests.
This is a hotfix addressing critical user feedback. Permission handling
requires further testing and improvements based on gathered user
reports since release.
Revert recent changes to dynacast and adaptive streaming functionality
to isolate potential causes of regression issues. Changes will be
reintroduced in future commits with improved error handling and
thorough investigation of root causes.
Leverage reference to initial processor choice to prevent unnecessary
preview track recreation when updating processor options.
Improves performance by maintaining existing track instance during
processor updates instead of creating new tracks, eliminating visual
interruptions and reducing resource overhead.
Correct wrong copy-paste error in audio track dynamic initialization
that was missed during previous PR merge process.
Fixes initialization logic that was accidentally duplicated or
incorrectly modified during merge conflict resolution.
Replace separate prejoin and room toggle components with unified
component that's adaptable and easier to evolve without overfitting.
Adds responsibilities to join component but eliminates duplication. Join
component needs future refactoring as complexity is growing
significantly.
Remove ugly toggle device configuration and implement hook to determine
appropriate keyboard shortcuts based on media device kind.
Cleaner approach that encapsulates shortcut logic in reusable hook
instead of scattered configuration objects.
Create hook to encapsulate permission denied/prompted/loading checks
based on media kind, eliminating props drilling and simplifying code.
Returns appropriate permission state for consuming components based on
media type, cleaning up code structure with small enhancement.
Refactor device selection within rooms and add audio output selection
to audio controls as requested by users.
Ensures code reuse between join and room components by sharing device
selection logic across both contexts.
Replace default usePreviewTrack behavior that acquired media streams
even when users disabled audio/video in preferences, causing OS
recording indicators to show despite explicit user rejection.
Implement custom logic to only initiate preview tracks when actually
needed by user. Code is naive and could be optimized, but fixes the
misleading OS-level recording feedback that created user distrust.
Implement user choice persistence for video publishing resolution
configuration to maintain user preferences across sessions.
Stores selected video resolution in user settings, ensuring consistent
video quality preferences without requiring reconfiguration on each
visit.
Limit useResolveDefaultDeviceId hook execution to initial rendering
only, considering removal due to ux concerns.
Hook's purpose is to populate select fields when 'default' device
option isn't available in enumerated device list. Current
implementation may be too confusing for code execution.
Replace usePreviewTrack approach with manual track management inspired
by deprecated LiveKit implementation that only toggles what's needed
instead of resetting entire preview track on config changes.
Previous approach created new videoTrack when updating audio track,
causing video preview to blink black during audio configuration
changes.
It was also acquiring video streams even when disabled.
New implementation:
* Initializes tracks once with user permissions for both devices
* Manually handles muting/unmuting and device updates
* Preserves muted state when changing device IDs
* Uses exact device constraints on Chrome for proper device selection
Prevents unnecessary track recreation and eliminates preview blinking.
Update track muting/unmuting to prioritize immediate UI state changes
over error handling to prevent weird UX delays.
Ensures toggle buttons reflect new state instantly rather than waiting
for operation completion. Users expect immediate visual feedback when
interacting with mute controls, even if errors occur later.
When browsers don't return 'default' audio output device ID and only
one device is available, automatically select the single option to
improve user experience.
Prevents unnecessary user interaction when there's only one choice
available, making the device selection flow smoother and more intuitive
for users with single audio output setups.
This is necessary only for audio output because we don't create
a preview track, compare to video or mic.
Disable speaker selection on prejoin screen for Safari based on LiveKit
documentation stating audio output selection isn't supported, though
this needs further verification.
Maintains consistency with audio tab behavior until Safari audio output
support can be confirmed. Feature remains available on other browsers
where support is verified.