Compare commits

..

35 Commits

Author SHA1 Message Date
Hristo Terezov
303f81f427 fix(participants-pane): replace :first-child with :first-of-type to silence emotion SSR warning
emotion/stylis emits a console error for :first-child selectors as they can
cause hydration mismatches in SSR. All three occurrences that trigger on
conference load are in ParticipantsPane. The semantics are equivalent since
all sibling elements are the same type.
2026-02-18 08:30:23 -06:00
Hristo Terezov
23a65fbf2a fix(ui): migrate ReactDOM.render to createRoot for React 18 compatibility
ReactDOM.render is deprecated in React 18 and causes console errors on every
render. Migrate to createRoot API across the main entry point and the legacy
video layout modules, storing root references to avoid recreating them on
repeated calls.
2026-02-18 08:25:25 -06:00
Hristo Terezov
5fee521938 fix(ui): replace kebab-case CSS properties with camelCase in style objects
Vendor-prefixed CSS properties like -webkit-appearance and -moz-appearance
used as object keys in emotion/tss-react style objects cause console errors
because JavaScript CSS-in-JS libraries require camelCase property names.

Replace all occurrences across Input, Chat, Filmstrip, and Slider components.
2026-02-18 08:25:08 -06:00
Mihaela Dumitru
9ea9f4c899 [WIP] feat(ui) add semantic tokens (#16772) 2026-02-06 15:06:54 +02:00
Mihaela Dumitru
28c72bfa7f fix(toolbar): remove hang up from prejoin app 2026-02-06 12:48:29 +01:00
dependabot[bot]
24c78cf8ff chore(deps-dev): bump webpack from 5.95.0 to 5.105.0
Bumps [webpack](https://github.com/webpack/webpack) from 5.95.0 to 5.105.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Changelog](https://github.com/webpack/webpack/blob/main/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack/compare/v5.95.0...v5.105.0)

---
updated-dependencies:
- dependency-name: webpack
  dependency-version: 5.105.0
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-06 08:58:18 +01:00
Stephan Paternotte
147e64106d lang: Dutch translation updates.
* Update main-nl.json

Full update to latest state of main.json including Dutch translation updates.
Validated with JSONLint

* Fixes lint.

---------

Co-authored-by: Дамян Минков <damencho@jitsi.org>
2026-02-05 16:58:54 -06:00
Hristo Terezov
3d53f2e4c5 fix(PiP): Mac OS minimize not working
When we minimize the meeting window on Mac OS via the yellow button, 2 requests for PiP are triggered one after another because of the blur and the visibilitychange handlers. If we implement in Electron to focus the meeting window on the pipLeft event this will lead to the first request to exit triggering the pipLeft event after the second request is triggered which will actually bring the window back because we focus on pipLeft event. This behaviour breaks the ability to minimize a window.

The current fix will prevent us for sending 2 requests for PiP one after another by skipping the second unnecessary request.
2026-02-05 12:36:07 -06:00
damencho
e60bfc573a fix(muc_auth_ban): Adds checks for jaas. 2026-02-04 06:14:30 -06:00
damencho
796a7efa7f fix(tests): Make tests to go through pre-join automatically. 2026-02-03 17:58:28 -06:00
damencho
fb075c376d fix(prejoin): Handles different ways of disabling prejoin.
Now handles using `config.prejoinConfig.enabled=false` and `config.prejoinConfig={enabled:false}.

Fixes #16892.
2026-02-03 17:58:28 -06:00
Mihaela Dumitru
c457ed0d3c feat(external-api): fire participantMuted event for all mute state changes (#16893) 2026-02-03 10:59:28 +02:00
damencho
1efc5e40e1 fix(settings): Fixes rendering a tab when props change.
The Profile tab can display logged in state and auth id used for it. It does not re-render when this changes in the background.
2026-02-02 15:52:19 -06:00
Дамян Минков
dc84826d9c fix(muc_cleanup_backend_services): Stops timer before creating new one. (#16894)
* fix(muc_cleanup_backend_services): Stops timer before creating new one.

* squash: Adds some logs and skip if already being destroyed.
2026-02-02 10:09:26 -06:00
Horatiu Muresan
b4a64ebc44 fix(recording-button) Fix recording button tooltip (#16891) 2026-01-30 13:28:39 +02:00
Hristo Terezov
dbb3ccc274 feat(CustomPanel): Implement an customizable panel.
The panel will appear on the right side after the participant pane panel. Currently the panel is disabled by default and the components that  are rendered in the panel are empty (null).  The panel is easily customizable by adding some content in the CustomPanel component.
2026-01-29 14:59:06 -06:00
bgrozev
9a6ed65cb1 test: Retry some tests (#16888)
* chore: Update wdio to 9.23.2.

* test: Add a retry test property.

* test: Retry some of the flaky tests.
2026-01-29 13:58:57 -06:00
bgrozev
21ea67b29c fix: Only parse transcript for transcription-result messages. (#16885) 2026-01-28 16:23:12 -06:00
bgrozev
ab4be2366f test: Add test for jaas "async" transcriptions. (#16793)
* test: Add test for jaas "async" transcriptions.

* fix: Do not expect name in async transcription events.
2026-01-27 18:56:16 -06:00
Calin-Teodor
417c38ab9e fix(filmstrip): keep AudioTracksContainer in the DOM while Filmstrip is hidden in reduced UI 2026-01-27 17:53:57 +02:00
Calinteodor
33a4245a1f fix(lobby): reset lobbyVisible state when we a potential conference is being left (#16881)
* Reset lobby state when we we dispatch conferenceWillLeave action.
2026-01-27 12:24:16 +02:00
Mihaela Dumitru
2eb07cb79f feat(participants): store user context data for external API events and functions 2026-01-27 10:42:02 +01:00
damencho
63e4c41d92 fix(tests): Fixes tests stack traces.
Before:
```
Error: element ("[data-testid="participant1-more-options-ba16a58a"]") still not existing after 1000ms
    at async ParticipantsPane.openParticipantContextMenu (/tmp/tmp.bMVHEDmYin/jitsi-meet/tests/pageobjects/ParticipantsPane.ts:202:9)
    at async BreakoutRooms.sendParticipantToBreakoutRoom (/tmp/tmp.bMVHEDmYin/jitsi-meet/tests/pageobjects/BreakoutRooms.ts:205:9)
```

After:
```
Error: element ("[data-testid="participant1-more-options-ecea6dd6"]") still not existing after 1000ms
    at async ParticipantsPane.openParticipantContextMenu (/tmp/tmp.j8VkoO9abR/jitsi-meet/tests/pageobjects/ParticipantsPane.ts:202:9)
    at async BreakoutRooms.sendParticipantToBreakoutRoom (/tmp/tmp.j8VkoO9abR/jitsi-meet/tests/pageobjects/BreakoutRooms.ts:205:9)
    at async Context.<anonymous> (/tmp/tmp.j8VkoO9abR/jitsi-meet/tests/specs/misc/breakoutRooms.spec.ts:349:9)
```
2026-01-26 12:30:32 -06:00
damencho
2c6ccd7d6b fix(transcriptions): Fixes stop transcriptions via api. 2026-01-23 13:39:48 -06:00
damencho
4ce27eeb1a chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v2118.0.0+67fd2c84...v2124.0.0+80df84a1
2026-01-23 12:34:38 -06:00
damencho
11453dcc79 fix(transcriptions): Fixes stop transcriptions in some cases. 2026-01-23 10:18:23 -06:00
damencho
3375ee49bd fix(tests): Ignore clickable for password dialog.
It gives strange error that is not clickable, but it is seen on the screenshot and test passes without it.
2026-01-23 06:03:06 -06:00
damencho
e45df58cfb fix(tests): Fixes a problem with two notifications. 2026-01-23 06:03:06 -06:00
Calin-Teodor
7d628960d7 feat(breakout-rooms): button margin style fix 2026-01-22 10:52:48 -06:00
Calin-Teodor
cf13b8f0ba fix(responsive-ui): apply reducedUI only when both width and height are below threshold 2026-01-22 10:52:48 -06:00
Jaya Allamsetty
e106109090 fix(recording) Restores track mute state to original state after consent is provided.
Do not forcefully unmute audio and video if the user consents to being recorded and wants to stay unmuted but was muted before hitting join.
2026-01-22 10:11:43 -05:00
Calin-Teodor
fc170891cb feat(subtitles): rework how isAsyncTranscriptionEnabled is being used 2026-01-22 14:00:06 +02:00
Calin-Teodor
775cc52f66 feat(subtitles): hide translation UI for both web and native 2026-01-22 10:50:03 +02:00
Calin-Teodor
3baede6ff1 feat(config): add reducedUIEnabled config to control web side 2026-01-22 10:44:15 +02:00
Дамян Минков
9462a9ce36 feat(breakout-rooms): Adds some jid validation on joining. (#16858)
* feat(breakout-rooms): Adds some jid validation on joining.

* squash: Fix comments.

* Update resources/prosody-plugins/mod_muc_breakout_rooms.lua

Co-authored-by: bgrozev <boris@jitsi.org>

* Update resources/prosody-plugins/mod_muc_breakout_rooms.lua

Co-authored-by: bgrozev <boris@jitsi.org>

---------

Co-authored-by: bgrozev <boris@jitsi.org>
2026-01-21 19:53:27 -06:00
199 changed files with 4763 additions and 1393 deletions

View File

@@ -927,6 +927,9 @@ var config = {
// [ 'microphone', 'camera' ]
// ],
// Enable reduced UI on web.
// reducedUIEnabled: true,
// Overrides the buttons displayed in the main toolbar for reduced UI.
// When there isn't an override for a certain configuration the default jitsi-meet configuration will be used.
// The order of the buttons in the array is preserved.

View File

@@ -45,7 +45,7 @@ body {
.jitsi-icon {
&-default svg {
fill: white;
fill: var(--icon-default-color, white);
}
}

View File

@@ -1,5 +1,5 @@
.always-on-top-toolbox {
background-color: $newToolbarBackgroundColor;
background-color: var(--toolbox-background-color, $newToolbarBackgroundColor);
border-radius: 3px;
display: flex;
z-index: $toolbarZ;

View File

@@ -2,7 +2,7 @@
* Round badge.
*/
.badge-round {
background-color: #165ECC;
background-color: var(--toolbar-badge-background, #165ECC);
border-radius: 50%;
box-sizing: border-box;
color: #FFFFFF;
@@ -93,7 +93,7 @@
.toolbox-content-wrapper::after {
content: '';
background: $newToolbarBackgroundColor;
background: var(--toolbox-background-color, $newToolbarBackgroundColor);
padding-bottom: env(safe-area-inset-bottom, 0);
}

View File

@@ -60,7 +60,7 @@
}
#notification-participant-list {
background-color: $newToolbarBackgroundColor;
background-color: var(--toolbox-background-color, $newToolbarBackgroundColor);
border: 1px solid rgba(255, 255, 255, .4);
border-radius: 8px;
left: 0;

File diff suppressed because it is too large Load Diff

View File

@@ -227,6 +227,9 @@
"video_ssrc": "Video SSRC:",
"yes": "yes"
},
"customPanel": {
"close": "Close"
},
"dateUtils": {
"earlier": "Earlier",
"today": "Today",
@@ -1315,6 +1318,7 @@
"chat": "Open / Close chat",
"clap": "Clap",
"closeChat": "Close chat",
"closeCustomPanel": "Close",
"closeMoreActions": "Close more actions menu",
"closeParticipantsPane": "Close participants pane",
"closedCaptions": "Closed captions",
@@ -1420,9 +1424,11 @@
"chat": "Open / Close chat",
"clap": "Clap",
"closeChat": "Close chat",
"closeCustomPanel": "Close",
"closeParticipantsPane": "Close participants pane",
"closeReactionsMenu": "Close reactions menu",
"closedCaptions": "Closed captions",
"copilot": "Copilot",
"disableNoiseSuppression": "Disable extra noise suppression",
"disableReactionSounds": "You can disable reaction sounds for this meeting",
"documentClose": "Close shared document",

View File

@@ -240,13 +240,27 @@ function initCommands() {
APP.store.dispatch(muteAllParticipants(exclude, muteMediaType));
},
'mute-remote-participant': (participantId, mediaType) => {
if (!isLocalParticipantModerator(APP.store.getState())) {
logger.error('Missing moderator rights to mute remote participant');
const state = APP.store.getState();
const muteMediaType = mediaType ? mediaType : MEDIA_TYPE.AUDIO;
const localParticipant = getLocalParticipant(state);
// Check if targeting the local participant
if (participantId === localParticipant?.id) {
if (muteMediaType === MEDIA_TYPE.AUDIO) {
APP.conference.toggleAudioMuted(false);
} else if (muteMediaType === MEDIA_TYPE.VIDEO) {
APP.conference.toggleVideoMuted(false, true);
}
return;
}
const muteMediaType = mediaType ? mediaType : MEDIA_TYPE.AUDIO;
if (!isLocalParticipantModerator(state)) {
logger.error('Missing moderator rights to mute remote participant');
return;
}
APP.store.dispatch(muteRemote(participantId, muteMediaType));
},
@@ -812,7 +826,7 @@ function initCommands() {
}
if (transcription) {
APP.store.dispatch(setRequestingSubtitles(false, false, null));
APP.store.dispatch(setRequestingSubtitles(false, false, null, true));
}
if (mode === 'local') {
@@ -1417,17 +1431,15 @@ class API {
*
* @param {string} participantId - The ID of the participant.
* @param {boolean} isMuted - True if muted, false if unmuted.
* @param {string} mediaType - Media type that was muted ('audio', 'video', or 'desktop').
* @param {boolean} isSelfMuted - True if participant muted themselves, false if muted by moderator.
* @param {string} mediaType - Media type that was muted ('audio' or 'video').
* @returns {void}
*/
notifyParticipantMuted(participantId, isMuted, mediaType, isSelfMuted = true) {
notifyParticipantMuted(participantId, isMuted, mediaType) {
this._sendEvent({
name: 'participant-muted',
id: participantId,
isMuted,
mediaType,
isSelfMuted
mediaType
});
}

View File

@@ -3,7 +3,7 @@
import Logger from '@jitsi/logger';
import $ from 'jquery';
import React from 'react';
import ReactDOM from 'react-dom';
import { createRoot } from 'react-dom/client';
import { I18nextProvider } from 'react-i18next';
import { Provider } from 'react-redux';
@@ -166,7 +166,8 @@ export default class LargeVideoManager {
this.removePresenceLabel();
ReactDOM.unmountComponentAtNode(this._dominantSpeakerAvatarContainer);
this._avatarRoot?.unmount();
this._avatarRoot = null;
this.container.style.display = 'none';
}
@@ -518,14 +519,16 @@ export default class LargeVideoManager {
* Updates the src of the dominant speaker avatar
*/
updateAvatar() {
ReactDOM.render(
if (!this._avatarRoot) {
this._avatarRoot = createRoot(this._dominantSpeakerAvatarContainer);
}
this._avatarRoot.render(
<Provider store = { APP.store }>
<Avatar
id = "dominantSpeakerAvatar"
participantId = { this.id }
size = { 200 } />
</Provider>,
this._dominantSpeakerAvatarContainer
</Provider>
);
}
@@ -559,15 +562,18 @@ export default class LargeVideoManager {
const presenceLabelContainer = document.getElementById('remotePresenceMessage');
if (presenceLabelContainer) {
ReactDOM.render(
if (!this._presenceLabelRoot) {
this._presenceLabelRoot = createRoot(presenceLabelContainer);
}
this._presenceLabelRoot.render(
<Provider store = { APP.store }>
<I18nextProvider i18n = { i18next }>
<PresenceLabel
participantID = { id }
className = 'presence-label' />
</I18nextProvider>
</Provider>,
presenceLabelContainer);
</Provider>
);
}
}
@@ -577,11 +583,8 @@ export default class LargeVideoManager {
* @returns {void}
*/
removePresenceLabel() {
const presenceLabelContainer = document.getElementById('remotePresenceMessage');
if (presenceLabelContainer) {
ReactDOM.unmountComponentAtNode(presenceLabelContainer);
}
this._presenceLabelRoot?.unmount();
this._presenceLabelRoot = null;
}
/**

View File

@@ -4,7 +4,7 @@
import Logger from '@jitsi/logger';
import $ from 'jquery';
import React from 'react';
import ReactDOM from 'react-dom';
import { createRoot } from 'react-dom/client';
import { browser } from '../../../react/features/base/lib-jitsi-meet';
import { FILMSTRIP_BREAKPOINT } from '../../../react/features/filmstrip/constants';
@@ -659,7 +659,12 @@ export class VideoContainer extends LargeContainer {
return;
}
ReactDOM.render(
const container = document.getElementById('largeVideoBackgroundContainer');
if (!this._backgroundRoot) {
this._backgroundRoot = createRoot(container);
}
this._backgroundRoot.render(
<LargeVideoBackground
hidden = { this._hideBackground || this._isHidden }
mirror = {
@@ -669,8 +674,7 @@ export class VideoContainer extends LargeContainer {
}
orientationFit = { this._backgroundOrientation }
videoElement = { this.video }
videoTrack = { this.stream } />,
document.getElementById('largeVideoBackgroundContainer')
videoTrack = { this.stream } />
);
}
}

2119
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -72,7 +72,7 @@
"js-md5": "0.6.1",
"js-sha512": "0.8.0",
"jwt-decode": "2.2.0",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2118.0.0+67fd2c84/lib-jitsi-meet.tgz",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2124.0.0+80df84a1/lib-jitsi-meet.tgz",
"lodash-es": "4.17.21",
"null-loader": "4.0.1",
"optional-require": "1.0.3",
@@ -164,12 +164,12 @@
"@types/w3c-image-capture": "1.0.6",
"@types/w3c-web-hid": "1.0.3",
"@types/zxcvbn": "4.4.1",
"@wdio/allure-reporter": "9.22.0",
"@wdio/cli": "9.22.0",
"@wdio/globals": "9.17.0",
"@wdio/junit-reporter": "9.21.0",
"@wdio/local-runner": "9.22.0",
"@wdio/mocha-framework": "9.22.0",
"@wdio/allure-reporter": "9.23.2",
"@wdio/cli": "9.23.2",
"@wdio/globals": "9.23.0",
"@wdio/junit-reporter": "9.23.2",
"@wdio/local-runner": "9.23.2",
"@wdio/mocha-framework": "9.23.2",
"babel-loader": "9.1.0",
"babel-plugin-optional-require": "0.3.1",
"circular-dependency-plugin": "5.2.0",
@@ -194,7 +194,7 @@
"typescript": "5.7.2",
"unorm": "1.6.0",
"webdriverio": "9.22.0",
"webpack": "5.95.0",
"webpack": "5.105.0",
"webpack-bundle-analyzer": "4.4.2",
"webpack-cli": "5.1.4",
"webpack-dev-server": "5.1.0"

View File

@@ -1,4 +1,4 @@
import { setRoom } from '../base/conference/actions';
import { setRoom } from '../base/conference/actions.native';
import { getConferenceState } from '../base/conference/functions';
import {
configWillLoad,
@@ -29,7 +29,7 @@ import {
} from '../mobile/navigation/rootNavigationContainerRef';
import { screen } from '../mobile/navigation/routes';
import { clearNotifications } from '../notifications/actions';
import { isUnsafeRoomWarningEnabled } from '../prejoin/functions';
import { isUnsafeRoomWarningEnabled } from '../prejoin/functions.native';
import { maybeRedirectToTokenAuthUrl } from './actions.any';
import { addTrackStateToURL, getDefaultURL } from './functions.native';

View File

@@ -26,5 +26,6 @@ import '../face-landmarks/middleware';
import '../gifs/middleware';
import '../whiteboard/middleware.web';
import '../file-sharing/middleware.web';
import '../custom-panel/middleware.web';
import './middlewares.any';

View File

@@ -1,5 +1,6 @@
import '../base/devices/reducer';
import '../base/premeeting/reducer';
import '../custom-panel/reducer';
import '../base/tooltip/reducer';
import '../e2ee/reducer';
import '../face-landmarks/reducer';

View File

@@ -31,6 +31,7 @@ import { IUserInteractionState } from '../base/user-interaction/reducer';
import { IBreakoutRoomsState } from '../breakout-rooms/reducer';
import { ICalendarSyncState } from '../calendar-sync/reducer';
import { IChatState } from '../chat/reducer';
import { ICustomPanelState } from '../custom-panel/reducer';
import { IDeepLinkingState } from '../deep-linking/reducer';
import { IDropboxState } from '../dropbox/reducer';
import { IDynamicBrandingState } from '../dynamic-branding/reducer';
@@ -121,6 +122,7 @@ export interface IReduxState {
'features/calendar-sync': ICalendarSyncState;
'features/call-integration': ICallIntegrationState;
'features/chat': IChatState;
'features/custom-panel': ICustomPanelState;
'features/deep-linking': IDeepLinkingState;
'features/dropbox': IDropboxState;
'features/dynamic-branding': IDynamicBrandingState;

View File

@@ -81,7 +81,6 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
break;
}
case LOCAL_PARTICIPANT_MODERATION_NOTIFICATION: {
let descriptionKey;
let titleKey;
let uid = '';
const localParticipant = getLocalParticipant(getState);
@@ -111,8 +110,6 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
!raisedHand && dispatch(raiseHand(true));
dispatch(hideNotification(uid));
}) ],
descriptionKey,
sticky: true,
titleKey,
uid
}, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
@@ -271,7 +268,6 @@ StateListenerRegistry.register(
dispatch(showNotification({
titleKey: 'notify.hostAskedUnmute',
sticky: true,
customActionNameKey,
customActionHandler,
uid: ASKED_TO_UNMUTE_NOTIFICATION_ID

View File

@@ -93,10 +93,17 @@ export function commonUserJoinedHandling(
if (!user.isHidden()) {
const isReplacing = user?.isReplacing();
const isPromoted = conference?.getMetadataHandler().getMetadata()?.visitors?.promoted?.[id];
const userIdentity = user.getIdentity()?.user;
// Map identity from JWT context to userContext for external API
const userContext = userIdentity ? {
id: userIdentity.id,
name: userIdentity.name
} : undefined;
// the identity and avatar come from jwt and never change in the presence
dispatch(participantJoined({
avatarURL: user.getIdentity()?.user?.avatar,
avatarURL: userIdentity?.avatar,
botType: user.getBotType(),
conference,
id,
@@ -105,7 +112,8 @@ export function commonUserJoinedHandling(
role: user.getRole(),
isPromoted,
isReplacing,
sources: user.getSources()
sources: user.getSources(),
userContext
}));
}
}

View File

@@ -559,6 +559,7 @@ export interface IConfig {
skipConsentInMeeting?: boolean;
suggestRecording?: boolean;
};
reducedUIEnabled?: boolean;
reducedUImainToolbarButtons?: Array<string>;
remoteVideoMenu?: {
disableDemote?: boolean;

View File

@@ -215,6 +215,7 @@ export default [
'recordings.showPrejoinWarning',
'recordings.showRecordingLink',
'recordings.suggestRecording',
'reducedUIEnabled',
'reducedUImainToolbarButtons',
'replaceParticipant',
'resolution',

View File

@@ -387,7 +387,8 @@ export function setConfigFromURLParams(
// When not in an iframe, start without media if the pre-join page is not enabled.
if (!isEmbedded()
&& 'config.prejoinConfig.enabled' in params && config.prejoinConfig?.enabled === false) {
&& ('config.prejoinConfig' in params || 'config.prejoinConfig.enabled' in params)
&& config.prejoinConfig?.enabled === false) {
logger.warn('Using prejoinConfig.enabled config URL overwrite implies starting without media.');
config.disableInitialGUM = true;
}

View File

@@ -4,6 +4,7 @@ import { getCustomerDetails } from '../../jaas/actions.any';
import { getJaasJWT, isVpaasMeeting } from '../../jaas/functions';
import { navigateRoot } from '../../mobile/navigation/rootNavigationContainerRef';
import { screen } from '../../mobile/navigation/routes';
import { conferenceWillLeave } from '../conference/actions.native';
import { setJWT } from '../jwt/actions';
import { JitsiConnectionErrors } from '../lib-jitsi-meet';
@@ -58,5 +59,8 @@ export function connect(id?: string, password?: string) {
* @returns {Function}
*/
export function hangup(_requestFeedback = false) {
return (dispatch: IStore['dispatch']) => dispatch(appNavigate(undefined));
return (dispatch: IStore['dispatch']) => {
dispatch(conferenceWillLeave());
dispatch(appNavigate(undefined));
};
}

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-sparkles-icon lucide-sparkles"><path d="M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594z"/><path d="M20 2v4"/><path d="M22 4h-4"/><circle cx="4" cy="20" r="2"/></svg>

After

Width:  |  Height:  |  Size: 582 B

View File

@@ -1,3 +1,4 @@
import { default as IconAI } from './AI.svg';
import { default as IconRecordAccount } from './account-record.svg';
import { default as IconAddUser } from './add-user.svg';
import { default as IconArrowBack } from './arrow-back.svg';
@@ -112,6 +113,7 @@ import { default as IconYahoo } from './yahoo.svg';
*/
export const DEFAULT_ICON: Record<string, any> = {
IconAddUser,
IconAI,
IconArrowBack,
IconArrowDown,
IconArrowDownLarge,

View File

@@ -4,6 +4,7 @@ import { DEFAULT_ICON } from './constants';
const {
IconAddUser,
IconAI,
IconArrowBack,
IconArrowDown,
IconArrowDownLarge,
@@ -123,6 +124,7 @@ const {
export {
IconAddUser,
IconAI,
IconArrowBack,
IconArrowDown,
IconArrowDownLarge,

View File

@@ -56,9 +56,9 @@ const useStyles = makeStyles()(theme => {
label: {
...theme.typography.labelRegular,
alignItems: 'center',
background: theme.palette.ui04,
background: theme.palette.labelBackground,
borderRadius: '4px',
color: theme.palette.text01,
color: theme.palette.labelText,
display: 'flex',
margin: '0 2px',
padding: '6px',
@@ -72,11 +72,11 @@ const useStyles = makeStyles()(theme => {
cursor: 'pointer'
},
[COLORS.white]: {
background: theme.palette.ui09,
color: theme.palette.text04,
background: theme.palette.labelWhiteBackground,
color: theme.palette.labelWhiteText,
'& svg': {
fill: theme.palette.icon04
fill: theme.palette.labelWhiteIcon
}
},
[COLORS.green]: {

View File

@@ -1,4 +1,5 @@
import { IReduxState, IStore } from '../../app/types';
import { getLocalParticipant } from '../participants/functions';
import StateListenerRegistry from '../redux/StateListenerRegistry';
/**
@@ -13,6 +14,13 @@ StateListenerRegistry.register(
if (muted !== previousMuted) {
APP.API.notifyAudioMutedStatusChanged(muted);
// Also fire the participantMuted event for consistency
const localParticipant = getLocalParticipant(store.getState());
if (localParticipant) {
APP.API.notifyParticipantMuted(localParticipant.id, muted, 'audio');
}
}
}
);

View File

@@ -606,13 +606,21 @@ function _e2eeUpdated({ getState, dispatch }: IStore, conference: IJitsiConferen
function _localParticipantJoined({ getState, dispatch }: IStore, next: Function, action: AnyAction) {
const result = next(action);
const settings = getState()['features/base/settings'];
const state = getState();
const settings = state['features/base/settings'];
const jwtUser = state['features/base/jwt']?.user;
const userContext = jwtUser ? {
id: jwtUser.id,
name: jwtUser.name
} : undefined;
dispatch(localParticipantJoined({
avatarURL: settings.avatarURL,
email: settings.email,
name: settings.displayName,
id: ''
id: '',
userContext
}));
return result;

View File

@@ -627,7 +627,8 @@ function _participantJoined({ participant }: { participant: IParticipant; }) {
pinned,
presence,
role,
sources
sources,
userContext
} = participant;
let { conference, id } = participant;
@@ -659,7 +660,8 @@ function _participantJoined({ participant }: { participant: IParticipant; }) {
pinned: pinned || false,
presence,
role: role || PARTICIPANT_ROLE.NONE,
sources
sources,
userContext
};
}

View File

@@ -41,6 +41,12 @@ export interface IParticipant {
role?: string;
sources?: Map<string, Map<string, ISourceInfo>>;
supportsRemoteControl?: boolean;
userContext?: IUserContext;
}
export interface IUserContext {
id?: string;
name?: string;
}
export interface ILocalParticipant extends IParticipant {

View File

@@ -84,7 +84,7 @@ const useStyles = makeStyles()(theme => {
...theme.typography.bodyLongBold,
borderRadius: theme.shape.borderRadius,
boxSizing: 'border-box',
color: theme.palette.text01,
color: theme.palette.actionButtonText,
cursor: 'pointer',
display: 'inline-block',
marginBottom: '16px',
@@ -95,20 +95,20 @@ const useStyles = makeStyles()(theme => {
border: 0,
'&.primary': {
background: theme.palette.action01,
color: theme.palette.text01,
background: theme.palette.prejoinActionButtonPrimary,
color: theme.palette.prejoinActionButtonPrimaryText,
'&:hover': {
backgroundColor: theme.palette.action01Hover
backgroundColor: theme.palette.prejoinActionButtonPrimaryHover
}
},
'&.secondary': {
background: theme.palette.action02,
color: theme.palette.text04,
background: theme.palette.prejoinActionButtonSecondary,
color: theme.palette.prejoinActionButtonSecondaryText,
'&:hover': {
backgroundColor: theme.palette.action02Hover
backgroundColor: theme.palette.prejoinActionButtonSecondaryHover
}
},
@@ -120,7 +120,7 @@ const useStyles = makeStyles()(theme => {
},
'&.disabled': {
background: theme.palette.disabled01,
background: theme.palette.prejoinActionButtonDisabled,
border: '1px solid #5E6D7A',
color: '#AFB6BC',
cursor: 'initial',

View File

@@ -109,7 +109,7 @@ const useStyles = makeStyles()(theme => {
position: 'absolute',
inset: '0 0 0 0',
display: 'flex',
backgroundColor: theme.palette.ui01,
backgroundColor: theme.palette.preMeetingBackground,
zIndex: 252,
'@media (max-width: 720px)': {
@@ -163,7 +163,7 @@ const useStyles = makeStyles()(theme => {
},
title: {
...theme.typography.heading4,
color: `${theme.palette.text01}!important`,
color: theme.palette.prejoinTitleText,
marginBottom: theme.spacing(3),
textAlign: 'center',
@@ -179,7 +179,7 @@ const useStyles = makeStyles()(theme => {
roomName: {
...theme.typography.heading5,
color: theme.palette.text01,
color: theme.palette.prejoinRoomNameText,
display: 'inline-block',
overflow: 'hidden',
textOverflow: 'ellipsis',

View File

@@ -6,7 +6,7 @@ const useStyles = makeStyles()(theme => {
return {
warning: {
bottom: 0,
color: theme.palette.text03,
color: theme.palette.prejoinRecordingWarningText,
display: 'flex',
justifyContent: 'center',
...theme.typography.bodyShortRegular,

View File

@@ -11,8 +11,8 @@ import { setUnsafeRoomConsent } from '../../actions.web';
const useStyles = makeStyles()(theme => {
return {
warning: {
backgroundColor: theme.palette.warning01,
color: theme.palette.text04,
backgroundColor: theme.palette.prejoinWarningBackground,
color: theme.palette.prejoinWarningText,
...theme.typography.bodyShortRegular,
padding: theme.spacing(3),
borderRadius: theme.shape.borderRadius,

View File

@@ -9,11 +9,11 @@ import { getSupportUrl } from '../../functions';
const useStyles = makeStyles()(theme => {
return {
dialog: {
backgroundColor: theme.palette.ui01,
border: `1px solid ${theme.palette.ui04}`,
backgroundColor: theme.palette.dialogBackground,
border: `1px solid ${theme.palette.inlineDialogBorder}`,
borderRadius: `${Number(theme.shape.borderRadius)}px`,
boxShadow: '0px 1px 2px rgba(41, 41, 41, 0.25)',
color: theme.palette.text01,
color: theme.palette.dialogText,
...theme.typography.bodyShortRegular,
padding: `${theme.spacing(3)} 10`,
'& .retry-button': {

View File

@@ -2,6 +2,7 @@ import { batch } from 'react-redux';
import { IStore } from '../../app/types';
import { CHAT_SIZE } from '../../chat/constants';
import { getCustomPanelWidth } from '../../custom-panel/functions';
import { getParticipantsPaneWidth } from '../../participants-pane/functions';
import {
@@ -43,6 +44,7 @@ export function clientResized(clientWidth: number, clientHeight: number) {
if (navigator.product !== 'ReactNative') {
const state = getState();
const { reducedUIEnabled = true } = state['features/base/config'];
const { isOpen: isChatOpen, width } = state['features/chat'];
if (isChatOpen) {
@@ -50,8 +52,9 @@ export function clientResized(clientWidth: number, clientHeight: number) {
}
availableWidth -= getParticipantsPaneWidth(state);
availableWidth -= getCustomPanelWidth(state);
dispatch(setReducedUI(availableWidth, clientHeight));
reducedUIEnabled && dispatch(setReducedUI(availableWidth, clientHeight));
}
batch(() => {
@@ -112,7 +115,7 @@ export function setReducedUI(width: number, height: number) {
const threshold = navigator.product === 'ReactNative'
? REDUCED_UI_THRESHOLD
: WEB_REDUCED_UI_THRESHOLD;
const reducedUI = Math.min(width, height) < threshold;
const reducedUI = Math.max(width, height) < threshold;
if (reducedUI !== getState()['features/base/responsive-ui'].reducedUI) {
return dispatch({

View File

@@ -22,11 +22,11 @@ interface IProps {
const useStyles = makeStyles()(theme => {
return {
container: {
backgroundColor: theme.palette.uiBackground,
backgroundColor: theme.palette.tooltipBackground,
borderRadius: '3px',
padding: theme.spacing(2),
...theme.typography.labelRegular,
color: theme.palette.text01,
color: theme.palette.tooltipText,
position: 'relative',
'&.mounting-animation': {

View File

@@ -51,7 +51,7 @@ import './subscriber.web';
MiddlewareRegistry.register(store => next => action => {
switch (action.type) {
case TRACK_ADDED: {
const { local } = action.track;
const { local, jitsiTrack } = action.track;
// The devices list needs to be refreshed when no initial video permissions
// were granted and a local video track is added by umuting the video.
@@ -65,6 +65,16 @@ MiddlewareRegistry.register(store => next => action => {
if (participantId) {
logTracksForParticipant(store.getState()['features/base/tracks'], participantId, 'Track added');
// Fire participantMuted event for initial state of remote tracks
if (typeof action.track?.muted !== 'undefined' && jitsiTrack) {
const isVideoTrack = jitsiTrack.getType() !== MEDIA_TYPE.AUDIO;
const mediaType = isVideoTrack
? (jitsiTrack.getVideoType() === VIDEO_TYPE.DESKTOP ? 'desktop' : 'video')
: 'audio';
APP.API.notifyParticipantMuted(participantId, action.track.muted, mediaType);
}
}
return result;
@@ -119,6 +129,16 @@ MiddlewareRegistry.register(store => next => action => {
// TODO Remove the following calls to APP.UI once components interested
// in track mute changes are moved into React and/or redux.
const { jitsiTrack } = action.track;
const participantID = jitsiTrack.getParticipantId();
const isVideoTrack = jitsiTrack.type !== MEDIA_TYPE.AUDIO;
const local = jitsiTrack.isLocal();
// Get old muted state BEFORE updating
const tracks = store.getState()['features/base/tracks'];
const oldTrack = tracks.find((t: ITrack) => t.jitsiTrack === jitsiTrack);
const oldMutedState = oldTrack?.muted;
const result = next(action);
const state = store.getState();
@@ -126,11 +146,6 @@ MiddlewareRegistry.register(store => next => action => {
return result;
}
const { jitsiTrack } = action.track;
const participantID = jitsiTrack.getParticipantId();
const isVideoTrack = jitsiTrack.type !== MEDIA_TYPE.AUDIO;
const local = jitsiTrack.isLocal();
if (isVideoTrack) {
if (local && !(jitsiTrack.getVideoType() === VIDEO_TYPE.DESKTOP)) {
APP.conference.setVideoMuteStatus();
@@ -144,12 +159,14 @@ MiddlewareRegistry.register(store => next => action => {
if (typeof action.track?.muted !== 'undefined' && participantID && !local) {
logTracksForParticipant(store.getState()['features/base/tracks'], participantID, 'Track updated');
// Notify external API when remote participant mutes/unmutes themselves
const mediaType = isVideoTrack
? (jitsiTrack.getVideoType() === VIDEO_TYPE.DESKTOP ? 'desktop' : 'video')
: 'audio';
// Fire participantMuted event only if muted state actually changed
if (oldMutedState !== action.track.muted) {
const mediaType = isVideoTrack
? (jitsiTrack.getVideoType() === VIDEO_TYPE.DESKTOP ? 'desktop' : 'video')
: 'audio';
APP.API.notifyParticipantMuted(participantID, action.track.muted, mediaType, true);
APP.API.notifyParticipantMuted(participantID, action.track.muted, mediaType);
}
}
return result;

View File

@@ -4,7 +4,7 @@ import { isEqual, sortBy } from 'lodash-es';
import VideoLayout from '../../../../modules/UI/videolayout/VideoLayout';
import { getAutoPinSetting } from '../../video-layout/functions.any';
import { MEDIA_TYPE } from '../media/constants';
import { getScreenshareParticipantIds } from '../participants/functions';
import { getLocalParticipant, getScreenshareParticipantIds } from '../participants/functions';
import StateListenerRegistry from '../redux/StateListenerRegistry';
import { isLocalTrackMuted } from './functions';
@@ -47,6 +47,13 @@ StateListenerRegistry.register(
/* listener */ (muted, store, previousMuted) => {
if (muted !== previousMuted) {
APP.API.notifyVideoMutedStatusChanged(muted);
// Also fire the participantMuted event for consistency
const localParticipant = getLocalParticipant(store.getState());
if (localParticipant) {
APP.API.notifyParticipantMuted(localParticipant.id, muted, 'video');
}
}
}
);

View File

@@ -1,3 +1,4 @@
/* eslint-disable @stylistic/no-multi-spaces */
// Mapping between the token used and the color
export const colorMap = {
// ----- Surfaces -----
@@ -7,7 +8,7 @@ export const colorMap = {
// - JitsiMeetView.java
uiBackground: 'surface01',
// Container backgrounds
// Container backgrounds (legacy tokens)
ui01: 'surface02',
ui02: 'surface03',
ui03: 'ui02',
@@ -47,6 +48,426 @@ export const colorMap = {
// Focus
focus01: 'focus01',
// ----- Semantic Tokens (component-based, backwards compatible) -----
// Dialog/Modal Components
dialogBackground: 'surface02', // Main dialog background (same as ui01)
dialogOverlay: 'surface03', // Overlay/backdrop (same as ui02)
dialogBorder: 'ui02', // Dialog borders
dialogText: 'textColor01', // Primary dialog text (same as text01)
dialogSecondaryText: 'textColor02', // Secondary dialog text (same as text02)
// Large Video
largeVideoBackground: 'surface03', // Main video area background (same as ui02)
largeVideoPlaceholder: 'surface03', // Placeholder when no video (same as ui02)
// Filmstrip
filmstripBackground: 'surface03', // Filmstrip container background (same as ui02)
filmstripBackgroundHover: 'uiBackground', // Filmstrip background on hover/focus
filmstripDragHandle: 'icon02', // Filmstrip resize drag handle color
filmstripDragHandleHover: 'icon01', // Filmstrip resize drag handle hover color
thumbnailBackground: 'surface03', // Individual thumbnail background (same as ui02)
thumbnailBorder: 'ui03', // Thumbnail borders (same as ui03)
thumbnailHover: 'hover05', // Thumbnail hover state (same as action03Hover)
thumbnailTintBackground: 'uiBackground', // Thumbnail tint overlay background
thumbnailRaisedHandIcon: 'uiBackground', // Thumbnail raised hand indicator icon
thumbnailVideoBackground: 'uiBackground', // Thumbnail video/placeholder background
// Chat
chatBackground: 'surface02', // Chat panel background (same as ui01)
chatBackdrop: 'ui04', // Chat screen background (same as ui10)
chatEmptyText: 'ui03', // Empty component text
chatInputBackground: 'surface03', // Chat input field background (same as ui02)
chatInputBorder: 'surface03', // Chat input border (same as ui02)
chatLink: 'action01', // Chat link color (same as link01)
chatLobbyMessageBubble: 'support06', // Lobby message bubble background
chatLobbyMessageNotice: 'surface01', // Lobby message notice text
chatLobbyRecipientContainer: 'support06', // Lobby recipient container background
chatMessageLocal: 'surface05', // Local participant message bubble (same as ui04)
chatMessagePrivate: 'support05', // Private/DM message bubble
chatMessageRemote: 'surface03', // Remote participant message bubble (same as ui02)
chatMessageText: 'textColor01', // Chat message text
chatPrivateNotice: 'textColor02', // Private message notice text
chatRecipientCancelIcon: 'icon01', // Recipient cancel icon color
chatRecipientContainer: 'support05', // Recipient container background
chatRecipientText: 'textColor01', // Recipient text color
chatReplyIcon: 'icon01', // Reply icon color
chatSenderName: 'textColor02', // Sender display name color
chatTimestamp: 'ui03', // Chat timestamp text
// Toolbox/Toolbar
toolboxBackground: 'surface02', // Main toolbox background
drawerBackground: 'surface02', // Drawer/side panel background
toolboxIconHover: 'surface05', // Toolbox icon hover background
toolboxIconActive: 'ui02', // Toolbox icon active/pressed background
toolboxIconToggled: 'ui02', // Toolbox icon toggled background
toolbarButton: 'action01', // Toolbar button color
toolbarButtonHover: 'hover01', // Toolbar button hover (same as action01Hover)
toolbarButtonActive: 'active01', // Toolbar button active/pressed state
toolbarIcon: 'icon01', // Toolbar icon color
toolbarIconHover: 'icon01', // Toolbar icon hover state
toolbarIconActive: 'action01', // Toolbar icon active/toggled state
// Overflow Menu (More Actions)
overflowMenuBackground: 'surface02', // Overflow menu background
overflowMenuBorder: 'surface05', // Overflow menu border
overflowMenuItemText: 'text01', // Overflow menu item text
overflowMenuItemIcon: 'text01', // Overflow menu item icon
overflowMenuItemHover: 'surface03', // Overflow menu item hover background
overflowMenuItemDisabled: 'text03', // Overflow menu item disabled text/icon
overflowMenuSeparator: 'ui03', // Overflow menu group separator
// Participants Pane
participantsPaneBackground: 'surface02', // Participants list background
participantItemBackground: 'surface03', // Individual participant item background
participantItemHover: 'hover05', // Participant item hover
participantItemBorder: 'ui02', // Participant item border
participantCounterBadge: 'ui02', // Participant counter badge background
participantCounterText: 'text01', // Participant counter text
participantModeratorLabel: 'text03', // Moderator label text
participantSectionText: 'text02', // Section header/subtitle text
participantActionButton: 'action02', // Action button background
participantLinkText: 'link01', // Link text color
participantWarningText: 'warning02', // Warning text color
participantRaisedHandBadge: 'warning02', // Raised hand indicator background
participantRaisedHandIcon: 'icon04', // Raised hand icon color
// Lobby
lobbyBackground: 'surface02', // Lobby screen background (same as ui01)
lobbyPreviewBackground: 'surface03', // Video preview background (same as ui02)
// Speaker Stats
speakerStatsBackground: 'surface02', // Speaker stats panel background
speakerStatsRowBackground: 'ui02', // Individual stat row background
speakerStatsRowAlternate: 'ui03', // Alternate row background
speakerStatsBorder: 'surface03', // Speaker stats borders
speakerStatsHeaderBackground: 'ui09', // Header background
speakerStatsSearchBackground: 'field01', // Search input background
speakerStatsSearchBorder: 'ui05', // Search input border
speakerStatsSearchText: 'text01', // Search input text
speakerStatsSearchPlaceholder: 'text03', // Search placeholder
speakerStatsSearchIcon: 'icon03', // Search icon color
speakerStatsLabelText: 'text03', // Label text color
speakerStatsSuccessBar: 'success02', // Success/progress bar
speakerStatsAvatarLeft: 'surface05', // Avatar background for participants who left
// Pre-meeting/Prejoin
preMeetingBackground: 'surface02', // Pre-meeting screen container background
preMeetingPreview: 'ui01', // Video preview in pre-meeting
prejoinDialogBackground: 'uiBackground', // Prejoin dialog background
prejoinDialogDelimiter: 'ui03', // Prejoin dialog delimiter line
prejoinDialogDelimiterText: 'text01', // Prejoin dialog delimiter text
prejoinTitleText: 'text01', // Prejoin title text color
prejoinRoomNameText: 'text01', // Prejoin room name text color
prejoinWarningBackground: 'warning01', // Warning banner background
prejoinWarningText: 'text04', // Warning banner text
prejoinRecordingWarningText: 'text03', // Recording warning text
prejoinActionButtonPrimary: 'action01', // Primary action button
prejoinActionButtonPrimaryHover: 'action01Hover', // Primary button hover
prejoinActionButtonPrimaryText: 'text01', // Primary button text
prejoinActionButtonSecondary: 'action02', // Secondary action button
prejoinActionButtonSecondaryHover: 'action02Hover', // Secondary button hover
prejoinActionButtonSecondaryText: 'text04', // Secondary button text
prejoinActionButtonDanger: 'actionDanger', // Danger button (leave)
prejoinActionButtonDisabled: 'disabled01', // Disabled button
prejoinCountryPickerBackground: 'ui01', // Country picker background
prejoinCountryPickerBorder: 'ui03', // Country picker border
prejoinCountryPickerText: 'text01', // Country picker text
prejoinCountryRowBackground: 'action03', // Country row background
prejoinCountryRowHover: 'action03Hover', // Country row hover
prejoinDeviceStatusOk: 'success01', // Device status OK background
prejoinDeviceStatusWarning: 'warning01', // Device status warning background
prejoinDeviceStatusText: 'uiBackground', // Device status text
// Notifications
notificationBackground: 'ui04', // Notification background
notificationNormalIcon: 'action01', // Normal notification icon
notificationError: 'iconError', // Error notification icon
notificationSuccess: 'success01', // Success notification icon
notificationWarning: 'warning01', // Warning notification icon
notificationText: 'text04', // Notification text
notificationActionText: 'action01', // Notification action text
notificationErrorText: 'textError', // Error notification text
notificationActionFocus: 'action01', // Notification action focus outline
notificationCloseIcon: 'icon04', // Notification close icon
// Forms/Inputs
inputBackground: 'field01', // Input field background
inputBorder: 'surface03', // Input field border (same as ui02)
inputText: 'textColor01', // Input field text (same as text01)
inputPlaceholder: 'textColor02', // Input placeholder text (same as text02)
// Breakout Rooms
breakoutRoomBackground: 'ui01', // Breakout rooms panel background
breakoutRoomItemBackground: 'surface03', // Individual breakout room background
breakoutRoomArrowBackground: 'ui02', // Breakout room arrow container background
// Settings
settingsBackground: 'ui01', // Settings dialog background
settingsSectionBackground: 'ui02', // Settings section background
settingsTabText: 'text01', // Settings tab text
settingsShortcutKey: 'surface05', // Keyboard shortcut key background
settingsVideoPreviewBorder: 'action01Hover', // Video preview border (selected)
settingsErrorIcon: 'iconError', // Error icon color
// Visitors
visitorsCountBadge: 'warning02', // Visitors count badge background
visitorsCountText: 'uiBackground', // Visitors count badge text
visitorsCountIcon: 'icon04', // Visitors count icon
visitorsQueueBackground: 'ui01', // Visitors queue panel background
visitorsQueueText: 'text01', // Visitors queue text
visitorsArrowBackground: 'ui02', // Visitors arrow container background
// Welcome Page
welcomeBackground: 'surface01', // Welcome page background (same as uiBackground)
welcomeCard: 'ui01', // Welcome page tab bar background
welcomeTabActive: 'icon01', // Welcome page active tab icon
welcomeTabInactive: 'icon03', // Welcome page inactive tab icon
// ----- Form Components -----
// Input
inputLabel: 'text01', // Input field label text
inputFieldBackground: 'ui02', // Input field background color
inputFieldBorder: 'ui02', // Input field border color
inputFieldText: 'text01', // Input field text color
inputFieldPlaceholder: 'text02', // Input field placeholder text
inputFieldDisabled: 'text03', // Input field disabled text
inputFieldError: 'textError', // Input field error state
inputFieldFocus: 'focus01', // Input field focus outline
inputClearButton: 'transparent', // Input clear button background
inputBottomLabel: 'text02', // Input bottom label text
inputBottomLabelError: 'textError', // Input bottom label error text
// Select
selectLabel: 'text01', // Select label text
selectBackground: 'ui02', // Select background color
selectText: 'text01', // Select text color
selectDisabled: 'text03', // Select disabled text
selectError: 'textError', // Select error state
selectFocus: 'focus01', // Select focus outline
selectIcon: 'icon01', // Select dropdown icon (enabled)
selectIconDisabled: 'icon03', // Select dropdown icon (disabled)
selectBottomLabel: 'text02', // Select bottom label text
selectBottomLabelError: 'textError', // Select bottom label error text
// MultiSelect
multiSelectBackground: 'ui01', // MultiSelect dropdown background
multiSelectBorder: 'ui04', // MultiSelect dropdown border
multiSelectItemText: 'text01', // MultiSelect item text
multiSelectItemHover: 'ui02', // MultiSelect item hover background
multiSelectItemDisabled: 'text03', // MultiSelect disabled item text
// Checkbox
checkboxLabel: 'text01', // Checkbox label text
checkboxBorder: 'icon03', // Checkbox border color
checkboxChecked: 'action01', // Checkbox checked background
checkboxDisabledBackground: 'ui02', // Checkbox disabled background
checkboxDisabledBorder: 'surface05', // Checkbox disabled border
checkboxDisabledChecked: 'ui02', // Checkbox disabled checked background
checkboxIcon: 'icon01', // Checkbox check icon (enabled)
checkboxIconDisabled: 'icon03', // Checkbox check icon (disabled)
// Switch
switchBackground: 'ui01', // Switch background (unchecked)
switchBackgroundOn: 'action01', // Switch background (checked)
switchToggle: 'ui04', // Switch toggle circle
switchToggleDisabled: 'ui03', // Switch toggle circle (disabled)
switchFocus: 'focus01', // Switch focus outline
// Tabs
tabText: 'text02', // Tab text (unselected)
tabTextHover: 'text01', // Tab text (hover)
tabTextSelected: 'text01', // Tab text (selected)
tabTextDisabled: 'text03', // Tab text (disabled)
tabBorder: 'ui05', // Tab bottom border (unselected)
tabBorderHover: 'ui10', // Tab bottom border (hover)
tabBorderSelected: 'action01', // Tab bottom border (selected)
tabBorderDisabled: 'ui05', // Tab bottom border (disabled)
tabFocus: 'focus01', // Tab focus outline
tabBadgeBackground: 'warning01', // Tab count badge background
tabBadgeText: 'text04', // Tab count badge text
// ListItem
listItemText: 'text01', // List item text color
listItemBackground: 'ui01', // List item default background
listItemHover: 'surface03', // List item hover background
listItemHighlighted: 'surface03', // List item highlighted/active background
listItemBoxShadow: 'ui02', // List item actions box shadow color
// ClickableIcon
clickableIconBackground: 'transparent', // Clickable icon background
clickableIconHover: 'ui02', // Clickable icon hover background
clickableIconActive: 'ui03', // Clickable icon active/pressed background
clickableIconFocus: 'focus01', // Clickable icon focus outline
// Label
labelBackground: 'ui04', // Label default background
labelText: 'text01', // Label text color
labelWhiteBackground: 'ui08', // Label white variant background
labelWhiteText: 'text04', // Label white variant text
labelWhiteIcon: 'surface01', // Label white variant icon
// Tooltip
tooltipBackground: 'uiBackground', // Tooltip background color
tooltipText: 'text01', // Tooltip text color
// Polls
pollsBackground: 'surface03', // Poll container background
pollsTitle: 'text01', // Poll title text
pollsSubtitle: 'text02', // Poll subtitle/secondary text
pollsQuestion: 'text01', // Poll question text
pollsAnswer: 'text01', // Poll answer text
pollsBarBackground: 'ui03', // Poll results bar background
pollsBarPercentage: 'text01', // Poll results percentage text
pollsVotersBackground: 'ui03', // Poll voters list background
pollsVotersText: 'text01', // Poll voters list text
pollsSeparator: 'ui03', // Poll section separator
pollsSendLabel: 'text01', // Poll send button label
pollsSendDisabled: 'text03', // Poll send button disabled label
pollsPaneBackground: 'ui01', // Poll pane container background
pollsPaneBorder: 'ui05', // Poll pane border
pollsCreateBackground: 'uiBackground', // Poll create dialog background
pollsCreateBorder: 'ui06', // Poll create dialog border
// Video Quality / Slider
sliderKnob: 'text01', // Slider knob/thumb color
sliderTrack: 'text03', // Slider track color
sliderFocus: 'ui06', // Slider focus outline
videoQualityText: 'text01', // Video quality dialog text
videoQualityBackground: 'surface02', // Video quality dialog background
// Connection Indicator
connectionIndicatorLost: 'ui05', // Connection indicator lost status
connectionIndicatorOther: 'action01', // Connection indicator other status
// Device Selection
deviceSelectorBackground: 'ui01', // Device selector background
deviceSelectorText: 'text01', // Device selector text
deviceSelectorBorder: 'ui03', // Device selector border
deviceSelectorTextBackground: 'uiBackground', // Device selector text-only background
deviceSelectorVideoPreview: 'uiBackground', // Device selector video preview background
// Invite / Dial-in
dialInBackground: 'ui01', // Dial-in summary background
dialInText: 'text01', // Dial-in summary text
dialInSecondaryText: 'text02', // Dial-in summary secondary text
// Reactions
reactionsMenuBackground: 'ui01', // Reactions menu background
reactionsMenuBorder: 'ui02', // Reactions menu border
// Recording / Live Stream
recordingBackground: 'ui01', // Recording panel background
recordingText: 'text01', // Recording panel text
recordingHighlightButton: 'ui04', // Recording highlight button background
recordingHighlightButtonDisabled: 'text02', // Recording highlight button disabled background
recordingHighlightButtonIcon: 'ui02', // Recording highlight button icon color
recordingHighlightButtonIconDisabled: 'text03', // Recording highlight button disabled icon color
recordingNotificationText: 'surface01', // Recording notification text color
recordingNotificationAction: 'action01', // Recording notification action color
// Virtual Background
virtualBackgroundBackground: 'ui01', // Virtual background picker background
virtualBackgroundText: 'text01', // Virtual background picker text
virtualBackgroundBorder: 'ui03', // Virtual background item border
virtualBackgroundPreview: 'uiBackground', // Virtual background preview container
// Conference / Meeting
conferenceTimerText: 'text01', // Conference timer text
conferenceSubjectText: 'text01', // Conference subject text
conferenceNoticeBackground: 'uiBackground', // Conference notice background
conferenceNoticeText: 'text01', // Conference notice text
conferenceRaisedHandLabelText: 'uiBackground', // Raised hands count label text
conferenceRaisedHandLabelIcon: 'surface01', // Raised hands count label icon
// Subtitle Messages
subtitleMessageBackground: 'ui02', // Subtitle message background
subtitleMessageText: 'text01', // Subtitle message text
subtitleMessageSender: 'text02', // Subtitle message sender name
subtitleMessageTime: 'text03', // Subtitle message timestamp
// Language Selector
languageSelectorBackground: 'ui01', // Language selector background
languageSelectorText: 'text01', // Language selector text
languageSelectorHover: 'ui02', // Language selector item hover
// Video Menu
videoMenuBackground: 'ui01', // Video menu background
videoMenuBorder: 'ui02', // Video menu border
videoMenuText: 'text01', // Video menu text
videoMenuSliderBackground: 'ui03', // Video menu slider background
// File Sharing
fileSharingBackground: 'ui01', // File sharing panel background
fileSharingText: 'text01', // File sharing text
fileSharingEmptyText: 'text02', // File sharing empty state text
fileSharingEmptyIcon: 'icon03', // File sharing empty state icon
fileSharingItemBackground: 'surface03', // File sharing item background
fileSharingItemBorder: 'ui02', // File sharing item hover/border
// Gifs
gifsBackground: 'ui01', // GIFs panel background
gifsText: 'text01', // GIFs panel text
// Whiteboard
whiteboardBackground: 'ui03', // Whiteboard background
whiteboardText: 'text01', // Whiteboard panel text
// Salesforce
salesforceSearchBackground: 'field01', // Salesforce search input background
salesforceSearchBorder: 'ui05', // Salesforce search input border
salesforceSearchText: 'dialogText', // Salesforce search input text
salesforceSearchPlaceholder: 'text03', // Salesforce search placeholder
salesforceSearchIcon: 'text03', // Salesforce search icon
// Security Dialog
securityDialogBackground: 'ui01', // Security dialog background
securityDialogText: 'text01', // Security dialog text
securityDialogSecondaryText: 'text02', // Security dialog secondary text
securityDialogBorder: 'ui07', // Security dialog border color
// Deep Linking
deepLinkingBackground: 'ui01', // Deep linking page content pane background
deepLinkingBorder: 'ui03', // Deep linking page content pane border
deepLinkingText: 'text01', // Deep linking page text
deepLinkingSeparator: 'ui03', // Deep linking separator line
deepLinkingLabelText: 'text02', // Deep linking label text
deepLinkingLink: 'link01', // Deep linking link color
// Base React Components
baseReactBackground: 'ui01', // Base react component background
baseReactText: 'text01', // Base react component text
baseReactBorder: 'ui03', // Base react component border
// Inline Dialog
inlineDialogBackground: 'ui01', // Inline dialog background
inlineDialogText: 'text01', // Inline dialog text
inlineDialogBorder: 'ui02', // Inline dialog border
// Pre-meeting / Action Button
actionButtonBackground: 'ui01', // Action button background (different from main buttons)
actionButtonText: 'text01', // Action button text
actionButtonBorder: 'ui03', // Action button border
// Audio Route Picker
audioRoutePickerBackground: 'ui01', // Audio route picker background
audioRoutePickerText: 'text01', // Audio route picker text
audioRoutePickerBorder: 'ui03', // Audio route picker border
// Etherpad
etherpadBackground: 'ui01', // Etherpad panel background
etherpadText: 'text01', // Etherpad panel text
// Display Name
displayNameBackground: 'ui01', // Display name background
displayNameText: 'text01', // Display name text
// Car Mode
carModeBackground: 'ui01', // Car mode background
carModeText: 'text01', // Car mode text
carModeBorder: 'ui03', // Car mode border
// ----- Links -----
link01: 'action01',

View File

@@ -14,7 +14,7 @@ const useStyles = makeStyles()(theme => {
width: '100%',
height: '100%',
position: 'fixed',
color: theme.palette.text01,
color: theme.palette.dialogText,
...theme.typography.bodyLongRegular,
top: 0,
left: 0,
@@ -49,13 +49,13 @@ const useStyles = makeStyles()(theme => {
height: '100%',
top: 0,
left: 0,
backgroundColor: theme.palette.ui02,
backgroundColor: theme.palette.dialogOverlay,
opacity: 0.75
},
modal: {
backgroundColor: theme.palette.ui01,
border: `1px solid ${theme.palette.ui03}`,
backgroundColor: theme.palette.dialogBackground,
border: `1px solid ${theme.palette.dialogBorder}`,
boxShadow: '0px 4px 25px 4px rgba(20, 20, 20, 0.6)',
borderRadius: `${theme.shape.borderRadius}px`,
display: 'flex',

View File

@@ -47,7 +47,7 @@ const useStyles = makeStyles()(theme => {
return {
formControl: {
...theme.typography.bodyLongRegular,
color: theme.palette.text01,
color: theme.palette.checkboxLabel,
display: 'inline-flex',
alignItems: 'center',
@@ -76,10 +76,10 @@ const useStyles = makeStyles()(theme => {
backgroundColor: 'transparent',
margin: '3px',
font: 'inherit',
color: theme.palette.icon03,
color: theme.palette.checkboxBorder,
width: '18px',
height: '18px',
border: `2px solid ${theme.palette.icon03}`,
border: `2px solid ${theme.palette.checkboxBorder}`,
borderRadius: '3px',
display: 'grid',
@@ -90,7 +90,7 @@ const useStyles = makeStyles()(theme => {
width: '18px',
height: '18px',
opacity: 0,
backgroundColor: theme.palette.action01,
backgroundColor: theme.palette.checkboxChecked,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
@@ -104,11 +104,11 @@ const useStyles = makeStyles()(theme => {
},
'&:disabled': {
backgroundColor: theme.palette.ui03,
borderColor: theme.palette.ui04,
backgroundColor: theme.palette.checkboxDisabledBackground,
borderColor: theme.palette.checkboxDisabledBorder,
'&::before': {
backgroundColor: theme.palette.ui04
backgroundColor: theme.palette.checkboxDisabledChecked
}
},
@@ -173,7 +173,7 @@ const Checkbox = ({
<Icon
aria-hidden = { true }
className = 'checkmark'
color = { disabled ? theme.palette.icon03 : theme.palette.icon01 }
color = { disabled ? theme.palette.checkboxIconDisabled : theme.palette.checkboxIcon }
size = { 18 }
src = { IconCheck } />
</div>

View File

@@ -16,22 +16,22 @@ const useStyles = makeStyles()(theme => {
return {
button: {
padding: '2px',
backgroundColor: theme.palette.action03,
backgroundColor: theme.palette.clickableIconBackground,
border: 0,
outline: 0,
borderRadius: `${theme.shape.borderRadius}px`,
'&:hover': {
backgroundColor: theme.palette.ui02
backgroundColor: theme.palette.clickableIconHover
},
'&.focus-visible': {
outline: 0,
boxShadow: `0px 0px 0px 2px ${theme.palette.focus01}`
boxShadow: `0px 0px 0px 2px ${theme.palette.clickableIconFocus}`
},
'&:active': {
backgroundColor: theme.palette.ui03
backgroundColor: theme.palette.clickableIconActive
},
'&.is-mobile': {

View File

@@ -133,11 +133,11 @@ const MAX_HEIGHT = 400;
const useStyles = makeStyles()(theme => {
return {
contextMenu: {
backgroundColor: theme.palette.ui01,
border: `1px solid ${theme.palette.ui04}`,
backgroundColor: theme.palette.overflowMenuBackground,
border: `1px solid ${theme.palette.overflowMenuBorder}`,
borderRadius: `${Number(theme.shape.borderRadius)}px`,
boxShadow: '0px 1px 2px rgba(41, 41, 41, 0.25)',
color: theme.palette.text01,
color: theme.palette.overflowMenuItemText,
...theme.typography.bodyShortRegular,
marginTop: '48px',
position: 'absolute',

View File

@@ -122,11 +122,11 @@ const useStyles = makeStyles()(theme => {
},
'&:hover': {
backgroundColor: theme.palette.ui02
backgroundColor: theme.palette.overflowMenuItemHover
},
'&:active': {
backgroundColor: theme.palette.ui03
backgroundColor: theme.palette.overflowMenuItemHover
},
'&.focus-visible': {
@@ -137,7 +137,7 @@ const useStyles = makeStyles()(theme => {
selected: {
borderLeft: `3px solid ${theme.palette.action01Hover}`,
paddingLeft: '13px',
backgroundColor: theme.palette.ui02
backgroundColor: theme.palette.overflowMenuItemHover
},
contextMenuItemDisabled: {
@@ -146,19 +146,19 @@ const useStyles = makeStyles()(theme => {
contextMenuItemIconDisabled: {
'& svg': {
fill: `${theme.palette.text03} !important`
fill: `${theme.palette.overflowMenuItemDisabled} !important`
}
},
contextMenuItemLabelDisabled: {
color: theme.palette.text03,
color: theme.palette.overflowMenuItemDisabled,
'&:hover': {
background: 'none'
},
'& svg': {
fill: theme.palette.text03
fill: theme.palette.overflowMenuItemDisabled
}
},
@@ -168,13 +168,13 @@ const useStyles = makeStyles()(theme => {
contextMenuItemIcon: {
'& svg': {
fill: theme.palette.icon01
fill: theme.palette.overflowMenuItemIcon
}
},
text: {
...theme.typography.bodyShortRegular,
color: theme.palette.text01
color: theme.palette.overflowMenuItemText
},
drawerText: {

View File

@@ -30,7 +30,7 @@ const useStyles = makeStyles()(theme => {
},
'& + &:not(:empty)': {
borderTop: `1px solid ${theme.palette.ui03}`
borderTop: `1px solid ${theme.palette.overflowMenuSeparator}`
},
'&:first-of-type': {

View File

@@ -24,7 +24,7 @@ const useStyles = makeStyles()(theme => {
},
title: {
color: theme.palette.text01,
color: theme.palette.dialogText,
...theme.typography.heading5,
margin: 0,
padding: 0

View File

@@ -42,7 +42,7 @@ const useStyles = makeStyles()(theme => {
flexDirection: 'column',
minWidth: '211px',
maxWidth: '100%',
borderRight: `1px solid ${theme.palette.ui03}`,
borderRight: `1px solid ${theme.palette.dialogBorder}`,
[`@media (max-width: ${MOBILE_BREAKPOINT}px)`]: {
width: '100%',
@@ -70,7 +70,7 @@ const useStyles = makeStyles()(theme => {
title: {
...theme.typography.heading5,
color: `${theme.palette.text01} !important`,
color: `${theme.palette.dialogText} !important`,
margin: 0,
padding: 0
},
@@ -301,7 +301,7 @@ const DialogWithTabs = ({
}
return null;
}, [ selectedTabIndex, tabStates ]);
}, [ selectedTabIndex, tabStates, tabs ]);
const closeIcon = useMemo(() => (
<ClickableIcon

View File

@@ -49,7 +49,7 @@ const useStyles = makeStyles()(theme => {
},
label: {
color: theme.palette.text01,
color: theme.palette.inputLabel,
...theme.typography.bodyShortRegular,
marginBottom: theme.spacing(2),
@@ -64,9 +64,9 @@ const useStyles = makeStyles()(theme => {
},
input: {
backgroundColor: theme.palette.ui03,
background: theme.palette.ui03,
color: theme.palette.text01,
backgroundColor: theme.palette.inputFieldBackground,
background: theme.palette.inputFieldBackground,
color: theme.palette.inputFieldText,
...theme.typography.bodyShortRegular,
padding: '10px 16px',
borderRadius: theme.shape.borderRadius,
@@ -76,16 +76,16 @@ const useStyles = makeStyles()(theme => {
width: '100%',
'&::placeholder': {
color: theme.palette.text02
color: theme.palette.inputFieldPlaceholder
},
'&:focus': {
outline: 0,
boxShadow: `0px 0px 0px 2px ${theme.palette.focus01}`
boxShadow: `0px 0px 0px 2px ${theme.palette.inputFieldFocus}`
},
'&:disabled': {
color: theme.palette.text03
color: theme.palette.inputFieldDisabled
},
'&.is-mobile': {
@@ -99,7 +99,7 @@ const useStyles = makeStyles()(theme => {
},
'&.error': {
boxShadow: `0px 0px 0px 2px ${theme.palette.textError}`
boxShadow: `0px 0px 0px 2px ${theme.palette.inputFieldError}`
},
'&.clearable-input': {
paddingRight: '46px'
@@ -107,12 +107,12 @@ const useStyles = makeStyles()(theme => {
},
'input::-webkit-outer-spin-button, input::-webkit-inner-spin-button': {
'-webkit-appearance': 'none',
WebkitAppearance: 'none',
margin: 0
},
'input[type=number]': {
'-moz-appearance': 'textfield'
MozAppearance: 'textfield'
},
icon: {
@@ -131,7 +131,7 @@ const useStyles = makeStyles()(theme => {
right: '16px',
top: '10px',
cursor: 'pointer',
backgroundColor: theme.palette.action03,
backgroundColor: theme.palette.inputClearButton,
border: 0,
padding: 0
},
@@ -139,14 +139,14 @@ const useStyles = makeStyles()(theme => {
bottomLabel: {
marginTop: theme.spacing(2),
...theme.typography.labelRegular,
color: theme.palette.text02,
color: theme.palette.inputBottomLabel,
'&.is-mobile': {
...theme.typography.bodyShortRegular
},
'&.error': {
color: theme.palette.textError
color: theme.palette.inputBottomLabelError
}
}
};

View File

@@ -83,7 +83,7 @@ const useStyles = makeStyles()(theme => {
return {
container: {
alignItems: 'center',
color: theme.palette.text01,
color: theme.palette.listItemText,
display: 'flex',
...theme.typography.bodyShortBold,
margin: `0 -${participantsPaneTheme.panePadding}px`,
@@ -93,7 +93,7 @@ const useStyles = makeStyles()(theme => {
minHeight: '40px',
'&:hover, &:focus-within': {
backgroundColor: theme.palette.ui02,
backgroundColor: theme.palette.listItemHover,
'& .indicators': {
display: 'none'
@@ -103,8 +103,8 @@ const useStyles = makeStyles()(theme => {
display: 'flex',
position: 'relative',
top: 'auto',
boxShadow: `-15px 0px 10px -5px ${theme.palette.ui02}`,
backgroundColor: theme.palette.ui02
boxShadow: `-15px 0px 10px -5px ${theme.palette.listItemBoxShadow}`,
backgroundColor: theme.palette.listItemHover
}
},
@@ -115,14 +115,14 @@ const useStyles = makeStyles()(theme => {
},
highlighted: {
backgroundColor: theme.palette.ui02,
backgroundColor: theme.palette.listItemHighlighted,
'& .actions': {
display: 'flex',
position: 'relative',
top: 'auto',
boxShadow: `-15px 0px 10px -5px ${theme.palette.ui02}`,
backgroundColor: theme.palette.ui02
boxShadow: `-15px 0px 10px -5px ${theme.palette.listItemBoxShadow}`,
backgroundColor: theme.palette.listItemHighlighted
}
},
@@ -170,20 +170,20 @@ const useStyles = makeStyles()(theme => {
actionsContainer: {
position: 'absolute',
top: '-1000px',
boxShadow: `-15px 0px 10px -5px ${theme.palette.ui02}`,
backgroundColor: theme.palette.ui02
boxShadow: `-15px 0px 10px -5px ${theme.palette.listItemBoxShadow}`,
backgroundColor: theme.palette.listItemHover
},
actionsPermanent: {
display: 'flex',
boxShadow: `-15px 0px 10px -5px ${theme.palette.ui01}`,
backgroundColor: theme.palette.ui01
boxShadow: `-15px 0px 10px -5px ${theme.palette.listItemBackground}`,
backgroundColor: theme.palette.listItemBackground
},
actionsVisible: {
display: 'flex',
boxShadow: `-15px 0px 10px -5px ${theme.palette.ui02}`,
backgroundColor: theme.palette.ui02
boxShadow: `-15px 0px 10px -5px ${theme.palette.listItemBoxShadow}`,
backgroundColor: theme.palette.listItemHighlighted
}
};
});

View File

@@ -38,8 +38,8 @@ const useStyles = makeStyles()(theme => {
},
marginTop: theme.spacing(2),
width: '100%',
backgroundColor: theme.palette.ui01,
border: `1px solid ${theme.palette.ui04}`,
backgroundColor: theme.palette.multiSelectBackground,
border: `1px solid ${theme.palette.multiSelectBorder}`,
borderRadius: `${Number(theme.shape.borderRadius)}px`,
...theme.typography.bodyShortRegular,
zIndex: 2,
@@ -57,7 +57,7 @@ const useStyles = makeStyles()(theme => {
inlineSize: 'calc(100% - 38px)',
overflowWrap: 'break-word',
marginLeft: theme.spacing(2),
color: theme.palette.text01,
color: theme.palette.multiSelectItemText,
'&.with-remove': {
// 60px because of the icon before the content and the remove button
inlineSize: 'calc(100% - 60px)',
@@ -76,15 +76,15 @@ const useStyles = makeStyles()(theme => {
cursor: 'pointer',
padding: `10px ${theme.spacing(3)}`,
'&:hover': {
backgroundColor: theme.palette.ui02
backgroundColor: theme.palette.multiSelectItemHover
}
},
'&.disabled': {
cursor: 'not-allowed',
'&:hover': {
backgroundColor: theme.palette.ui01
backgroundColor: theme.palette.multiSelectBackground
},
color: theme.palette.text03
color: theme.palette.multiSelectItemDisabled
}
},
errorMessage: {

View File

@@ -70,7 +70,7 @@ const useStyles = makeStyles()(theme => {
},
label: {
color: theme.palette.text01,
color: theme.palette.selectLabel,
...theme.typography.bodyShortRegular,
marginBottom: theme.spacing(2),
@@ -84,11 +84,11 @@ const useStyles = makeStyles()(theme => {
},
select: {
backgroundColor: theme.palette.ui03,
backgroundColor: theme.palette.selectBackground,
borderRadius: `${theme.shape.borderRadius}px`,
width: '100%',
...theme.typography.bodyShortRegular,
color: theme.palette.text01,
color: theme.palette.selectText,
padding: '10px 16px',
paddingRight: '42px',
border: 0,
@@ -99,11 +99,11 @@ const useStyles = makeStyles()(theme => {
'&:focus': {
outline: 0,
boxShadow: `0px 0px 0px 2px ${theme.palette.focus01}`
boxShadow: `0px 0px 0px 2px ${theme.palette.selectFocus}`
},
'&:disabled': {
color: theme.palette.text03
color: theme.palette.selectDisabled
},
'&.is-mobile': {
@@ -113,7 +113,7 @@ const useStyles = makeStyles()(theme => {
},
'&.error': {
boxShadow: `0px 0px 0px 2px ${theme.palette.textError}`
boxShadow: `0px 0px 0px 2px ${theme.palette.selectError}`
}
},
@@ -132,14 +132,14 @@ const useStyles = makeStyles()(theme => {
bottomLabel: {
marginTop: theme.spacing(2),
...theme.typography.labelRegular,
color: theme.palette.text02,
color: theme.palette.selectBottomLabel,
'&.is-mobile': {
...theme.typography.bodyShortRegular
},
'&.error': {
color: theme.palette.textError
color: theme.palette.selectBottomLabelError
}
}
};
@@ -180,7 +180,7 @@ const Select = ({
</select>
<Icon
className = { cx(classes.icon, isMobile && 'is-mobile') }
color = { disabled ? theme.palette.icon03 : theme.palette.icon01 }
color = { disabled ? theme.palette.selectIconDisabled : theme.palette.selectIcon }
size = { 22 }
src = { IconArrowDown } />
</div>

View File

@@ -18,7 +18,7 @@ const useStyles = makeStyles()(theme => {
return {
container: {
position: 'relative',
backgroundColor: theme.palette.ui05,
backgroundColor: theme.palette.switchBackground,
borderRadius: '12px',
width: '40px',
height: '24px',
@@ -29,11 +29,11 @@ const useStyles = makeStyles()(theme => {
display: 'inline-block',
'&.disabled': {
backgroundColor: theme.palette.ui05,
backgroundColor: theme.palette.switchBackground,
cursor: 'default',
'& .toggle': {
backgroundColor: theme.palette.ui03
backgroundColor: theme.palette.switchToggleDisabled
}
},
@@ -45,7 +45,7 @@ const useStyles = makeStyles()(theme => {
},
containerOn: {
backgroundColor: theme.palette.action01
backgroundColor: theme.palette.switchBackgroundOn
},
toggle: {
@@ -55,7 +55,7 @@ const useStyles = makeStyles()(theme => {
zIndex: 5,
top: '4px',
left: '4px',
backgroundColor: theme.palette.ui10,
backgroundColor: theme.palette.switchToggle,
borderRadius: '100%',
transition: '.3s',
@@ -87,7 +87,7 @@ const useStyles = makeStyles()(theme => {
'&.focus-visible + .toggle-checkbox-ring': {
outline: 0,
boxShadow: `0px 0px 0px 2px ${theme.palette.focus01}`
boxShadow: `0px 0px 0px 2px ${theme.palette.switchFocus}`
}
},

View File

@@ -29,13 +29,13 @@ const useStyles = makeStyles()(theme => {
tab: {
...theme.typography.bodyShortBold,
color: theme.palette.text02,
color: theme.palette.tabText,
flex: 1,
padding: '14px',
background: 'none',
border: 0,
appearance: 'none',
borderBottom: `2px solid ${theme.palette.ui05}`,
borderBottom: `2px solid ${theme.palette.tabBorder}`,
transition: 'color, border-color 0.2s',
display: 'flex',
alignItems: 'center',
@@ -43,25 +43,25 @@ const useStyles = makeStyles()(theme => {
borderRadius: 0,
'&:hover': {
color: theme.palette.text01,
borderColor: theme.palette.ui10
color: theme.palette.tabTextHover,
borderColor: theme.palette.tabBorderHover
},
'&.focus-visible': {
outline: 0,
boxShadow: `0px 0px 0px 2px ${theme.palette.focus01}`,
boxShadow: `0px 0px 0px 2px ${theme.palette.tabFocus}`,
border: 0,
color: theme.palette.text01
color: theme.palette.tabTextSelected
},
'&.selected': {
color: theme.palette.text01,
borderColor: theme.palette.action01
color: theme.palette.tabTextSelected,
borderColor: theme.palette.tabBorderSelected
},
'&:disabled': {
color: theme.palette.text03,
borderColor: theme.palette.ui05
color: theme.palette.tabTextDisabled,
borderColor: theme.palette.tabBorderDisabled
},
'&.is-mobile': {
@@ -72,9 +72,9 @@ const useStyles = makeStyles()(theme => {
badge: {
...theme.typography.labelBold,
alignItems: 'center',
backgroundColor: theme.palette.warning01,
backgroundColor: theme.palette.tabBadgeBackground,
borderRadius: theme.spacing(2),
color: theme.palette.text04,
color: theme.palette.tabBadgeText,
display: 'inline-flex',
height: theme.spacing(3),
justifyContent: 'center',

View File

@@ -11,6 +11,23 @@ export * from './constants.any';
*/
export const commonStyles = (theme: Theme) => {
return {
':root': {
// Inject semantic tokens as CSS custom properties for use in SCSS
'--toolbox-background-color': theme.palette.toolboxBackground,
'--drawer-background-color': theme.palette.drawerBackground,
'--toolbar-button-color': theme.palette.toolbarButton,
'--toolbar-button-hover-color': theme.palette.toolbarButtonHover,
'--toolbar-button-active-color': theme.palette.toolbarButtonActive,
'--toolbar-icon-color': theme.palette.toolbarIcon,
'--toolbar-icon-hover-color': theme.palette.toolbarIconHover,
'--toolbar-icon-active-color': theme.palette.toolbarIconActive,
'--overflow-menu-background-color': theme.palette.overflowMenuBackground,
'--overflow-menu-item-text-color': theme.palette.overflowMenuItemText,
'--overflow-menu-item-icon-color': theme.palette.overflowMenuItemIcon,
'--overflow-menu-item-hover-color': theme.palette.overflowMenuItemHover,
'--overflow-menu-item-disabled-color': theme.palette.overflowMenuItemDisabled
},
'.empty-list': {
listStyleType: 'none',
margin: 0,
@@ -39,7 +56,7 @@ export const commonStyles = (theme: Theme) => {
'.overflow-menu-item': {
alignItems: 'center',
color: theme.palette.text01,
color: theme.palette.overflowMenuItemText,
cursor: 'pointer',
display: 'flex',
fontSize: '0.875rem',
@@ -59,20 +76,20 @@ export const commonStyles = (theme: Theme) => {
'&.disabled': {
cursor: 'initial',
color: theme.palette.text03,
color: theme.palette.overflowMenuItemDisabled,
'&:hover': {
background: 'none'
},
'& svg': {
fill: theme.palette.text03
fill: theme.palette.overflowMenuItemDisabled
}
},
'@media (hover: hover) and (pointer: fine)': {
'&:hover': {
background: theme.palette.action02Hover
background: theme.palette.overflowMenuItemHover
},
'&.unclickable:hover': {
background: 'inherit'
@@ -100,14 +117,14 @@ export const commonStyles = (theme: Theme) => {
},
'& svg': {
fill: theme.palette.text01,
fill: theme.palette.overflowMenuItemIcon,
height: 20,
width: 20
}
},
'.prejoin-dialog': {
backgroundColor: theme.palette.uiBackground,
backgroundColor: theme.palette.prejoinDialogBackground,
boxShadow: '0px 2px 20px rgba(0, 0, 0, 0.5)',
borderRadius: theme.shape.borderRadius,
color: '#fff',
@@ -173,7 +190,7 @@ export const commonStyles = (theme: Theme) => {
},
'.prejoin-dialog-delimiter': {
background: theme.palette.ui03,
background: theme.palette.prejoinDialogDelimiter,
border: '0',
height: '1px',
margin: '0',
@@ -194,8 +211,8 @@ export const commonStyles = (theme: Theme) => {
},
'.prejoin-dialog-delimiter-txt': {
background: theme.palette.uiBackground,
color: theme.palette.text01,
background: theme.palette.prejoinDialogBackground,
color: theme.palette.prejoinDialogDelimiterText,
fontSize: '0.75rem',
textTransform: 'uppercase' as const,
padding: `0 ${theme.spacing(2)}`
@@ -219,11 +236,11 @@ export const commonStyles = (theme: Theme) => {
'@media (hover: hover) and (pointer: fine)': {
'&:hover': {
backgroundColor: theme.palette.ui04
backgroundColor: theme.palette.toolboxIconHover
},
'&:active': {
backgroundColor: theme.palette.ui03
backgroundColor: theme.palette.toolboxIconActive
}
},
[theme.breakpoints.down(320)]: {
@@ -232,7 +249,7 @@ export const commonStyles = (theme: Theme) => {
},
'&.toggled': {
backgroundColor: theme.palette.ui03
backgroundColor: theme.palette.toolboxIconToggled
},
'&.disabled': {
@@ -240,13 +257,13 @@ export const commonStyles = (theme: Theme) => {
backgroundColor: `${theme.palette.disabled01} !important`,
'& svg': {
fill: `${theme.palette.text03} !important`
fill: `${theme.palette.icon03} !important`
}
}
},
'.toolbox-button': {
color: theme.palette.text01,
color: theme.palette.toolbarIcon,
cursor: 'pointer',
display: 'inline-block',
lineHeight: '3rem',
@@ -254,7 +271,7 @@ export const commonStyles = (theme: Theme) => {
},
'.toolbox-content-items': {
background: theme.palette.ui01,
background: theme.palette.toolboxBackground,
borderRadius: 6,
margin: '0 auto',
padding: 6,

11
react/features/base/ui/types.d.ts vendored Normal file
View File

@@ -0,0 +1,11 @@
import '@mui/material/styles';
import { IPalette, ITypography } from './types';
declare module '@mui/material/styles' {
interface Palette extends IPalette {}
interface PaletteOptions extends Partial<IPalette> {}
interface Typography extends ITypography {}
interface TypographyOptions extends Partial<ITypography> {}
}

View File

@@ -5,6 +5,7 @@ interface ITypographyType {
lineHeight: string;
}
/* eslint-disable typescript-sort-keys/interface */
export interface IPalette {
action01: string;
action01Active: string;
@@ -58,6 +59,324 @@ export interface IPalette {
uiBackground: string;
warning01: string;
warning02: string;
// Semantic tokens (component-based, more descriptive names)
breakoutRoomArrowBackground: string;
breakoutRoomBackground: string;
breakoutRoomItemBackground: string;
chatBackground: string;
chatBackdrop: string;
chatEmptyText: string;
chatInputBackground: string;
chatInputBorder: string;
chatLink: string;
chatLobbyMessageBubble: string;
chatLobbyMessageNotice: string;
chatLobbyRecipientContainer: string;
chatMessageLocal: string;
chatMessagePrivate: string;
chatMessageRemote: string;
chatMessageText: string;
chatPrivateNotice: string;
chatRecipientCancelIcon: string;
chatRecipientContainer: string;
chatRecipientText: string;
chatReplyIcon: string;
chatSenderName: string;
chatTimestamp: string;
dialogBackground: string;
dialogBorder: string;
dialogOverlay: string;
dialogSecondaryText: string;
dialogText: string;
drawerBackground: string;
filmstripBackground: string;
filmstripBackgroundHover: string;
filmstripDragHandle: string;
filmstripDragHandleHover: string;
inputBackground: string;
inputBorder: string;
inputPlaceholder: string;
inputText: string;
largeVideoBackground: string;
largeVideoPlaceholder: string;
lobbyBackground: string;
lobbyPreviewBackground: string;
notificationActionFocus: string;
notificationActionText: string;
notificationBackground: string;
notificationCloseIcon: string;
notificationError: string;
notificationErrorText: string;
notificationNormalIcon: string;
notificationSuccess: string;
notificationText: string;
notificationWarning: string;
overflowMenuBackground: string;
overflowMenuBorder: string;
overflowMenuItemDisabled: string;
overflowMenuItemHover: string;
overflowMenuItemIcon: string;
overflowMenuItemText: string;
overflowMenuSeparator: string;
participantActionButton: string;
participantCounterBadge: string;
participantCounterText: string;
participantItemBackground: string;
participantItemBorder: string;
participantItemHover: string;
participantLinkText: string;
participantModeratorLabel: string;
participantRaisedHandBadge: string;
participantRaisedHandIcon: string;
participantSectionText: string;
participantsPaneBackground: string;
participantWarningText: string;
preMeetingBackground: string;
preMeetingPreview: string;
prejoinActionButtonDanger: string;
prejoinActionButtonDisabled: string;
prejoinActionButtonPrimary: string;
prejoinActionButtonPrimaryHover: string;
prejoinActionButtonPrimaryText: string;
prejoinActionButtonSecondary: string;
prejoinActionButtonSecondaryHover: string;
prejoinActionButtonSecondaryText: string;
prejoinCountryPickerBackground: string;
prejoinCountryPickerBorder: string;
prejoinCountryPickerText: string;
prejoinCountryRowBackground: string;
prejoinCountryRowHover: string;
prejoinDeviceStatusOk: string;
prejoinDeviceStatusText: string;
prejoinDeviceStatusWarning: string;
prejoinDialogBackground: string;
prejoinDialogDelimiter: string;
prejoinDialogDelimiterText: string;
prejoinRecordingWarningText: string;
prejoinRoomNameText: string;
prejoinTitleText: string;
prejoinWarningBackground: string;
prejoinWarningText: string;
settingsBackground: string;
settingsErrorIcon: string;
settingsSectionBackground: string;
settingsShortcutKey: string;
settingsTabText: string;
settingsVideoPreviewBorder: string;
speakerStatsAvatarLeft: string;
speakerStatsBackground: string;
speakerStatsBorder: string;
speakerStatsHeaderBackground: string;
speakerStatsLabelText: string;
speakerStatsRowAlternate: string;
speakerStatsRowBackground: string;
speakerStatsSearchBackground: string;
speakerStatsSearchBorder: string;
speakerStatsSearchIcon: string;
speakerStatsSearchPlaceholder: string;
speakerStatsSearchText: string;
speakerStatsSuccessBar: string;
thumbnailBackground: string;
thumbnailBorder: string;
thumbnailHover: string;
thumbnailRaisedHandIcon: string;
thumbnailTintBackground: string;
thumbnailVideoBackground: string;
toolbarButton: string;
toolbarButtonActive: string;
toolbarButtonHover: string;
toolbarIcon: string;
toolbarIconActive: string;
toolbarIconHover: string;
toolboxBackground: string;
toolboxIconActive: string;
toolboxIconHover: string;
toolboxIconToggled: string;
visitorsArrowBackground: string;
visitorsCountBadge: string;
visitorsCountIcon: string;
visitorsCountText: string;
visitorsQueueBackground: string;
visitorsQueueText: string;
welcomeBackground: string;
welcomeCard: string;
welcomeTabActive: string;
welcomeTabInactive: string;
// Form components
actionButtonBackground: string;
actionButtonBorder: string;
actionButtonText: string;
audioRoutePickerBackground: string;
audioRoutePickerBorder: string;
audioRoutePickerText: string;
baseReactBackground: string;
baseReactBorder: string;
baseReactText: string;
carModeBackground: string;
carModeBorder: string;
carModeText: string;
checkboxBorder: string;
checkboxChecked: string;
checkboxDisabledBackground: string;
checkboxDisabledBorder: string;
checkboxDisabledChecked: string;
checkboxIcon: string;
checkboxIconDisabled: string;
checkboxLabel: string;
clickableIconActive: string;
clickableIconBackground: string;
clickableIconFocus: string;
clickableIconHover: string;
conferenceNoticeBackground: string;
conferenceNoticeText: string;
conferenceRaisedHandLabelIcon: string;
conferenceRaisedHandLabelText: string;
conferenceSubjectText: string;
conferenceTimerText: string;
connectionIndicatorLost: string;
connectionIndicatorOther: string;
deepLinkingBackground: string;
deepLinkingBorder: string;
deepLinkingLabelText: string;
deepLinkingLink: string;
deepLinkingSeparator: string;
deepLinkingText: string;
deviceSelectorBackground: string;
deviceSelectorBorder: string;
deviceSelectorText: string;
deviceSelectorTextBackground: string;
deviceSelectorVideoPreview: string;
dialInBackground: string;
dialInSecondaryText: string;
dialInText: string;
displayNameBackground: string;
displayNameText: string;
etherpadBackground: string;
etherpadText: string;
fileSharingBackground: string;
fileSharingEmptyIcon: string;
fileSharingEmptyText: string;
fileSharingItemBackground: string;
fileSharingItemBorder: string;
fileSharingText: string;
gifsBackground: string;
gifsText: string;
inlineDialogBackground: string;
inlineDialogBorder: string;
inlineDialogText: string;
inputBottomLabel: string;
inputBottomLabelError: string;
inputClearButton: string;
inputFieldBackground: string;
inputFieldBorder: string;
inputFieldDisabled: string;
inputFieldError: string;
inputFieldFocus: string;
inputFieldPlaceholder: string;
inputFieldText: string;
inputLabel: string;
labelBackground: string;
labelText: string;
labelWhiteBackground: string;
labelWhiteIcon: string;
labelWhiteText: string;
languageSelectorBackground: string;
languageSelectorHover: string;
languageSelectorText: string;
listItemBackground: string;
listItemBoxShadow: string;
listItemHighlighted: string;
listItemHover: string;
listItemText: string;
multiSelectBackground: string;
multiSelectBorder: string;
multiSelectItemDisabled: string;
multiSelectItemHover: string;
multiSelectItemText: string;
pollsAnswer: string;
pollsBackground: string;
pollsBarBackground: string;
pollsBarPercentage: string;
pollsCreateBackground: string;
pollsCreateBorder: string;
pollsPaneBackground: string;
pollsPaneBorder: string;
pollsQuestion: string;
pollsSendDisabled: string;
pollsSendLabel: string;
pollsSeparator: string;
pollsSubtitle: string;
pollsTitle: string;
pollsVotersBackground: string;
pollsVotersText: string;
reactionsMenuBackground: string;
reactionsMenuBorder: string;
recordingBackground: string;
recordingHighlightButton: string;
recordingHighlightButtonDisabled: string;
recordingHighlightButtonIcon: string;
recordingHighlightButtonIconDisabled: string;
recordingNotificationAction: string;
recordingNotificationText: string;
recordingText: string;
securityDialogBackground: string;
securityDialogBorder: string;
securityDialogSecondaryText: string;
securityDialogText: string;
selectBackground: string;
selectBottomLabel: string;
selectBottomLabelError: string;
selectDisabled: string;
selectError: string;
selectFocus: string;
selectIcon: string;
selectIconDisabled: string;
selectLabel: string;
selectText: string;
sliderFocus: string;
sliderKnob: string;
sliderTrack: string;
subtitleMessageBackground: string;
subtitleMessageSender: string;
subtitleMessageText: string;
subtitleMessageTime: string;
switchBackground: string;
switchBackgroundOn: string;
switchFocus: string;
switchToggle: string;
switchToggleDisabled: string;
tabBadgeBackground: string;
tabBadgeText: string;
tabBorder: string;
tabBorderDisabled: string;
tabBorderHover: string;
tabBorderSelected: string;
tabFocus: string;
tabText: string;
tabTextDisabled: string;
tabTextHover: string;
tabTextSelected: string;
tooltipBackground: string;
tooltipText: string;
videoMenuBackground: string;
videoMenuBorder: string;
videoMenuSliderBackground: string;
videoMenuText: string;
videoQualityBackground: string;
videoQualityText: string;
virtualBackgroundBackground: string;
virtualBackgroundBorder: string;
virtualBackgroundPreview: string;
virtualBackgroundText: string;
whiteboardBackground: string;
whiteboardText: string;
salesforceSearchBackground: string;
salesforceSearchBorder: string;
salesforceSearchIcon: string;
salesforceSearchPlaceholder: string;
salesforceSearchText: string;
}
export interface ITypography {

View File

@@ -11,13 +11,49 @@ import * as tokens from './tokens.json';
*/
export function createColorTokens(colorMap: Object): any {
const allTokens = merge({}, tokens, jitsiTokens);
const result: any = {};
return Object.entries(colorMap)
.reduce((result, [ token, value ]: [any, string]) => {
const color = allTokens[value as keyof typeof allTokens] || value;
// First pass: resolve tokens that reference allTokens directly
Object.entries(colorMap).forEach(([ token, value ]: [any, string]) => {
const color = allTokens[value as keyof typeof allTokens] || value;
return Object.assign(result, { [token]: color });
}, {});
result[token] = color;
});
// Second pass: resolve semantic tokens that reference other colorMap entries
// Recursively resolve until we get actual color values
const resolveToken = (value: string, depth = 0): string => {
// Prevent infinite loops
if (depth > 10) {
return value;
}
// If it's already a color (starts with # or rgb/rgba), return it
if (value.startsWith('#') || value.startsWith('rgb')) {
return value;
}
// Look up in the result map first (for colorMap token references)
if (result[value]) {
return resolveToken(result[value], depth + 1);
}
// Then look up in allTokens
const resolved = allTokens[value as keyof typeof allTokens];
if (resolved && resolved !== value && typeof resolved === 'string') {
return resolveToken(resolved, depth + 1);
}
return value;
};
// Third pass: recursively resolve all values
Object.entries(result).forEach(([ token, value ]) => {
result[token] = resolveToken(String(value));
});
return result;
}
/**

View File

@@ -8,7 +8,7 @@ export default {
button: {
marginBottom: BaseTheme.spacing[4],
marginHorizontal: BaseTheme.spacing[2]
marginHorizontal: BaseTheme.spacing[3]
},
collapsibleList: {

View File

@@ -44,6 +44,16 @@ export const getMainRoom = (stateful: IStateful) => {
* @returns {IRoomsInfo} The rooms info.
*/
export const getRoomsInfo = (stateful: IStateful) => {
const state = toState(stateful);
const localParticipant = getLocalParticipant(stateful);
const jwtUser = state['features/base/jwt']?.user;
const localUserContext = jwtUser ? {
id: jwtUser.id,
name: jwtUser.name
} : {
id: localParticipant?.jwtId,
name: localParticipant?.name
};
const breakoutRooms = getBreakoutRooms(stateful);
const conference = getCurrentConference(stateful);
@@ -57,7 +67,6 @@ export const getRoomsInfo = (stateful: IStateful) => {
const conferenceParticipants = conference?.getParticipants()
.filter((participant: IJitsiParticipant) => !participant.isHidden());
const localParticipant = getLocalParticipant(stateful);
let localParticipantInfo;
if (localParticipant) {
@@ -65,7 +74,8 @@ export const getRoomsInfo = (stateful: IStateful) => {
role: localParticipant.role,
displayName: localParticipant.name,
avatarUrl: localParticipant.loadableAvatarUrl,
id: localParticipant.id
id: localParticipant.id,
userContext: localUserContext
};
}
@@ -86,7 +96,8 @@ export const getRoomsInfo = (stateful: IStateful) => {
role: participantItem.getRole(),
displayName: participantItem.getDisplayName(),
avatarUrl: storeParticipant?.loadableAvatarUrl,
id: participantItem.getId()
id: participantItem.getId(),
userContext: storeParticipant?.userContext
} as IRoomInfoParticipant;
}) ]
: [ localParticipantInfo ]
@@ -110,13 +121,18 @@ export const getRoomsInfo = (stateful: IStateful) => {
const storeParticipant = getParticipantById(stateful,
ids.length > 1 ? ids[1] : participantItem.jid);
// Check if this is the local participant
const isLocal = storeParticipant?.id === localParticipant?.id;
const userContext = isLocal ? localUserContext : (storeParticipant?.userContext || participantItem.userContext);
return {
jid: participantItem?.jid,
role: participantItem?.role,
displayName: participantItem?.displayName,
avatarUrl: storeParticipant?.loadableAvatarUrl,
id: storeParticipant ? storeParticipant.id
: participantLongId
: participantLongId,
userContext
} as IRoomInfoParticipant;
}) : []
} as IRoomInfo;

View File

@@ -44,19 +44,59 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
switch (type) {
case UPDATE_BREAKOUT_ROOMS: {
// edit name if it was overwritten
// Enrich participants with userContext from Redux store
if (!action.updatedNames) {
const { overwrittenNameList } = getState()['features/base/participants'];
const state = getState();
const { overwrittenNameList, local: localParticipant } = state['features/base/participants'];
const jwtUser = state['features/base/jwt']?.user;
const localUserContext = jwtUser ? {
id: jwtUser.id,
name: jwtUser.name
} : {
id: localParticipant?.jwtId,
name: localParticipant?.name
};
if (Object.keys(overwrittenNameList).length > 0) {
const newRooms: IRooms = {};
// Get existing userContext cache
const existingCache = state['features/breakout-rooms'].userContextCache || {};
const newCache = { ...existingCache };
Object.entries(action.rooms as IRooms).forEach(([ key, r ]) => {
let participants = r?.participants || {};
let jid;
const newRooms: IRooms = {};
Object.entries(action.rooms as IRooms).forEach(([ key, r ]) => {
let participants = r?.participants || {};
// Add userContext to each participant
const enhancedParticipants: typeof participants = {};
for (const [ participantJid, participantData ] of Object.entries(participants)) {
const ids = participantJid.split('/');
const participantId = ids.length > 1 ? ids[1] : participantData.jid;
const storeParticipant = getParticipantById(state, participantId);
const isLocal = storeParticipant?.id === localParticipant?.id;
// Try to get userContext from: local, store, cache, or incoming data
const userContext = isLocal
? localUserContext
: (storeParticipant?.userContext || newCache[participantId] || participantData.userContext);
// Update cache if we have userContext
if (userContext && participantId) {
newCache[participantId] = userContext;
}
enhancedParticipants[participantJid] = {
...participantData,
userContext
};
}
participants = enhancedParticipants;
// Apply overwritten display names
if (Object.keys(overwrittenNameList).length > 0) {
for (const id of Object.keys(overwrittenNameList)) {
jid = Object.keys(participants).find(p => p.slice(p.indexOf('/') + 1) === id);
const jid = Object.keys(participants).find(p => p.slice(p.indexOf('/') + 1) === id);
if (jid) {
participants = {
@@ -68,15 +108,16 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
};
}
}
}
newRooms[key] = {
...r,
participants
};
});
newRooms[key] = {
...r,
participants
};
});
action.rooms = newRooms;
}
action.rooms = newRooms;
action.userContextCache = newCache;
}
// edit the chat history to match names for participants in breakout rooms

View File

@@ -10,12 +10,19 @@ import { IRooms } from './types';
const DEFAULT_STATE = {
rooms: {},
roomCounter: 0
roomCounter: 0,
userContextCache: {}
};
export interface IBreakoutRoomsState {
roomCounter: number;
rooms: IRooms;
userContextCache: {
[participantId: string]: {
id?: string;
name?: string;
};
};
}
/**
@@ -29,12 +36,13 @@ ReducerRegistry.register<IBreakoutRoomsState>(FEATURE_KEY, (state = DEFAULT_STAT
roomCounter: action.roomCounter
};
case UPDATE_BREAKOUT_ROOMS: {
const { roomCounter, rooms } = action;
const { roomCounter, rooms, userContextCache } = action;
return {
...state,
roomCounter,
rooms
rooms,
userContextCache: userContextCache || state.userContextCache
};
}
case _RESET_BREAKOUT_ROOMS: {

View File

@@ -8,6 +8,10 @@ export interface IRoom {
displayName: string;
jid: string;
role: string;
userContext?: {
id?: string;
name?: string;
};
};
};
}
@@ -33,4 +37,8 @@ export interface IRoomInfoParticipant {
id: string;
jid: string;
role: string;
userContext?: {
id?: string;
name?: string;
};
}

View File

@@ -42,6 +42,8 @@ const ClosedCaptions = ({
const navigateToLanguageSelect = useCallback(() => {
navigate(screen.conference.subtitles);
}, [ navigation, screen ]);
const isAsyncTranscriptionEnabled = useSelector((state: IReduxState) =>
state['features/base/conference'].conference?.getMetadataHandler()?.getMetadata()?.asyncTranscription);
useEffect(() => {
navigation?.setOptions({
@@ -91,17 +93,20 @@ const ClosedCaptions = ({
return (
<>
<View style = { closedCaptionsStyles.languageButtonContainer as ViewStyle }>
<Text style = { closedCaptionsStyles.languageButtonText }>{ t('transcribing.translateTo') }:</Text>
<TouchableHighlight onPress = { navigateToLanguageSelect }>
<View style = { closedCaptionsStyles.languageButtonContent as ViewStyle }>
<Text style = { closedCaptionsStyles.languageButtonText }>{ t(selectedLanguage ?? 'transcribing.subtitlesOff') }</Text>
<Icon
size = { 24 }
src = { IconArrowRight } />
</View>
</TouchableHighlight>
</View>
{
// Hide the "Translate to" option when asyncTranscription is enabled
!isAsyncTranscriptionEnabled && <View style = { closedCaptionsStyles.languageButtonContainer as ViewStyle }>
<Text style = { closedCaptionsStyles.languageButtonText }>{ t('transcribing.translateTo') }:</Text>
<TouchableHighlight onPress = { navigateToLanguageSelect }>
<View style = { closedCaptionsStyles.languageButtonContent as ViewStyle }>
<Text style = { closedCaptionsStyles.languageButtonText }>{ t(selectedLanguage ?? 'transcribing.subtitlesOff') }</Text>
<Icon
size = { 24 }
src = { IconArrowRight } />
</View>
</TouchableHighlight>
</View>
}
<View style = { closedCaptionsStyles.messagesContainer as ViewStyle }>
<SubtitlesMessagesContainer
groups = { groupedSubtitles }

View File

@@ -107,7 +107,7 @@ interface IProps extends AbstractProps {
const useStyles = makeStyles<{ _isResizing: boolean; width: number; }>()((theme, { _isResizing, width }) => {
return {
container: {
backgroundColor: theme.palette.ui01,
backgroundColor: theme.palette.chatBackground,
flexShrink: 0,
overflow: 'hidden',
position: 'relative',
@@ -132,7 +132,7 @@ const useStyles = makeStyles<{ _isResizing: boolean; width: number; }>()((theme,
'*': {
userSelect: 'text',
'-webkit-user-select': 'text'
WebkitUserSelect: 'text'
}
},
@@ -146,7 +146,7 @@ const useStyles = makeStyles<{ _isResizing: boolean; width: number; }>()((theme,
padding: `${theme.spacing(3)} ${theme.spacing(4)}`,
alignItems: 'center',
boxSizing: 'border-box',
color: theme.palette.text01,
color: theme.palette.chatMessageText,
...theme.typography.heading6,
lineHeight: 'unset',
fontWeight: theme.typography.heading6.fontWeight as any,

View File

@@ -37,7 +37,7 @@ const styles = (_theme: Theme, { _chatWidth }: IProps) => {
}
},
chatDisabled: {
borderTop: `1px solid ${_theme.palette.ui02}`,
borderTop: `1px solid ${_theme.palette.chatInputBorder}`,
boxSizing: 'border-box' as const,
padding: _theme.spacing(4),
textAlign: 'center' as const,

View File

@@ -43,7 +43,7 @@ const useStyles = makeStyles()((theme: Theme) => {
chatMessage: {
display: 'inline-flex',
padding: '12px',
backgroundColor: theme.palette.ui02,
backgroundColor: theme.palette.chatMessageRemote,
borderRadius: '4px 12px 12px 12px',
maxWidth: '100%',
marginTop: '4px',
@@ -66,21 +66,21 @@ const useStyles = makeStyles()((theme: Theme) => {
},
'&.privatemessage': {
backgroundColor: theme.palette.support05
backgroundColor: theme.palette.chatMessagePrivate
},
'&.local': {
backgroundColor: theme.palette.ui04,
backgroundColor: theme.palette.chatMessageLocal,
borderRadius: '12px 4px 12px 12px',
'&.privatemessage': {
backgroundColor: theme.palette.support05
backgroundColor: theme.palette.chatMessagePrivate
},
'&.local': {
backgroundColor: theme.palette.ui04,
backgroundColor: theme.palette.chatMessageLocal,
borderRadius: '12px 4px 12px 12px',
'&.privatemessage': {
backgroundColor: theme.palette.support05
backgroundColor: theme.palette.chatMessagePrivate
}
},
@@ -91,7 +91,7 @@ const useStyles = makeStyles()((theme: Theme) => {
},
'&.lobbymessage': {
backgroundColor: theme.palette.support05
backgroundColor: theme.palette.chatMessagePrivate
}
},
'&.error': {
@@ -100,7 +100,7 @@ const useStyles = makeStyles()((theme: Theme) => {
fontWeight: 100
},
'&.lobbymessage': {
backgroundColor: theme.palette.support05
backgroundColor: theme.palette.chatMessagePrivate
}
},
sideBySideContainer: {
@@ -146,7 +146,7 @@ const useStyles = makeStyles()((theme: Theme) => {
},
displayName: {
...theme.typography.labelBold,
color: theme.palette.text02,
color: theme.palette.chatSenderName,
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
overflow: 'hidden',
@@ -155,18 +155,18 @@ const useStyles = makeStyles()((theme: Theme) => {
},
userMessage: {
...theme.typography.bodyShortRegular,
color: theme.palette.text01,
color: theme.palette.chatMessageText,
whiteSpace: 'pre-wrap',
wordBreak: 'break-word'
},
privateMessageNotice: {
...theme.typography.labelRegular,
color: theme.palette.text02,
color: theme.palette.chatPrivateNotice,
marginTop: theme.spacing(1)
},
timestamp: {
...theme.typography.labelRegular,
color: theme.palette.text03,
color: theme.palette.chatTimestamp,
marginTop: theme.spacing(1),
marginLeft: theme.spacing(1),
whiteSpace: 'nowrap',
@@ -174,12 +174,12 @@ const useStyles = makeStyles()((theme: Theme) => {
},
reactionsPopover: {
padding: theme.spacing(2),
backgroundColor: theme.palette.ui03,
backgroundColor: theme.palette.chatInputBackground,
borderRadius: theme.shape.borderRadius,
maxWidth: '150px',
maxHeight: '400px',
overflowY: 'auto',
color: theme.palette.text01
color: theme.palette.chatMessageText
},
reactionItem: {
display: 'flex',

View File

@@ -24,7 +24,7 @@ const useStyles = makeStyles()(theme => {
padding: '16px',
flex: 1,
boxSizing: 'border-box',
color: theme.palette.text01
color: theme.palette.chatMessageText
},
container: {
display: 'flex',
@@ -48,7 +48,7 @@ const useStyles = makeStyles()(theme => {
boxSizing: 'border-box',
flexDirection: 'column',
gap: '16px',
color: theme.palette.text01,
color: theme.palette.chatMessageText,
textAlign: 'center'
},
emptyIcon: {
@@ -62,7 +62,7 @@ const useStyles = makeStyles()(theme => {
},
emptyState: {
...theme.typography.bodyLongBold,
color: theme.palette.text02
color: theme.palette.chatSenderName
}
};
});
@@ -103,7 +103,7 @@ const ClosedCaptionsTab = ({
<div className = { classes.emptyContent }>
<Icon
className = { classes.emptyIcon }
color = { theme.palette.icon03 }
color = { theme.palette.chatEmptyText }
src = { IconSubtitles } />
<span className = { classes.emptyState }>
{ t('closedCaptionsTab.emptyState') }

View File

@@ -12,7 +12,7 @@ const useStyles = makeStyles()((theme: Theme) => {
display: 'flex',
flexDirection: 'row',
borderRadius: '4px',
backgroundColor: theme.palette.ui03
backgroundColor: theme.palette.chatInputBackground
},
emojiButton: {

View File

@@ -46,12 +46,12 @@ const useStyles = makeStyles()(theme => {
// Add background to button container to hide text underneath in chat context
'& > div:last-child': {
backgroundColor: theme.palette.ui02,
backgroundColor: theme.palette.chatMessageRemote,
paddingLeft: theme.spacing(2)
},
'&:hover > div:last-child': {
backgroundColor: theme.palette.ui03
backgroundColor: theme.palette.chatInputBackground
}
},
@@ -66,7 +66,7 @@ const useStyles = makeStyles()(theme => {
deletedFileMessage: {
...theme.typography.bodyShortRegular,
fontStyle: 'italic',
color: theme.palette.text02,
color: theme.palette.fileSharingEmptyText,
padding: theme.spacing(1, 0)
}
};

View File

@@ -39,14 +39,14 @@ const useStyles = makeStyles()(theme => {
}
},
menuPanel: {
backgroundColor: theme.palette.ui03,
backgroundColor: theme.palette.chatInputBackground,
borderRadius: theme.shape.borderRadius,
boxShadow: theme.shadows[3],
overflow: 'hidden'
},
copiedMessage: {
position: 'fixed',
backgroundColor: theme.palette.ui03,
backgroundColor: theme.palette.chatInputBackground,
color: 'white',
padding: '4px 8px',
borderRadius: '4px',

View File

@@ -24,7 +24,7 @@ const useStyles = makeStyles()(theme => {
backgroundColor: theme.palette.support05,
borderRadius: theme.shape.borderRadius,
...theme.typography.bodyShortRegular,
color: theme.palette.text01
color: theme.palette.chatRecipientText
},
text: {

View File

@@ -22,7 +22,7 @@ interface IProps extends ISubtitle {
const useStyles = makeStyles()(theme => {
return {
messageContainer: {
backgroundColor: theme.palette.ui02,
backgroundColor: theme.palette.subtitleMessageBackground,
borderRadius: '4px 12px 12px 12px',
padding: '12px',
maxWidth: '100%',
@@ -39,7 +39,7 @@ const useStyles = makeStyles()(theme => {
messageHeader: {
...theme.typography.labelBold,
color: theme.palette.text02,
color: theme.palette.subtitleMessageSender,
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
overflow: 'hidden',
@@ -49,14 +49,14 @@ const useStyles = makeStyles()(theme => {
messageText: {
...theme.typography.bodyShortRegular,
color: theme.palette.text01,
color: theme.palette.subtitleMessageText,
whiteSpace: 'pre-wrap',
wordBreak: 'break-word'
},
timestamp: {
...theme.typography.labelRegular,
color: theme.palette.text03,
color: theme.palette.subtitleMessageTime,
marginTop: theme.spacing(1)
},

View File

@@ -14,6 +14,7 @@ import { setColorAlpha } from '../../../base/util/helpers';
import { openChat, setFocusedTab } from '../../../chat/actions.web';
import Chat from '../../../chat/components/web/Chat';
import { ChatTabs } from '../../../chat/constants';
import CustomPanel from '../../../custom-panel/components/web/CustomPanel';
import { isFileUploadingEnabled, processFiles } from '../../../file-sharing/functions.any';
import MainFilmstrip from '../../../filmstrip/components/web/MainFilmstrip';
import ScreenshareFilmstrip from '../../../filmstrip/components/web/ScreenshareFilmstrip';
@@ -257,6 +258,9 @@ class Conference extends AbstractConference<IProps, any> {
id = 'videospace'
onTouchStart = { this._onVideospaceTouchStart }>
<LargeVideo />
<StageFilmstrip />
<ScreenshareFilmstrip />
<MainFilmstrip />
</div>
<span
aria-level = { 1 }
@@ -323,6 +327,7 @@ class Conference extends AbstractConference<IProps, any> {
{ _showVisitorsQueue && <VisitorsQueue />}
</div>
<ParticipantsPane />
<CustomPanel />
<ReactionAnimations />
</div>
);

View File

@@ -7,7 +7,7 @@ const useStyles = makeStyles()(theme => {
return {
timer: {
...theme.typography.labelRegular,
color: theme.palette.text01,
color: theme.palette.conferenceTimerText,
padding: '6px 8px',
backgroundColor: 'rgba(0, 0, 0, 0.8)',
boxSizing: 'border-box',

View File

@@ -15,8 +15,8 @@ const useStyles = makeStyles()(theme => {
},
message: {
backgroundColor: theme.palette.uiBackground,
color: theme.palette.text01,
backgroundColor: theme.palette.conferenceNoticeBackground,
color: theme.palette.conferenceNoticeText,
padding: '3px',
borderRadius: '5px'
}

View File

@@ -13,7 +13,7 @@ const useStyles = makeStyles()(theme => {
return {
label: {
backgroundColor: theme.palette.warning02,
color: theme.palette.uiBackground
color: theme.palette.conferenceRaisedHandLabelText
}
};
});
@@ -35,7 +35,7 @@ const RaisedHandsCountLabel = () => {
accessibilityText = { t('raisedHandsLabel') }
className = { styles.label }
icon = { IconRaiseHand }
iconColor = { theme.palette.icon04 }
iconColor = { theme.palette.conferenceRaisedHandLabelIcon }
id = 'raisedHandsCountLabel'
onClick = { onClick }
text = { `${raisedHandsCount}` } />

View File

@@ -10,7 +10,7 @@ const useStyles = makeStyles()(theme => {
return {
container: {
...theme.typography.bodyLongRegular,
color: theme.palette.text01,
color: theme.palette.conferenceSubjectText,
padding: '2px 16px',
backgroundColor: 'rgba(0, 0, 0, 0.6)',
maxWidth: '324px',

View File

@@ -14,13 +14,13 @@ import Input from '../../../base/ui/components/web/Input';
const useStyles = makeStyles()(theme => {
return {
content: {
color: theme.palette.text01
color: theme.palette.dialogText
},
info: {
background: theme.palette.ui01,
background: theme.palette.dialogBackground,
...theme.typography.labelRegular,
color: theme.palette.text02,
color: theme.palette.dialogSecondaryText,
marginTop: theme.spacing(2)
},

View File

@@ -179,7 +179,7 @@ const styles = (theme: Theme) => {
},
'&.status-lost': {
backgroundColor: theme.palette.ui05
backgroundColor: theme.palette.connectionIndicatorLost
},
'&.status-other': {

View File

@@ -0,0 +1,14 @@
/**
* Action type to signal the closing of the custom panel.
*/
export const CUSTOM_PANEL_CLOSE = 'CUSTOM_PANEL_CLOSE';
/**
* Action type to signal the opening of the custom panel.
*/
export const CUSTOM_PANEL_OPEN = 'CUSTOM_PANEL_OPEN';
/**
* Action type to enable or disable the custom panel dynamically.
*/
export const SET_CUSTOM_PANEL_ENABLED = 'SET_CUSTOM_PANEL_ENABLED';

View File

@@ -0,0 +1,40 @@
import {
CUSTOM_PANEL_CLOSE,
CUSTOM_PANEL_OPEN,
SET_CUSTOM_PANEL_ENABLED
} from './actionTypes';
/**
* Action to close the custom panel.
*
* @returns {Object} The action object.
*/
export function close() {
return {
type: CUSTOM_PANEL_CLOSE
};
}
/**
* Action to open the custom panel.
*
* @returns {Object} The action object.
*/
export function open() {
return {
type: CUSTOM_PANEL_OPEN
};
}
/**
* Action to enable or disable the custom panel dynamically.
*
* @param {boolean} enabled - Whether the custom panel should be enabled.
* @returns {Object} The action object.
*/
export function setCustomPanelEnabled(enabled: boolean) {
return {
type: SET_CUSTOM_PANEL_ENABLED,
enabled
};
}

View File

@@ -0,0 +1,10 @@
/**
* Custom panel placeholder component.
* This file is overridden by jitsi-meet-branding at build time
* to provide the actual panel implementation with iframe content.
*
* @returns {null} This placeholder renders nothing.
*/
const CustomPanel = (): null => null;
export default CustomPanel;

View File

@@ -0,0 +1,10 @@
/**
* Custom panel button placeholder component.
* This file is overridden by jitsi-meet-branding at build time
* to provide the actual button implementation with custom icon.
*
* @returns {null} This placeholder renders nothing.
*/
const CustomPanelButton = (): null => null;
export default CustomPanelButton;

View File

@@ -0,0 +1,4 @@
/**
* Default width for the custom panel in pixels.
*/
export const DEFAULT_CUSTOM_PANEL_WIDTH = 315;

View File

@@ -0,0 +1,67 @@
import { IReduxState } from '../app/types';
import { DEFAULT_CUSTOM_PANEL_WIDTH } from './constants';
/**
* Returns whether the custom panel is enabled based on Redux state.
* The feature is disabled by default and can be enabled dynamically via console.
*
* @param {IReduxState} state - The Redux state.
* @returns {boolean} Whether the custom panel is enabled.
*/
export function isCustomPanelEnabled(state: IReduxState): boolean {
return Boolean(state['features/custom-panel']?.enabled);
}
/**
* Returns the custom panel URL.
* Override to provide the actual URL.
*
* @returns {string} The custom panel URL.
*/
export function getCustomPanelUrl(): string {
return '';
}
/**
* Returns the custom panel button icon.
* Override to provide the actual icon.
*
* @returns {Function | undefined} The icon component.
*/
export function getCustomPanelIcon(): Function | undefined {
return undefined;
}
/**
* Returns the configured panel width.
*
* @returns {number} The panel width in pixels.
*/
export function getCustomPanelConfiguredWidth(): number {
return DEFAULT_CUSTOM_PANEL_WIDTH;
}
/**
* Returns whether the custom panel is currently open.
*
* @param {IReduxState} state - The Redux state.
* @returns {boolean} Whether the custom panel is open.
*/
export function getCustomPanelOpen(state: IReduxState): boolean {
return Boolean(state['features/custom-panel']?.isOpen);
}
/**
* Returns the current panel width (0 if closed or disabled).
*
* @param {IReduxState} state - The Redux state.
* @returns {number} The panel width in pixels.
*/
export function getCustomPanelWidth(state: IReduxState): number {
if (!isCustomPanelEnabled(state)) {
return 0;
}
return getCustomPanelOpen(state) ? getCustomPanelConfiguredWidth() : 0;
}

View File

@@ -0,0 +1,29 @@
import { useSelector } from 'react-redux';
import CustomPanelButton from './components/web/CustomPanelButton';
import { isCustomPanelEnabled } from './functions';
/**
* Configuration for the custom panel toolbar button.
*/
const customPanel = {
key: 'custom-panel',
Content: CustomPanelButton,
group: 2
};
/**
* A hook that returns the custom panel button if the feature is enabled.
* Uses useSelector for reactive updates when the feature is toggled dynamically.
*
* @returns {Object | undefined} The button configuration or undefined if disabled.
*/
export function useCustomPanelButton() {
const enabled = useSelector(isCustomPanelEnabled);
if (enabled) {
return customPanel;
}
return undefined;
}

View File

@@ -0,0 +1,4 @@
/**
* Custom panel middleware placeholder.
* Override to add custom panel functionality.
*/

View File

@@ -0,0 +1,59 @@
import ReducerRegistry from '../base/redux/ReducerRegistry';
import {
CUSTOM_PANEL_CLOSE,
CUSTOM_PANEL_OPEN,
SET_CUSTOM_PANEL_ENABLED
} from './actionTypes';
/**
* The state of the custom panel feature.
*/
export interface ICustomPanelState {
/**
* Whether the custom panel feature is enabled.
* This can be toggled dynamically via console.
*/
enabled: boolean;
/**
* Whether the custom panel is currently open.
*/
isOpen: boolean;
}
const DEFAULT_STATE: ICustomPanelState = {
enabled: false,
isOpen: false
};
/**
* Listen for actions that mutate the custom panel state.
*/
ReducerRegistry.register(
'features/custom-panel', (state: ICustomPanelState = DEFAULT_STATE, action): ICustomPanelState => {
switch (action.type) {
case CUSTOM_PANEL_CLOSE:
return {
...state,
isOpen: false
};
case CUSTOM_PANEL_OPEN:
return {
...state,
isOpen: true
};
case SET_CUSTOM_PANEL_ENABLED:
return {
...state,
enabled: action.enabled
};
default:
return state;
}
}
);

View File

@@ -33,12 +33,12 @@ const useStyles = makeStyles()((theme: Theme) => {
contentPane: {
display: 'flex',
flexDirection: 'column',
background: theme.palette.ui01,
border: `1px solid ${theme.palette.ui03}`,
background: theme.palette.deepLinkingBackground,
border: `1px solid ${theme.palette.deepLinkingBorder}`,
padding: 40,
borderRadius: 16,
maxWidth: 410,
color: theme.palette.text01
color: theme.palette.deepLinkingText
},
logo: {
marginBottom: 32
@@ -66,14 +66,14 @@ const useStyles = makeStyles()((theme: Theme) => {
marginTop: 40,
height: 1,
maxWidth: 390,
background: theme.palette.ui03
background: theme.palette.deepLinkingSeparator
},
label: {
marginTop: 40,
...theme.typography.labelRegular,
color: theme.palette.text02,
color: theme.palette.deepLinkingLabelText,
'& a': {
color: theme.palette.link01
color: theme.palette.deepLinkingLink
}
}
};

View File

@@ -44,7 +44,7 @@ const useStyles = makeStyles()((theme: Theme) => {
flexDirection: 'column',
padding: `${PADDINGS.topBottom}px ${PADDINGS.leftRight}px`,
maxWidth: 410,
color: theme.palette.text01
color: theme.palette.deepLinkingText
},
launchingMeetingLabel: {
marginTop: 24,
@@ -89,7 +89,7 @@ const useStyles = makeStyles()((theme: Theme) => {
marginTop: '32px',
height: 1,
width: `calc(100% + ${2 * PADDINGS.leftRight}px)`,
background: theme.palette.ui03
background: theme.palette.deepLinkingSeparator
}
};
});

View File

@@ -26,7 +26,7 @@ const useStyles = makeStyles()(theme => {
flex: 1,
height: '4px',
borderRadius: '1px',
backgroundColor: theme.palette.ui04,
backgroundColor: theme.palette.labelBackground,
marginRight: theme.spacing(1),
'&:last-of-type': {

View File

@@ -20,7 +20,7 @@ const useStyles = makeStyles()(theme => {
label: {
...theme.typography.bodyShortRegular,
color: theme.palette.text01,
color: theme.palette.deviceSelectorText,
marginBottom: theme.spacing(2)
},

View File

@@ -56,11 +56,11 @@ const useStyles = makeStyles()(theme => {
width: '100%',
boxSizing: 'border-box',
borderRadius: theme.shape.borderRadius,
backgroundColor: theme.palette.uiBackground,
backgroundColor: theme.palette.deviceSelectorTextBackground,
padding: '10px 16px',
textAlign: 'center',
...theme.typography.bodyShortRegular,
border: `1px solid ${theme.palette.ui03}`
border: `1px solid ${theme.palette.deviceSelectorBorder}`
}
};
});

View File

@@ -32,7 +32,7 @@ const useStyles = makeStyles()(theme => {
borderRadius: '3px',
overflow: 'hidden',
marginBottom: theme.spacing(4),
backgroundColor: theme.palette.uiBackground
backgroundColor: theme.palette.deviceSelectorVideoPreview
},
video: {
@@ -42,7 +42,7 @@ const useStyles = makeStyles()(theme => {
},
errorText: {
color: theme.palette.text01,
color: theme.palette.deviceSelectorText,
left: 0,
position: 'absolute',
right: 0,

View File

@@ -212,7 +212,7 @@ MiddlewareRegistry.register(store => next => action => {
const state = store.getState();
const { defaultRemoteDisplayName } = state['features/base/config'];
const { participant } = action;
const { fakeParticipant, id, local, name } = participant;
const { fakeParticipant, id, local, name, userContext } = participant;
// The version of external api outside of middleware did not emit
// the local participant being created.
@@ -225,7 +225,8 @@ MiddlewareRegistry.register(store => next => action => {
APP.API.notifyUserJoined(id, {
displayName: name,
formattedDisplayName: appendSuffix(
name || defaultRemoteDisplayName)
name || defaultRemoteDisplayName),
userContext
});
}

View File

@@ -31,7 +31,7 @@ const useStyles = makeStyles()(theme => {
ratingLabel: {
...theme.typography.bodyShortBold,
color: theme.palette.text01,
color: theme.palette.dialogText,
marginBottom: theme.spacing(2),
height: '20px'
},

View File

@@ -84,7 +84,7 @@ const useStyles = makeStyles()(theme => {
},
fileItem: {
backgroundColor: theme.palette.ui02,
backgroundColor: theme.palette.fileSharingItemBackground,
borderRadius: theme.shape.borderRadius,
boxSizing: 'border-box',
display: 'flex',
@@ -106,7 +106,7 @@ const useStyles = makeStyles()(theme => {
},
'&:hover': {
backgroundColor: theme.palette.ui03,
backgroundColor: theme.palette.fileSharingItemBorder,
'& .actionIconVisibility': {
opacity: 1
@@ -186,7 +186,7 @@ const useStyles = makeStyles()(theme => {
},
progressBar: {
backgroundColor: theme.palette.ui03,
backgroundColor: theme.palette.fileSharingItemBorder,
borderRadius: theme.shape.borderRadius,
height: 4,
overflow: 'hidden',
@@ -275,7 +275,7 @@ const FileItem = ({
<>
<div className = { classes.fileIconContainer }>
<Icon
color = { BaseTheme.palette.icon01 }
color = { BaseTheme.palette.fileSharingText }
size = { iconSize }
src = { getFileIcon(file.fileType) } />
</div>
@@ -320,7 +320,7 @@ const FileItem = ({
onClick = { handleDownload }
type = 'button'>
<Icon
color = { BaseTheme.palette.icon01 }
color = { BaseTheme.palette.fileSharingText }
size = { 24 }
src = { IconDownload } />
</button>
@@ -335,7 +335,7 @@ const FileItem = ({
onClick = { handleRemove }
type = 'button'>
<Icon
color = { BaseTheme.palette.icon01 }
color = { BaseTheme.palette.fileSharingText }
size = { 24 }
src = { IconTrash } />
</button>

View File

@@ -32,8 +32,8 @@ const useStyles = makeStyles()(theme => {
},
dropZone: {
backgroundColor: theme.palette.ui02,
border: `2px dashed ${theme.palette.ui03}`,
backgroundColor: theme.palette.fileSharingItemBackground,
border: `2px dashed ${theme.palette.fileSharingItemBorder}`,
borderRadius: theme.shape.borderRadius,
bottom: 0,
left: 0,
@@ -44,7 +44,7 @@ const useStyles = makeStyles()(theme => {
zIndex: 0,
'&.dragging': {
backgroundColor: theme.palette.ui03,
backgroundColor: theme.palette.fileSharingItemBorder,
borderColor: theme.palette.action01,
opacity: 0.8,
zIndex: 2
@@ -85,7 +85,7 @@ const useStyles = makeStyles()(theme => {
noFilesText: {
...theme.typography.bodyLongBold,
color: theme.palette.text02,
color: theme.palette.fileSharingEmptyText,
padding: '0 24px',
textAlign: 'center'
},
@@ -189,7 +189,7 @@ const FileSharing = () => {
tabIndex = { 0 }>
<Icon
className = { classes.uploadIcon }
color = { BaseTheme.palette.icon03 }
color = { BaseTheme.palette.fileSharingEmptyIcon }
size = { 160 }
src = { IconCloudUpload } />
<span className = { classes.noFilesText }>

View File

@@ -79,7 +79,7 @@ function styles(theme: Theme, props: IProps) {
zIndex: 1,
'&:hover, &:focus-within': {
backgroundColor: theme.palette.ui02
backgroundColor: theme.palette.filmstripBackground
}
},
@@ -94,7 +94,7 @@ function styles(theme: Theme, props: IProps) {
margin: 0,
border: 'none',
'-webkit-appearance': 'none',
WebkitAppearance: 'none' as const,
'& svg': {
fill: theme.palette.icon01
@@ -156,10 +156,10 @@ function styles(theme: Theme, props: IProps) {
},
filmstripBackground: {
backgroundColor: theme.palette.uiBackground,
backgroundColor: theme.palette.filmstripBackgroundHover,
'&:hover, &:focus-within': {
backgroundColor: theme.palette.uiBackground
backgroundColor: theme.palette.filmstripBackgroundHover
}
},
@@ -198,7 +198,7 @@ function styles(theme: Theme, props: IProps) {
'&:hover': {
'& .dragHandle': {
backgroundColor: theme.palette.icon01
backgroundColor: theme.palette.filmstripDragHandleHover
}
},
@@ -206,7 +206,7 @@ function styles(theme: Theme, props: IProps) {
visibility: 'visible' as const,
'& .dragHandle': {
backgroundColor: theme.palette.icon01
backgroundColor: theme.palette.filmstripDragHandleHover
}
},
@@ -224,7 +224,7 @@ function styles(theme: Theme, props: IProps) {
},
dragHandle: {
backgroundColor: theme.palette.icon02,
backgroundColor: theme.palette.filmstripDragHandle,
height: '100px',
width: '3px',
borderRadius: '1px'
@@ -338,6 +338,11 @@ export interface IProps extends WithTranslation {
*/
_maxTopPanelHeight: number;
/**
* Whethere reduced UI feature is enabled or not.
*/
_reducedUI: boolean;
/**
* The participants in the call.
*/
@@ -546,6 +551,7 @@ class Filmstrip extends PureComponent <IProps, IState> {
_filmstripDisabled,
_localScreenShareId,
_mainFilmstripVisible,
_reducedUI,
_resizableFilmstrip,
_topPanelFilmstrip,
_topPanelMaxHeight,
@@ -589,6 +595,13 @@ class Filmstrip extends PureComponent <IProps, IState> {
}
}
// FIX: Until we move AudioTracksContainer to a more global place,
// we apply this css hot fix to hide Filmstrip but keep AudioTracksContainer in the DOM,
// so we don't have audio problems when reduced UI is enabled.
if (_reducedUI) {
filmstripStyle.display = 'none';
}
let toolbar: React.ReactNode = null;
if (!this.props._iAmRecorder && this.props._isFilmstripButtonEnabled
@@ -1120,6 +1133,7 @@ function _mapStateToProps(state: IReduxState, ownProps: any) {
const _currentLayout = getCurrentLayout(state);
const _isVerticalFilmstrip = _currentLayout === LAYOUTS.VERTICAL_FILMSTRIP_VIEW
|| (filmstripType === FILMSTRIP_TYPE.MAIN && _currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW);
const { reducedUI } = state['features/base/responsive-ui'];
return {
_className: className,
@@ -1137,6 +1151,7 @@ function _mapStateToProps(state: IReduxState, ownProps: any) {
_mainFilmstripVisible: notDisabled,
_maxFilmstripWidth: videoSpaceWidth - MIN_STAGE_VIEW_WIDTH,
_maxTopPanelHeight: clientHeight - MIN_STAGE_VIEW_HEIGHT,
_reducedUI: reducedUI,
_remoteParticipantsLength: _remoteParticipants?.length ?? 0,
_topPanelHeight: topPanelHeight.current,
_topPanelMaxHeight: topPanelHeight.current || TOP_FILMSTRIP_HEIGHT,

Some files were not shown because too many files have changed in this diff Show More