mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-04-05 10:20:19 +00:00
Compare commits
17 Commits
7226
...
android-ap
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d2521bc67a | ||
|
|
38a293f8f6 | ||
|
|
13e8f992b5 | ||
|
|
c384d0d3a9 | ||
|
|
2b71fa512b | ||
|
|
d381ceb040 | ||
|
|
e18c428f52 | ||
|
|
9fc32dc59b | ||
|
|
6f45622ef1 | ||
|
|
e56c7070c2 | ||
|
|
0aef7a36aa | ||
|
|
c030cf941e | ||
|
|
f6760e4ac7 | ||
|
|
9060c77307 | ||
|
|
a1d018eef4 | ||
|
|
3f724d8fb7 | ||
|
|
49d69a5a02 |
@@ -19,7 +19,7 @@ buildscript {
|
||||
ext {
|
||||
buildToolsVersion = "31.0.0"
|
||||
compileSdkVersion = 32
|
||||
minSdkVersion = 23
|
||||
minSdkVersion = 24
|
||||
targetSdkVersion = 32
|
||||
supportLibVersion = "28.0.0"
|
||||
|
||||
|
||||
@@ -1406,6 +1406,7 @@ var config = {
|
||||
disableAGC
|
||||
disableAP
|
||||
disableHPF
|
||||
disableLocalStats
|
||||
disableNS
|
||||
enableTalkWhileMuted
|
||||
forceJVB121Ratio
|
||||
|
||||
@@ -74,6 +74,10 @@
|
||||
a:active {
|
||||
color: black;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-corner {
|
||||
background: #3a3a3a;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -108,6 +108,10 @@
|
||||
#largeVideoContainer {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
#largeVideoWrapper {
|
||||
|
||||
@@ -11,11 +11,11 @@ import { setColorAlpha } from '../../react/features/base/util/helpers';
|
||||
import { setDocumentUrl } from '../../react/features/etherpad/actions';
|
||||
import { setFilmstripVisible } from '../../react/features/filmstrip/actions.any';
|
||||
import {
|
||||
joinLeaveNotificationsDisabled,
|
||||
setNotificationsEnabled,
|
||||
showNotification
|
||||
} from '../../react/features/notifications/actions';
|
||||
import { NOTIFICATION_TIMEOUT_TYPE } from '../../react/features/notifications/constants';
|
||||
import { joinLeaveNotificationsDisabled } from '../../react/features/notifications/functions';
|
||||
import {
|
||||
dockToolbox,
|
||||
setToolboxEnabled,
|
||||
|
||||
45
package-lock.json
generated
45
package-lock.json
generated
@@ -60,7 +60,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/v1623.0.0+c520877a/lib-jitsi-meet.tgz",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1626.0.0+a41aa571/lib-jitsi-meet.tgz",
|
||||
"lodash": "4.17.21",
|
||||
"moment": "2.29.4",
|
||||
"moment-duration-format": "2.2.2",
|
||||
@@ -12739,8 +12739,8 @@
|
||||
},
|
||||
"node_modules/lib-jitsi-meet": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1623.0.0+c520877a/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-dMNFJOfL3xFeh+HsZ4HoyWQY3ARU0Sz5tacIhEtg2Y2KJXGkAih+d8agHSZVAkTCU5S8JmX75q0gPc3MNZ5kWg==",
|
||||
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1626.0.0+a41aa571/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-0LK5EMCvtsAEQkjkrWzyniTl8BKtshAZxI3e9hGnA/RVDuULLXre7GEWd2zCP3tCfdxclkhqt7skQpfNhCTdqg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
@@ -12758,7 +12758,7 @@
|
||||
"patch-package": "6.5.1",
|
||||
"promise.allsettled": "1.0.4",
|
||||
"sdp-transform": "2.3.0",
|
||||
"strophe.js": "1.6.0",
|
||||
"strophe.js": "1.5.0",
|
||||
"strophejs-plugin-disco": "0.0.2",
|
||||
"strophejs-plugin-stream-management": "git+https://github.com/jitsi/strophejs-plugin-stream-management#679be5902097ed612fb5062b5549f3f32b6f5f47",
|
||||
"uuid": "8.1.0",
|
||||
@@ -17615,18 +17615,27 @@
|
||||
}
|
||||
},
|
||||
"node_modules/strophe.js": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/strophe.js/-/strophe.js-1.6.0.tgz",
|
||||
"integrity": "sha512-LE2B6nEJNUbF2Cl/p1tLIsXVJ9l86B/Z12HYYiO3n92VwYkhJ/5vJ+1ZMdwP9eN9GP8a3nbqfS5zE9umcK0FdA==",
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/strophe.js/-/strophe.js-1.5.0.tgz",
|
||||
"integrity": "sha512-H5tE/tZxPR5xP3jhXyQwsjnMSwQMf7vrn9r1OkufTApyGHYe8WjzhsfxtL3AFhVu7vFjXPPZBrmUOTm1ccYgOA==",
|
||||
"dependencies": {
|
||||
"abab": "^2.0.3",
|
||||
"karma-rollup-preprocessor": "^7.0.8"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@xmldom/xmldom": "0.8.3",
|
||||
"@xmldom/xmldom": "0.8.2",
|
||||
"ws": "^8.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/strophe.js/node_modules/@xmldom/xmldom": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.2.tgz",
|
||||
"integrity": "sha512-+R0juSseERyoPvnBQ/cZih6bpF7IpCXlWbHRoCRzYzqpz6gWHOgf8o4MOEf6KBVuOyqU+gCNLkCWVIJAro8XyQ==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/strophe.js/node_modules/ws": {
|
||||
"version": "8.13.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",
|
||||
@@ -29108,8 +29117,8 @@
|
||||
}
|
||||
},
|
||||
"lib-jitsi-meet": {
|
||||
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1623.0.0+c520877a/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-dMNFJOfL3xFeh+HsZ4HoyWQY3ARU0Sz5tacIhEtg2Y2KJXGkAih+d8agHSZVAkTCU5S8JmX75q0gPc3MNZ5kWg==",
|
||||
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1626.0.0+a41aa571/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-0LK5EMCvtsAEQkjkrWzyniTl8BKtshAZxI3e9hGnA/RVDuULLXre7GEWd2zCP3tCfdxclkhqt7skQpfNhCTdqg==",
|
||||
"requires": {
|
||||
"@jitsi/js-utils": "2.0.0",
|
||||
"@jitsi/logger": "2.0.0",
|
||||
@@ -29125,7 +29134,7 @@
|
||||
"patch-package": "6.5.1",
|
||||
"promise.allsettled": "1.0.4",
|
||||
"sdp-transform": "2.3.0",
|
||||
"strophe.js": "1.6.0",
|
||||
"strophe.js": "1.5.0",
|
||||
"strophejs-plugin-disco": "0.0.2",
|
||||
"strophejs-plugin-stream-management": "git+https://github.com/jitsi/strophejs-plugin-stream-management#679be5902097ed612fb5062b5549f3f32b6f5f47",
|
||||
"uuid": "8.1.0",
|
||||
@@ -32836,16 +32845,22 @@
|
||||
"dev": true
|
||||
},
|
||||
"strophe.js": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/strophe.js/-/strophe.js-1.6.0.tgz",
|
||||
"integrity": "sha512-LE2B6nEJNUbF2Cl/p1tLIsXVJ9l86B/Z12HYYiO3n92VwYkhJ/5vJ+1ZMdwP9eN9GP8a3nbqfS5zE9umcK0FdA==",
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/strophe.js/-/strophe.js-1.5.0.tgz",
|
||||
"integrity": "sha512-H5tE/tZxPR5xP3jhXyQwsjnMSwQMf7vrn9r1OkufTApyGHYe8WjzhsfxtL3AFhVu7vFjXPPZBrmUOTm1ccYgOA==",
|
||||
"requires": {
|
||||
"@xmldom/xmldom": "0.8.7",
|
||||
"@xmldom/xmldom": "0.8.2",
|
||||
"abab": "^2.0.3",
|
||||
"karma-rollup-preprocessor": "^7.0.8",
|
||||
"ws": "^8.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@xmldom/xmldom": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.2.tgz",
|
||||
"integrity": "sha512-+R0juSseERyoPvnBQ/cZih6bpF7IpCXlWbHRoCRzYzqpz6gWHOgf8o4MOEf6KBVuOyqU+gCNLkCWVIJAro8XyQ==",
|
||||
"optional": true
|
||||
},
|
||||
"ws": {
|
||||
"version": "8.13.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",
|
||||
|
||||
@@ -65,7 +65,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/v1623.0.0+c520877a/lib-jitsi-meet.tgz",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1626.0.0+a41aa571/lib-jitsi-meet.tgz",
|
||||
"lodash": "4.17.21",
|
||||
"moment": "2.29.4",
|
||||
"moment-duration-format": "2.2.2",
|
||||
|
||||
@@ -3,6 +3,7 @@ import { sendAnalytics } from '../../analytics/functions';
|
||||
import { appNavigate } from '../../app/actions';
|
||||
import { IReduxState, IStore } from '../../app/types';
|
||||
import { endpointMessageReceived } from '../../subtitles/actions.any';
|
||||
import { iAmVisitor } from '../../visitors/functions';
|
||||
import { getReplaceParticipant } from '../config/functions';
|
||||
import { disconnect } from '../connection/actions';
|
||||
import { JITSI_CONNECTION_CONFERENCE_KEY } from '../connection/constants';
|
||||
@@ -450,11 +451,12 @@ export function conferenceUniqueIdSet(conference: IJitsiConference) {
|
||||
*/
|
||||
export function _conferenceWillJoin(conference: IJitsiConference) {
|
||||
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
|
||||
const state = getState();
|
||||
const localTracks
|
||||
= getLocalTracks(getState()['features/base/tracks'])
|
||||
= getLocalTracks(state['features/base/tracks'])
|
||||
.map(t => t.jitsiTrack);
|
||||
|
||||
if (localTracks.length) {
|
||||
if (localTracks.length && !iAmVisitor(state)) {
|
||||
_addLocalTracksToConference(conference, localTracks);
|
||||
}
|
||||
|
||||
|
||||
@@ -308,6 +308,7 @@ export function getVisitorOptions(stateful: IStateful, params: Array<string>) {
|
||||
muc: config.oldConfig.hosts.muc
|
||||
},
|
||||
focusUserJid: focusJid,
|
||||
disableLocalStats: false,
|
||||
bosh: config.oldConfig.bosh && appendURLParam(config.oldConfig.bosh, 'customusername', username),
|
||||
websocket: config.oldConfig.websocket
|
||||
&& appendURLParam(config.oldConfig.websocket, 'customusername', username),
|
||||
@@ -338,6 +339,7 @@ export function getVisitorOptions(stateful: IStateful, params: Array<string>) {
|
||||
},
|
||||
focusUserJid: focusJid,
|
||||
disableFocus: true, // This flag disables sending the initial conference request
|
||||
disableLocalStats: true,
|
||||
bosh: config.bosh && appendURLParam(config.bosh, 'vnode', vnode),
|
||||
websocket: config.websocket && appendURLParam(config.websocket, 'vnode', vnode)
|
||||
};
|
||||
|
||||
@@ -29,7 +29,7 @@ interface IState {
|
||||
* A react {@code Component} that implements an expanded label as tooltip-like
|
||||
* component to explain the meaning of the {@code Label}.
|
||||
*/
|
||||
export default class ExpandedLabel<P extends IProps> extends Component<P, IState> {
|
||||
export default abstract class ExpandedLabel<P extends IProps> extends Component<P, IState> {
|
||||
/**
|
||||
* Instantiates a new {@code ExpandedLabel} instance.
|
||||
*
|
||||
@@ -85,7 +85,7 @@ export default class ExpandedLabel<P extends IProps> extends Component<P, IState
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
_getLabel: () => string;
|
||||
abstract _getLabel(): string;
|
||||
|
||||
/**
|
||||
* Defines the color of the expanded label. This function returns a default
|
||||
|
||||
@@ -163,7 +163,7 @@ class MultiSelectAutocomplete extends Component<IProps, IState> {
|
||||
const autoFocus = this.props.shouldFocus || false;
|
||||
const disabled = this.props.isDisabled || false;
|
||||
const placeholder = this.props.placeholder || '';
|
||||
const noMatchesFound = this.props.noMatchesFound || '';
|
||||
const noMatchesFound = this.state.loading ? this.props.loadingMessage : this.props.noMatchesFound || '';
|
||||
const errorDialog = this._renderError();
|
||||
|
||||
return (
|
||||
@@ -200,7 +200,7 @@ class MultiSelectAutocomplete extends Component<IProps, IState> {
|
||||
error: this.state.error && Boolean(filterValue),
|
||||
filterValue,
|
||||
isOpen: Boolean(this.state.items.length) && Boolean(filterValue),
|
||||
items: filterValue ? this.state.items : [],
|
||||
items: [],
|
||||
loading: Boolean(filterValue)
|
||||
});
|
||||
if (filterValue) {
|
||||
|
||||
@@ -24,6 +24,8 @@ interface IProps {
|
||||
selectedItems?: MultiSelectItem[];
|
||||
}
|
||||
|
||||
const MULTI_SELECT_HEIGHT = 200;
|
||||
|
||||
const useStyles = makeStyles()(theme => {
|
||||
return {
|
||||
container: {
|
||||
@@ -41,7 +43,7 @@ const useStyles = makeStyles()(theme => {
|
||||
borderRadius: `${Number(theme.shape.borderRadius)}px`,
|
||||
...withPixelLineHeight(theme.typography.bodyShortRegular),
|
||||
zIndex: 2,
|
||||
maxHeight: '400px',
|
||||
maxHeight: `${MULTI_SELECT_HEIGHT}px`,
|
||||
overflowY: 'auto',
|
||||
padding: '0'
|
||||
},
|
||||
@@ -128,7 +130,7 @@ const MultiSelect = ({
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
: <div>{noMatchesText}</div>
|
||||
: <div className = { classes.listItem }>{noMatchesText}</div>
|
||||
}
|
||||
</div>
|
||||
), [ items ]);
|
||||
|
||||
@@ -90,6 +90,8 @@ class ChatInput extends Component<IProps, IState> {
|
||||
if (isMobileBrowser()) {
|
||||
// Ensure textarea is not focused when opening chat on mobile browser.
|
||||
this._textArea?.current && this._textArea.current.blur();
|
||||
} else {
|
||||
this._focus();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,9 +29,9 @@ const styles = (theme: Theme) => {
|
||||
chatMessage: {
|
||||
display: 'inline-flex',
|
||||
padding: '12px',
|
||||
marginRight: '12px',
|
||||
backgroundColor: theme.palette.ui02,
|
||||
borderRadius: '4px 12px 12px 12px',
|
||||
boxSizing: 'border-box' as const,
|
||||
maxWidth: '100%',
|
||||
marginTop: '4px',
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@ import { getURLWithoutParamsNormalized } from '../base/connection/utils';
|
||||
import { hideDialog } from '../base/dialog/actions';
|
||||
import { isDialogOpen } from '../base/dialog/functions';
|
||||
import { getLocalizedDateFormatter } from '../base/i18n/dateUtil';
|
||||
import { translateToHTML } from '../base/i18n/functions';
|
||||
import i18next from '../base/i18n/i18next';
|
||||
import { browser } from '../base/lib-jitsi-meet';
|
||||
import { pinParticipant } from '../base/participants/actions';
|
||||
import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
|
||||
@@ -31,7 +33,7 @@ import { isCalendarEnabled } from '../calendar-sync/functions';
|
||||
import FeedbackDialog from '../feedback/components/FeedbackDialog';
|
||||
import { setFilmstripEnabled } from '../filmstrip/actions.any';
|
||||
import { isVpaasMeeting } from '../jaas/functions';
|
||||
import { hideNotification, showNotification } from '../notifications/actions';
|
||||
import { hideNotification, showNotification, showWarningNotification } from '../notifications/actions';
|
||||
import {
|
||||
CALENDAR_NOTIFICATION_ID,
|
||||
NOTIFICATION_ICON,
|
||||
@@ -190,9 +192,14 @@ function _checkIframe(state: IReduxState, dispatch: IStore['dispatch']) {
|
||||
if (inIframe() && state['features/base/config'].disableIframeAPI && !browser.isElectron()
|
||||
&& !isVpaasMeeting(state) && !allowIframe) {
|
||||
// show sticky notification and redirect in 5 minutes
|
||||
dispatch(showNotification({
|
||||
descriptionKey: 'notify.disabledIframe',
|
||||
descriptionArguments: { timeout: IFRAME_DISABLED_TIMEOUT_MINUTES }
|
||||
dispatch(showWarningNotification({
|
||||
description: translateToHTML(
|
||||
i18next.t.bind(i18next),
|
||||
'notify.disabledIframe',
|
||||
{
|
||||
timeout: IFRAME_DISABLED_TIMEOUT_MINUTES
|
||||
}
|
||||
)
|
||||
}, NOTIFICATION_TIMEOUT_TYPE.STICKY));
|
||||
|
||||
setTimeout(() => {
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { Text } from 'react-native-paper';
|
||||
import { connect } from 'react-redux';
|
||||
import { type Dispatch } from 'redux';
|
||||
|
||||
import { createToolbarEvent } from '../../../analytics/AnalyticsEvents';
|
||||
import { sendAnalytics } from '../../../analytics/functions';
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { RAISE_HAND_ENABLED } from '../../../base/flags/constants';
|
||||
import { getFeatureFlag } from '../../../base/flags/functions';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
@@ -15,6 +13,7 @@ import {
|
||||
getLocalParticipant,
|
||||
hasRaisedHand
|
||||
} from '../../../base/participants/functions';
|
||||
import { ILocalParticipant } from '../../../base/participants/types';
|
||||
import { IProps as AbstractButtonProps } from '../../../base/toolbox/components/AbstractButton';
|
||||
import Button from '../../../base/ui/components/native/Button';
|
||||
import { BUTTON_TYPES } from '../../../base/ui/constants.native';
|
||||
@@ -24,43 +23,33 @@ import styles from './styles';
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link RaiseHandButton}.
|
||||
*/
|
||||
type Props = AbstractButtonProps & {
|
||||
interface IProps extends AbstractButtonProps {
|
||||
|
||||
/**
|
||||
* Whether this button is enabled or not.
|
||||
*/
|
||||
_enabled: boolean,
|
||||
_enabled: boolean;
|
||||
|
||||
/**
|
||||
* The local participant.
|
||||
*/
|
||||
_localParticipant: Object,
|
||||
_localParticipant?: ILocalParticipant;
|
||||
|
||||
/**
|
||||
* Whether the participant raused their hand or not.
|
||||
*/
|
||||
_raisedHand: boolean,
|
||||
|
||||
/**
|
||||
* The redux {@code dispatch} function.
|
||||
*/
|
||||
dispatch: Dispatch<any>,
|
||||
|
||||
/**
|
||||
* Used for translation.
|
||||
*/
|
||||
t: Function,
|
||||
_raisedHand: boolean;
|
||||
|
||||
/**
|
||||
* Used to close the overflow menu after raise hand is clicked.
|
||||
*/
|
||||
onCancel: Function
|
||||
};
|
||||
onCancel: Function;
|
||||
}
|
||||
|
||||
/**
|
||||
* An implementation of a button to raise or lower hand.
|
||||
*/
|
||||
class RaiseHandButton extends Component<Props, *> {
|
||||
class RaiseHandButton extends Component<IProps> {
|
||||
accessibilityLabel = 'toolbar.accessibilityLabel.raiseHand';
|
||||
label = 'toolbar.raiseYourHand';
|
||||
toggledLabel = 'toolbar.lowerYourHand';
|
||||
@@ -68,10 +57,10 @@ class RaiseHandButton extends Component<Props, *> {
|
||||
/**
|
||||
* Initializes a new {@code RaiseHandButton} instance.
|
||||
*
|
||||
* @param {Props} props - The React {@code Component} props to initialize
|
||||
* @param {IProps} props - The React {@code Component} props to initialize
|
||||
* the new {@code RaiseHandButton} instance with.
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
// Bind event handlers so they are only bound once per instance.
|
||||
@@ -80,12 +69,6 @@ class RaiseHandButton extends Component<Props, *> {
|
||||
this._getLabel = this._getLabel.bind(this);
|
||||
}
|
||||
|
||||
_onClick: () => void;
|
||||
|
||||
_toggleRaisedHand: () => void;
|
||||
|
||||
_getLabel: () => string;
|
||||
|
||||
/**
|
||||
* Handles clicking / pressing the button.
|
||||
*
|
||||
@@ -163,9 +146,9 @@ class RaiseHandButton extends Component<Props, *> {
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @private
|
||||
* @returns {Props}
|
||||
* @returns {IProps}
|
||||
*/
|
||||
function _mapStateToProps(state): Object {
|
||||
function _mapStateToProps(state: IReduxState) {
|
||||
const _localParticipant = getLocalParticipant(state);
|
||||
const enabled = getFeatureFlag(state, RAISE_HAND_ENABLED, true);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// @flow
|
||||
|
||||
import React, { useCallback } from 'react';
|
||||
import { Text, TouchableHighlight } from 'react-native';
|
||||
import { WithTranslation } from 'react-i18next';
|
||||
import { ColorValue, GestureResponderEvent, Text, TouchableHighlight, ViewStyle } from 'react-native';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import { createReactionMenuEvent } from '../../../analytics/AnalyticsEvents';
|
||||
@@ -11,71 +10,65 @@ import { StyleType } from '../../../base/styles/functions.native';
|
||||
import { addReactionToBuffer } from '../../actions.any';
|
||||
import { REACTIONS } from '../../constants';
|
||||
|
||||
|
||||
export type ReactionStyles = {
|
||||
|
||||
/**
|
||||
* Style for the gif button.
|
||||
*/
|
||||
gifButton: StyleType,
|
||||
|
||||
/**
|
||||
* Style for the button.
|
||||
*/
|
||||
style: StyleType,
|
||||
|
||||
/**
|
||||
* Underlay color for the button.
|
||||
*/
|
||||
underlayColor: StyleType,
|
||||
|
||||
/**
|
||||
* Style for the emoji text on the button.
|
||||
*/
|
||||
emoji: StyleType,
|
||||
|
||||
/**
|
||||
* Style for the label text on the button.
|
||||
*/
|
||||
text?: StyleType,
|
||||
interface IReactionStyles {
|
||||
|
||||
/**
|
||||
* Style for text container. Used on raise hand button.
|
||||
*/
|
||||
container?: StyleType
|
||||
container?: StyleType;
|
||||
|
||||
/**
|
||||
* Style for the emoji text on the button.
|
||||
*/
|
||||
emoji: StyleType;
|
||||
|
||||
/**
|
||||
* Style for the gif button.
|
||||
*/
|
||||
gifButton: StyleType;
|
||||
|
||||
/**
|
||||
* Style for the button.
|
||||
*/
|
||||
style: StyleType;
|
||||
|
||||
/**
|
||||
* Style for the label text on the button.
|
||||
*/
|
||||
text?: StyleType;
|
||||
|
||||
/**
|
||||
* Underlay color for the button.
|
||||
*/
|
||||
underlayColor: ColorValue;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link ReactionButton}.
|
||||
*/
|
||||
type Props = {
|
||||
interface IProps extends WithTranslation {
|
||||
|
||||
/**
|
||||
* Component children.
|
||||
*/
|
||||
children?: ReactNode,
|
||||
children?: React.ReactNode;
|
||||
|
||||
/**
|
||||
* External click handler.
|
||||
*/
|
||||
onClick?: Function,
|
||||
|
||||
/**
|
||||
* Collection of styles for the button.
|
||||
*/
|
||||
styles: ReactionStyles,
|
||||
onClick?: (e?: GestureResponderEvent) => void;
|
||||
|
||||
/**
|
||||
* The reaction to be sent.
|
||||
*/
|
||||
reaction: string,
|
||||
reaction?: string;
|
||||
|
||||
/**
|
||||
* Invoked to obtain translated strings.
|
||||
* Collection of styles for the button.
|
||||
*/
|
||||
t: Function
|
||||
};
|
||||
styles: IReactionStyles;
|
||||
}
|
||||
|
||||
/**
|
||||
* An implementation of a button to send a reaction.
|
||||
@@ -88,11 +81,13 @@ function ReactionButton({
|
||||
styles,
|
||||
reaction,
|
||||
t
|
||||
}: Props) {
|
||||
}: IProps) {
|
||||
const dispatch = useDispatch();
|
||||
const _onClick = useCallback(() => {
|
||||
dispatch(addReactionToBuffer(reaction));
|
||||
sendAnalytics(createReactionMenuEvent(reaction));
|
||||
if (reaction) {
|
||||
dispatch(addReactionToBuffer(reaction));
|
||||
sendAnalytics(createReactionMenuEvent(reaction));
|
||||
}
|
||||
}, [ reaction ]);
|
||||
|
||||
return (
|
||||
@@ -100,9 +95,9 @@ function ReactionButton({
|
||||
accessibilityLabel = { t(`toolbar.accessibilityLabel.${reaction}`) }
|
||||
accessibilityRole = 'button'
|
||||
onPress = { onClick || _onClick }
|
||||
style = { [ styles.style, children && styles?.gifButton ] }
|
||||
style = { [ styles.style, children && styles?.gifButton ] as ViewStyle[] }
|
||||
underlayColor = { styles.underlayColor }>
|
||||
{children ?? <Text style = { styles.emoji }>{REACTIONS[reaction].emoji}</Text>}
|
||||
{children ?? <Text style = { styles.emoji }>{REACTIONS[reaction ?? ''].emoji}</Text>}
|
||||
</TouchableHighlight>
|
||||
);
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
// @flow
|
||||
|
||||
import React, { useCallback } from 'react';
|
||||
import { Image, View } from 'react-native';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import ColorSchemeRegistry from '../../../base/color-scheme/ColorSchemeRegistry';
|
||||
import { isGifEnabled } from '../../../gifs/functions';
|
||||
import { isGifEnabled } from '../../../gifs/functions.native';
|
||||
import { navigate } from '../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
|
||||
import { screen } from '../../../mobile/navigation/routes';
|
||||
import { REACTIONS } from '../../constants';
|
||||
@@ -16,18 +15,18 @@ import ReactionButton from './ReactionButton';
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link ReactionMenu}.
|
||||
*/
|
||||
type Props = {
|
||||
interface IProps {
|
||||
|
||||
/**
|
||||
* Used to close the overflow menu after raise hand is clicked.
|
||||
*/
|
||||
onCancel: Function,
|
||||
onCancel: Function;
|
||||
|
||||
/**
|
||||
* Whether or not it's displayed in the overflow menu.
|
||||
*/
|
||||
overflowMenu: boolean
|
||||
};
|
||||
overflowMenu: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Animated reaction emoji.
|
||||
@@ -37,8 +36,8 @@ type Props = {
|
||||
function ReactionMenu({
|
||||
onCancel,
|
||||
overflowMenu
|
||||
}: Props) {
|
||||
const _styles = useSelector(state => ColorSchemeRegistry.get(state, 'Toolbox'));
|
||||
}: IProps) {
|
||||
const _styles: any = useSelector((state: IReduxState) => ColorSchemeRegistry.get(state, 'Toolbox'));
|
||||
const gifEnabled = useSelector(isGifEnabled);
|
||||
|
||||
const openGifMenu = useCallback(() => {
|
||||
@@ -62,7 +61,7 @@ function ReactionMenu({
|
||||
<ReactionButton
|
||||
onClick = { openGifMenu }
|
||||
styles = { _styles.reactionButton }>
|
||||
<Image
|
||||
<Image // @ts-ignore
|
||||
height = { 22 }
|
||||
source = { require('../../../../../images/GIPHY_icon.png') } />
|
||||
</ReactionButton>
|
||||
@@ -1,9 +1,8 @@
|
||||
// @flow
|
||||
|
||||
import React, { PureComponent } from 'react';
|
||||
import React, { ComponentType, PureComponent } from 'react';
|
||||
import { SafeAreaView, TouchableWithoutFeedback, View } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState, IStore } from '../../../app/types';
|
||||
import ColorSchemeRegistry from '../../../base/color-scheme/ColorSchemeRegistry';
|
||||
import { hideDialog } from '../../../base/dialog/actions';
|
||||
import { isDialogOpen } from '../../../base/dialog/functions';
|
||||
@@ -15,38 +14,38 @@ import ReactionMenu from './ReactionMenu';
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link ReactionMenuDialog}.
|
||||
*/
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The color-schemed stylesheet of the feature.
|
||||
*/
|
||||
_styles: StyleType,
|
||||
|
||||
/**
|
||||
* True if the dialog is currently visible, false otherwise.
|
||||
*/
|
||||
_isOpen: boolean,
|
||||
|
||||
/**
|
||||
* The width of the screen.
|
||||
*/
|
||||
_width: number,
|
||||
interface IProps {
|
||||
|
||||
/**
|
||||
* The height of the screen.
|
||||
*/
|
||||
_height: number,
|
||||
_height: number;
|
||||
|
||||
/**
|
||||
* True if the dialog is currently visible, false otherwise.
|
||||
*/
|
||||
_isOpen: boolean;
|
||||
|
||||
/**
|
||||
* Number of conference participants.
|
||||
*/
|
||||
_participantCount: number,
|
||||
_participantCount: number;
|
||||
|
||||
/**
|
||||
* The color-schemed stylesheet of the feature.
|
||||
*/
|
||||
_styles: StyleType;
|
||||
|
||||
/**
|
||||
* The width of the screen.
|
||||
*/
|
||||
_width: number;
|
||||
|
||||
/**
|
||||
* Used for hiding the dialog when the selection was completed.
|
||||
*/
|
||||
dispatch: Function
|
||||
};
|
||||
dispatch: IStore['dispatch'];
|
||||
}
|
||||
|
||||
/**
|
||||
* The exported React {@code Component}. We need it to execute
|
||||
@@ -55,19 +54,19 @@ type Props = {
|
||||
* XXX It does not break our coding style rule to not utilize globals for state,
|
||||
* because it is merely another name for {@code export}'s {@code default}.
|
||||
*/
|
||||
let ReactionMenu_; // eslint-disable-line prefer-const
|
||||
let ReactionMenu_: ComponentType<any>; // eslint-disable-line prefer-const
|
||||
|
||||
/**
|
||||
* Implements a React {@code Component} with some extra actions in addition to
|
||||
* those in the toolbar.
|
||||
*/
|
||||
class ReactionMenuDialog extends PureComponent<Props> {
|
||||
class ReactionMenuDialog extends PureComponent<IProps> {
|
||||
/**
|
||||
* Initializes a new {@code ReactionMenuDialog} instance.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
// Bind event handlers so they are only bound once per instance.
|
||||
@@ -103,8 +102,6 @@ class ReactionMenuDialog extends PureComponent<Props> {
|
||||
);
|
||||
}
|
||||
|
||||
_onCancel: () => boolean;
|
||||
|
||||
/**
|
||||
* Hides this {@code ReactionMenuDialog}.
|
||||
*
|
||||
@@ -127,11 +124,11 @@ class ReactionMenuDialog extends PureComponent<Props> {
|
||||
*
|
||||
* @param {Object} state - Redux state.
|
||||
* @private
|
||||
* @returns {Props}
|
||||
* @returns {IProps}
|
||||
*/
|
||||
function _mapStateToProps(state) {
|
||||
function _mapStateToProps(state: IReduxState) {
|
||||
return {
|
||||
_isOpen: isDialogOpen(state, ReactionMenu_),
|
||||
_isOpen: isDialogOpen(state, ReactionMenu_), // @ts-ignore
|
||||
_styles: ColorSchemeRegistry.get(state, 'Toolbox').reactionDialog,
|
||||
_width: state['features/base/responsive-ui'].clientWidth,
|
||||
_height: state['features/base/responsive-ui'].clientHeight,
|
||||
@@ -1,8 +1,6 @@
|
||||
// @flow
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
import { type Dispatch } from 'redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { openDialog } from '../../../base/dialog/actions';
|
||||
import { isDialogOpen } from '../../../base/dialog/functions';
|
||||
import { RAISE_HAND_ENABLED } from '../../../base/flags/constants';
|
||||
@@ -19,28 +17,23 @@ import ReactionMenuDialog from './ReactionMenuDialog';
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link ReactionsMenuButton}.
|
||||
*/
|
||||
type Props = AbstractButtonProps & {
|
||||
interface IProps extends AbstractButtonProps {
|
||||
|
||||
/**
|
||||
* Whether the participant raised their hand or not.
|
||||
*/
|
||||
_raisedHand: boolean,
|
||||
_raisedHand: boolean;
|
||||
|
||||
/**
|
||||
* Whether or not the reactions menu is open.
|
||||
*/
|
||||
_reactionsOpen: boolean,
|
||||
|
||||
/**
|
||||
* The redux {@code dispatch} function.
|
||||
*/
|
||||
dispatch: Dispatch<any>
|
||||
};
|
||||
_reactionsOpen: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* An implementation of a button to raise or lower hand.
|
||||
*/
|
||||
class ReactionsMenuButton extends AbstractButton<Props, *> {
|
||||
class ReactionsMenuButton extends AbstractButton<IProps> {
|
||||
accessibilityLabel = 'toolbar.accessibilityLabel.reactionsMenu';
|
||||
icon = IconRaiseHand;
|
||||
label = 'toolbar.openReactionsMenu';
|
||||
@@ -75,9 +68,9 @@ class ReactionsMenuButton extends AbstractButton<Props, *> {
|
||||
* @param {Object} state - The Redux state.
|
||||
* @param {Object} ownProps - The properties explicitly passed to the component instance.
|
||||
* @private
|
||||
* @returns {Props}
|
||||
* @returns {IProps}
|
||||
*/
|
||||
function _mapStateToProps(state, ownProps): Object {
|
||||
function _mapStateToProps(state: IReduxState, ownProps: any) {
|
||||
const _localParticipant = getLocalParticipant(state);
|
||||
const enabled = getFeatureFlag(state, RAISE_HAND_ENABLED, true);
|
||||
const { visible = enabled } = ownProps;
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Component } from 'react';
|
||||
import { WithTranslation } from 'react-i18next';
|
||||
|
||||
import { createLiveStreamingDialogEvent } from '../../../analytics/AnalyticsEvents';
|
||||
import { sendAnalytics } from '../../../analytics/functions';
|
||||
@@ -10,7 +11,7 @@ import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
|
||||
* The type of the React {@code Component} props of
|
||||
* {@link AbstractStartLiveStreamDialog}.
|
||||
*/
|
||||
export interface IProps {
|
||||
export interface IProps extends WithTranslation {
|
||||
|
||||
/**
|
||||
* The {@code JitsiConference} for the current conference.
|
||||
@@ -39,10 +40,7 @@ export interface IProps {
|
||||
*/
|
||||
dispatch: Function;
|
||||
|
||||
/**
|
||||
* Invoked to obtain translated strings.
|
||||
*/
|
||||
t: Function;
|
||||
navigation?: any;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -38,7 +38,7 @@ export interface IProps extends WithTranslation {
|
||||
*/
|
||||
_liveStreaming: LiveStreamingProps;
|
||||
|
||||
classes: any;
|
||||
classes?: any;
|
||||
|
||||
/**
|
||||
* Callback invoked when the entered stream key has changed.
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
import { WithTranslation } from 'react-i18next';
|
||||
import { Text, View, ViewStyle } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../../app/types';
|
||||
import { _abstractMapStateToProps } from '../../../../base/dialog/functions';
|
||||
import { translate } from '../../../../base/i18n/functions';
|
||||
import { StyleType } from '../../../../base/styles/functions.native';
|
||||
import { setGoogleAPIState } from '../../../../google-api/actions';
|
||||
import GoogleSignInButton from '../../../../google-api/components/GoogleSignInButton.native';
|
||||
import {
|
||||
GOOGLE_API_STATES,
|
||||
GOOGLE_SCOPE_YOUTUBE
|
||||
} from '../../../../google-api/constants';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import googleApi from '../../../../google-api/googleApi.native';
|
||||
import logger from '../../../logger';
|
||||
|
||||
@@ -21,52 +22,47 @@ import styles from './styles';
|
||||
/**
|
||||
* Prop type of the component {@code GoogleSigninForm}.
|
||||
*/
|
||||
type Props = {
|
||||
interface IProps extends WithTranslation {
|
||||
|
||||
/**
|
||||
* Style of the dialogs feature.
|
||||
*/
|
||||
_dialogStyles: StyleType,
|
||||
_dialogStyles: any;
|
||||
|
||||
/**
|
||||
* The Redux dispatch Function.
|
||||
*/
|
||||
dispatch: Function,
|
||||
dispatch: Function;
|
||||
|
||||
/**
|
||||
* The current state of the Google api as defined in {@code constants.js}.
|
||||
*/
|
||||
googleAPIState: number,
|
||||
googleAPIState: number;
|
||||
|
||||
/**
|
||||
* The recently received Google response.
|
||||
*/
|
||||
googleResponse: Object,
|
||||
googleResponse: any;
|
||||
|
||||
/**
|
||||
* A callback to be invoked when an authenticated user changes, so
|
||||
* then we can get (or clear) the YouTube stream key.
|
||||
*/
|
||||
onUserChanged: Function,
|
||||
|
||||
/**
|
||||
* Function to be used to translate i18n labels.
|
||||
*/
|
||||
t: Function
|
||||
};
|
||||
onUserChanged: Function;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class to render a google sign in form, or a google stream picker dialog.
|
||||
*
|
||||
* @augments Component
|
||||
*/
|
||||
class GoogleSigninForm extends Component<Props> {
|
||||
class GoogleSigninForm extends Component<IProps> {
|
||||
/**
|
||||
* Instantiates a new {@code GoogleSigninForm} component.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this._logGoogleError = this._logGoogleError.bind(this);
|
||||
@@ -86,7 +82,7 @@ class GoogleSigninForm extends Component<Props> {
|
||||
scopes: [ GOOGLE_SCOPE_YOUTUBE ]
|
||||
});
|
||||
|
||||
googleApi.signInSilently().then(response => {
|
||||
googleApi.signInSilently().then((response: any) => {
|
||||
this._setApiState(response
|
||||
? GOOGLE_API_STATES.SIGNED_IN
|
||||
: GOOGLE_API_STATES.LOADED,
|
||||
@@ -95,7 +91,7 @@ class GoogleSigninForm extends Component<Props> {
|
||||
this._setApiState(GOOGLE_API_STATES.LOADED);
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
.catch((error: Error) => {
|
||||
this._logGoogleError(error);
|
||||
this._setApiState(GOOGLE_API_STATES.NOT_AVAILABLE);
|
||||
});
|
||||
@@ -109,9 +105,7 @@ class GoogleSigninForm extends Component<Props> {
|
||||
render() {
|
||||
const { _dialogStyles, t } = this.props;
|
||||
const { googleAPIState, googleResponse } = this.props;
|
||||
const signedInUser = googleResponse
|
||||
&& googleResponse.user
|
||||
&& googleResponse.user.email;
|
||||
const signedInUser = googleResponse?.user?.email;
|
||||
|
||||
if (googleAPIState === GOOGLE_API_STATES.NOT_AVAILABLE
|
||||
|| googleAPIState === GOOGLE_API_STATES.NEEDS_LOADING
|
||||
@@ -124,8 +118,8 @@ class GoogleSigninForm extends Component<Props> {
|
||||
: t('liveStreaming.signInCTA');
|
||||
|
||||
return (
|
||||
<View style = { styles.formWrapper }>
|
||||
<View style = { styles.helpText }>
|
||||
<View style = { styles.formWrapper as ViewStyle }>
|
||||
<View style = { styles.helpText as ViewStyle }>
|
||||
<Text
|
||||
style = { [
|
||||
_dialogStyles.text,
|
||||
@@ -142,8 +136,6 @@ class GoogleSigninForm extends Component<Props> {
|
||||
);
|
||||
}
|
||||
|
||||
_logGoogleError: Object => void;
|
||||
|
||||
/**
|
||||
* A helper function to log developer related errors.
|
||||
*
|
||||
@@ -151,14 +143,12 @@ class GoogleSigninForm extends Component<Props> {
|
||||
* @param {Object} error - The error to be logged.
|
||||
* @returns {void}
|
||||
*/
|
||||
_logGoogleError(error) {
|
||||
_logGoogleError(error: Error) {
|
||||
// NOTE: This is a developer error message, not intended for the
|
||||
// user to see.
|
||||
logger.error('Google API error. Possible cause: bad config.', error);
|
||||
}
|
||||
|
||||
_onGoogleButtonPress: () => void;
|
||||
|
||||
/**
|
||||
* Callback to be invoked when the user presses the Google button,
|
||||
* regardless of being logged in or out.
|
||||
@@ -169,7 +159,7 @@ class GoogleSigninForm extends Component<Props> {
|
||||
_onGoogleButtonPress() {
|
||||
const { googleResponse } = this.props;
|
||||
|
||||
if (googleResponse && googleResponse.user) {
|
||||
if (googleResponse?.user) {
|
||||
// the user is signed in
|
||||
this._onSignOut();
|
||||
} else {
|
||||
@@ -177,8 +167,6 @@ class GoogleSigninForm extends Component<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
_onSignIn: () => void;
|
||||
|
||||
/**
|
||||
* Initiates a sign in if the user is not signed in yet.
|
||||
*
|
||||
@@ -186,13 +174,11 @@ class GoogleSigninForm extends Component<Props> {
|
||||
* @returns {void}
|
||||
*/
|
||||
_onSignIn() {
|
||||
googleApi.signIn().then(response => {
|
||||
googleApi.signIn().then((response: any) => {
|
||||
this._setApiState(GOOGLE_API_STATES.SIGNED_IN, response);
|
||||
}, this._logGoogleError);
|
||||
}
|
||||
|
||||
_onSignOut: () => void;
|
||||
|
||||
/**
|
||||
* Initiates a sign out if the user is signed in.
|
||||
*
|
||||
@@ -200,7 +186,7 @@ class GoogleSigninForm extends Component<Props> {
|
||||
* @returns {void}
|
||||
*/
|
||||
_onSignOut() {
|
||||
googleApi.signOut().then(response => {
|
||||
googleApi.signOut().then((response: any) => {
|
||||
this._setApiState(GOOGLE_API_STATES.LOADED, response);
|
||||
}, this._logGoogleError);
|
||||
}
|
||||
@@ -213,7 +199,7 @@ class GoogleSigninForm extends Component<Props> {
|
||||
* @param {?Object} googleResponse - The response from the API.
|
||||
* @returns {void}
|
||||
*/
|
||||
_setApiState(apiState, googleResponse) {
|
||||
_setApiState(apiState: number, googleResponse?: Object) {
|
||||
this.props.onUserChanged(googleResponse);
|
||||
this.props.dispatch(setGoogleAPIState(apiState, googleResponse));
|
||||
}
|
||||
@@ -230,7 +216,7 @@ class GoogleSigninForm extends Component<Props> {
|
||||
* googleResponse: Object
|
||||
* }}
|
||||
*/
|
||||
function _mapStateToProps(state: Object) {
|
||||
function _mapStateToProps(state: IReduxState) {
|
||||
const { googleAPIState, googleResponse } = state['features/google-api'];
|
||||
|
||||
return {
|
||||
@@ -1,7 +1,6 @@
|
||||
// @flow
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../../app/types';
|
||||
import { openDialog } from '../../../../base/dialog/actions';
|
||||
import { LIVE_STREAMING_ENABLED } from '../../../../base/flags/constants';
|
||||
import { getFeatureFlag } from '../../../../base/flags/functions';
|
||||
@@ -9,12 +8,15 @@ import { translate } from '../../../../base/i18n/functions';
|
||||
import { navigate }
|
||||
from '../../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
|
||||
import { screen } from '../../../../mobile/navigation/routes';
|
||||
import AbstractLiveStreamButton,
|
||||
{ _mapStateToProps as _abstractMapStateToProps } from '../AbstractLiveStreamButton';
|
||||
import type { Props } from '../AbstractStartLiveStreamDialog';
|
||||
import AbstractLiveStreamButton, {
|
||||
IProps as AbstractProps,
|
||||
_mapStateToProps as _abstractMapStateToProps
|
||||
} from '../AbstractLiveStreamButton';
|
||||
import { IProps } from '../AbstractStartLiveStreamDialog';
|
||||
|
||||
import StopLiveStreamDialog from './StopLiveStreamDialog';
|
||||
|
||||
type Props = IProps & AbstractProps;
|
||||
|
||||
/**
|
||||
* Button for opening the live stream settings screen.
|
||||
@@ -48,7 +50,7 @@ class LiveStreamButton extends AbstractLiveStreamButton<Props> {
|
||||
* @private
|
||||
* @returns {Props}
|
||||
*/
|
||||
export function mapStateToProps(state: Object, ownProps: Object) {
|
||||
export function mapStateToProps(state: IReduxState, ownProps: any) {
|
||||
const enabled = getFeatureFlag(state, LIVE_STREAMING_ENABLED, true);
|
||||
const abstractProps = _abstractMapStateToProps(state, ownProps);
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { translate } from '../../../../base/i18n/functions';
|
||||
import JitsiScreen from '../../../../base/modal/components/JitsiScreen';
|
||||
import { StyleType } from '../../../../base/styles/functions.any';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import googleApi from '../../../../google-api/googleApi.native';
|
||||
import HeaderNavigationButton
|
||||
from '../../../../mobile/navigation/components/HeaderNavigationButton';
|
||||
import { goBack }
|
||||
from '../../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
|
||||
import { setLiveStreamKey } from '../../../actions';
|
||||
import AbstractStartLiveStreamDialog,
|
||||
{ type Props, _mapStateToProps } from '../AbstractStartLiveStreamDialog';
|
||||
import AbstractStartLiveStreamDialog, { IProps, _mapStateToProps } from '../AbstractStartLiveStreamDialog';
|
||||
|
||||
import GoogleSigninForm from './GoogleSigninForm';
|
||||
import StreamKeyForm from './StreamKeyForm';
|
||||
@@ -23,13 +23,13 @@ import styles from './styles';
|
||||
* A React Component for requesting a YouTube stream key to use for live
|
||||
* streaming of the current conference.
|
||||
*/
|
||||
class StartLiveStreamDialog extends AbstractStartLiveStreamDialog<Props> {
|
||||
class StartLiveStreamDialog extends AbstractStartLiveStreamDialog<IProps> {
|
||||
/**
|
||||
* Constructor of the component.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
// Bind event handlers so they are only bound once per instance.
|
||||
@@ -60,8 +60,6 @@ class StartLiveStreamDialog extends AbstractStartLiveStreamDialog<Props> {
|
||||
});
|
||||
}
|
||||
|
||||
_onStartPress: () => void;
|
||||
|
||||
/**
|
||||
* Starts live stream session and goes back to the previous screen.
|
||||
*
|
||||
@@ -78,7 +76,7 @@ class StartLiveStreamDialog extends AbstractStartLiveStreamDialog<Props> {
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<JitsiScreen style = { styles.startLiveStreamContainer }>
|
||||
<JitsiScreen style = { styles.startLiveStreamContainer as StyleType }>
|
||||
<GoogleSigninForm
|
||||
onUserChanged = { this._onUserChanged } />
|
||||
<StreamKeyPicker
|
||||
@@ -87,20 +85,12 @@ class StartLiveStreamDialog extends AbstractStartLiveStreamDialog<Props> {
|
||||
<StreamKeyForm
|
||||
onChange = { this._onStreamKeyChangeNative }
|
||||
value = {
|
||||
this.state.streamKey || this.props._streamKey
|
||||
this.state.streamKey || this.props._streamKey || ''
|
||||
} />
|
||||
</JitsiScreen>
|
||||
);
|
||||
}
|
||||
|
||||
_onCancel: () => boolean;
|
||||
|
||||
_onSubmit: () => boolean;
|
||||
|
||||
_onStreamKeyChange: string => void;
|
||||
|
||||
_onStreamKeyChangeNative: string => void;
|
||||
|
||||
/**
|
||||
* Callback to handle stream key changes.
|
||||
*
|
||||
@@ -113,13 +103,11 @@ class StartLiveStreamDialog extends AbstractStartLiveStreamDialog<Props> {
|
||||
* @param {string} streamKey - The new key value.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onStreamKeyChangeNative(streamKey) {
|
||||
_onStreamKeyChangeNative(streamKey: string) {
|
||||
this.props.dispatch(setLiveStreamKey(streamKey));
|
||||
this._onStreamKeyChange(streamKey);
|
||||
}
|
||||
|
||||
_onStreamKeyPick: string => void;
|
||||
|
||||
/**
|
||||
* Callback to be invoked when the user selects a stream from the picker.
|
||||
*
|
||||
@@ -127,14 +115,12 @@ class StartLiveStreamDialog extends AbstractStartLiveStreamDialog<Props> {
|
||||
* @param {string} streamKey - The key of the selected stream.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onStreamKeyPick(streamKey) {
|
||||
_onStreamKeyPick(streamKey: string) {
|
||||
this.setState({
|
||||
streamKey
|
||||
});
|
||||
}
|
||||
|
||||
_onUserChanged: Object => void;
|
||||
|
||||
/**
|
||||
* A callback to be invoked when an authenticated user changes, so
|
||||
* then we can get (or clear) the YouTube stream key.
|
||||
@@ -145,12 +131,12 @@ class StartLiveStreamDialog extends AbstractStartLiveStreamDialog<Props> {
|
||||
* @param {Object} response - The retrieved signin response.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onUserChanged(response) {
|
||||
_onUserChanged(response: Object) {
|
||||
if (response) {
|
||||
googleApi.getTokens()
|
||||
.then(tokens => {
|
||||
.then((tokens: any) => {
|
||||
googleApi.getYouTubeLiveStreams(tokens.accessToken)
|
||||
.then(broadcasts => {
|
||||
.then((broadcasts: any) => {
|
||||
this.setState({
|
||||
broadcasts
|
||||
});
|
||||
@@ -1,5 +1,3 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
@@ -30,8 +28,6 @@ class StopLiveStreamDialog extends AbstractStopLiveStreamDialog {
|
||||
onSubmit = { this._onSubmit } />
|
||||
);
|
||||
}
|
||||
|
||||
_onSubmit: () => boolean;
|
||||
}
|
||||
|
||||
export default translate(connect(_mapStateToProps)(StopLiveStreamDialog));
|
||||
@@ -1,42 +1,41 @@
|
||||
import React from 'react';
|
||||
import { Linking, Text, View } from 'react-native';
|
||||
import { Linking, Text, View, ViewStyle } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../../app/types';
|
||||
import { _abstractMapStateToProps } from '../../../../base/dialog/functions';
|
||||
import { translate } from '../../../../base/i18n/functions';
|
||||
import { StyleType } from '../../../../base/styles/functions.native';
|
||||
import Button from '../../../../base/ui/components/native/Button';
|
||||
import Input from '../../../../base/ui/components/native/Input';
|
||||
import { BUTTON_TYPES } from '../../../../base/ui/constants.native';
|
||||
import AbstractStreamKeyForm, {
|
||||
type Props as AbstractProps
|
||||
IProps as AbstractProps
|
||||
} from '../AbstractStreamKeyForm';
|
||||
import { getLiveStreaming } from '../functions';
|
||||
|
||||
|
||||
import styles from './styles';
|
||||
|
||||
type Props = AbstractProps & {
|
||||
interface IProps extends AbstractProps {
|
||||
|
||||
/**
|
||||
* Style of the dialogs feature.
|
||||
*/
|
||||
_dialogStyles: StyleType
|
||||
};
|
||||
_dialogStyles: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* A React Component for entering a key for starting a YouTube live stream.
|
||||
*
|
||||
* @augments Component
|
||||
*/
|
||||
class StreamKeyForm extends AbstractStreamKeyForm<Props> {
|
||||
class StreamKeyForm extends AbstractStreamKeyForm<IProps> {
|
||||
/**
|
||||
* Initializes a new {@code StreamKeyForm} instance.
|
||||
*
|
||||
* @param {Props} props - The React {@code Component} props to initialize
|
||||
* @param {IProps} props - The React {@code Component} props to initialize
|
||||
* the new {@code StreamKeyForm} instance with.
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
// Bind event handlers so they are only bound once per instance.
|
||||
@@ -57,7 +56,7 @@ class StreamKeyForm extends AbstractStreamKeyForm<Props> {
|
||||
|
||||
return (
|
||||
<>
|
||||
<View style = { styles.formWrapper }>
|
||||
<View style = { styles.formWrapper as ViewStyle }>
|
||||
<Input
|
||||
customStyles = {{
|
||||
input: styles.streamKeyInput,
|
||||
@@ -65,7 +64,7 @@ class StreamKeyForm extends AbstractStreamKeyForm<Props> {
|
||||
onChange = { this._onInputChange }
|
||||
placeholder = { t('liveStreaming.enterStreamKey') }
|
||||
value = { this.props.value } />
|
||||
<View style = { styles.formValidationItem }>
|
||||
<View style = { styles.formValidationItem as ViewStyle }>
|
||||
{
|
||||
this.state.showValidationError && <Text
|
||||
style = { [
|
||||
@@ -77,7 +76,7 @@ class StreamKeyForm extends AbstractStreamKeyForm<Props> {
|
||||
}
|
||||
</View>
|
||||
</View>
|
||||
<View style = { styles.formButtonsWrapper }>
|
||||
<View style = { styles.formButtonsWrapper as ViewStyle }>
|
||||
<Button
|
||||
accessibilityLabel = 'liveStreaming.streamIdHelp'
|
||||
labelKey = 'liveStreaming.streamIdHelp'
|
||||
@@ -101,10 +100,6 @@ class StreamKeyForm extends AbstractStreamKeyForm<Props> {
|
||||
);
|
||||
}
|
||||
|
||||
_onInputChange: Object => void;
|
||||
|
||||
_onOpenGooglePrivacyPolicy: () => void;
|
||||
|
||||
/**
|
||||
* Opens the Google Privacy Policy web page.
|
||||
*
|
||||
@@ -115,8 +110,6 @@ class StreamKeyForm extends AbstractStreamKeyForm<Props> {
|
||||
Linking.openURL(this.props._liveStreaming.dataPrivacyURL);
|
||||
}
|
||||
|
||||
_onOpenHelp: () => void;
|
||||
|
||||
/**
|
||||
* Opens the information link on how to manually locate a YouTube broadcast
|
||||
* stream key.
|
||||
@@ -132,8 +125,6 @@ class StreamKeyForm extends AbstractStreamKeyForm<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
_onOpenYoutubeTerms: () => void;
|
||||
|
||||
/**
|
||||
* Opens the YouTube terms and conditions web page.
|
||||
*
|
||||
@@ -155,7 +146,7 @@ class StreamKeyForm extends AbstractStreamKeyForm<Props> {
|
||||
* _liveStreaming: LiveStreamingProps
|
||||
* }}
|
||||
*/
|
||||
function _mapStateToProps(state: Object) {
|
||||
function _mapStateToProps(state: IReduxState) {
|
||||
return {
|
||||
..._abstractMapStateToProps(state),
|
||||
_liveStreaming: getLiveStreaming(state)
|
||||
@@ -1,52 +1,46 @@
|
||||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { WithTranslation } from 'react-i18next';
|
||||
import {
|
||||
Linking,
|
||||
Text,
|
||||
TouchableHighlight,
|
||||
TouchableOpacity,
|
||||
View
|
||||
View,
|
||||
ViewStyle
|
||||
} from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { _abstractMapStateToProps } from '../../../../base/dialog/functions';
|
||||
import { translate } from '../../../../base/i18n/functions';
|
||||
import { StyleType } from '../../../../base/styles/functions.native';
|
||||
import { YOUTUBE_LIVE_DASHBOARD_URL } from '../constants';
|
||||
|
||||
import styles, { ACTIVE_OPACITY, TOUCHABLE_UNDERLAY } from './styles';
|
||||
|
||||
type Props = {
|
||||
interface IProps extends WithTranslation {
|
||||
|
||||
/**
|
||||
* Style of the dialogs feature.
|
||||
*/
|
||||
_dialogStyles: StyleType,
|
||||
_dialogStyles: any;
|
||||
|
||||
/**
|
||||
* The list of broadcasts the user can pick from.
|
||||
*/
|
||||
broadcasts: ?Array<Object>,
|
||||
broadcasts?: Array<{ key: string; title: string; }>;
|
||||
|
||||
/**
|
||||
* Callback to be invoked when the user picked a broadcast. To be invoked
|
||||
* with a single key (string).
|
||||
*/
|
||||
onChange: Function,
|
||||
|
||||
/**
|
||||
* Function to be used to translate i18n labels.
|
||||
*/
|
||||
t: Function
|
||||
onChange: Function;
|
||||
}
|
||||
|
||||
type State = {
|
||||
interface IState {
|
||||
|
||||
/**
|
||||
* The key of the currently selected stream.
|
||||
*/
|
||||
streamKey: ?string
|
||||
/**
|
||||
* The key of the currently selected stream.
|
||||
*/
|
||||
streamKey?: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,14 +51,14 @@ type State = {
|
||||
* a later point to unify mobile and web logic for this functionality. But it's
|
||||
* out of the scope for now of the mobile live streaming functionality.
|
||||
*/
|
||||
class StreamKeyPicker extends Component<Props, State> {
|
||||
class StreamKeyPicker extends Component<IProps, IState> {
|
||||
|
||||
/**
|
||||
* Instantiates a new instance of StreamKeyPicker.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
@@ -89,7 +83,7 @@ class StreamKeyPicker extends Component<Props, State> {
|
||||
|
||||
if (!broadcasts.length) {
|
||||
return (
|
||||
<View style = { styles.formWrapper }>
|
||||
<View style = { styles.formWrapper as ViewStyle }>
|
||||
<TouchableOpacity
|
||||
onPress = { this._onOpenYoutubeDashboard }>
|
||||
<Text
|
||||
@@ -106,8 +100,8 @@ class StreamKeyPicker extends Component<Props, State> {
|
||||
}
|
||||
|
||||
return (
|
||||
<View style = { styles.formWrapper }>
|
||||
<View style = { styles.streamKeyPickerCta }>
|
||||
<View style = { styles.formWrapper as ViewStyle }>
|
||||
<View style = { styles.streamKeyPickerCta as ViewStyle }>
|
||||
<Text
|
||||
style = { [
|
||||
_dialogStyles.text,
|
||||
@@ -116,7 +110,7 @@ class StreamKeyPicker extends Component<Props, State> {
|
||||
{ this.props.t('liveStreaming.choose') }
|
||||
</Text>
|
||||
</View>
|
||||
<View style = { styles.streamKeyPickerWrapper } >
|
||||
<View style = { styles.streamKeyPickerWrapper as ViewStyle } >
|
||||
{ broadcasts.map((broadcast, index) =>
|
||||
(<TouchableHighlight
|
||||
activeOpacity = { ACTIVE_OPACITY }
|
||||
@@ -126,7 +120,7 @@ class StreamKeyPicker extends Component<Props, State> {
|
||||
styles.streamKeyPickerItem,
|
||||
this.state.streamKey === broadcast.key
|
||||
? styles.streamKeyPickerItemHighlight : null
|
||||
] }
|
||||
] as ViewStyle[] }
|
||||
underlayColor = { TOUCHABLE_UNDERLAY }>
|
||||
<Text
|
||||
style = { [
|
||||
@@ -142,8 +136,6 @@ class StreamKeyPicker extends Component<Props, State> {
|
||||
);
|
||||
}
|
||||
|
||||
_onOpenYoutubeDashboard: () => void;
|
||||
|
||||
/**
|
||||
* Opens the link which should display the YouTube broadcast live stream
|
||||
* key.
|
||||
@@ -155,8 +147,6 @@ class StreamKeyPicker extends Component<Props, State> {
|
||||
Linking.openURL(YOUTUBE_LIVE_DASHBOARD_URL);
|
||||
}
|
||||
|
||||
_onStreamPick: string => Function;
|
||||
|
||||
/**
|
||||
* Callback to be invoked when the user picks a stream from the list.
|
||||
*
|
||||
@@ -164,7 +154,7 @@ class StreamKeyPicker extends Component<Props, State> {
|
||||
* @param {string} streamKey - The key of the stream selected.
|
||||
* @returns {Function}
|
||||
*/
|
||||
_onStreamPick(streamKey) {
|
||||
_onStreamPick(streamKey: string) {
|
||||
return () => {
|
||||
this.setState({
|
||||
streamKey
|
||||
@@ -1,5 +1,3 @@
|
||||
// @flow
|
||||
|
||||
import { createStyleSheet } from '../../../../base/styles/functions.native';
|
||||
import BaseTheme from '../../../../base/ui/components/BaseTheme.native';
|
||||
|
||||
@@ -83,6 +83,8 @@ export interface IProps extends WithTranslation {
|
||||
* The redux dispatch function.
|
||||
*/
|
||||
dispatch: IStore['dispatch'];
|
||||
|
||||
navigation: any;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
@@ -386,6 +388,7 @@ class AbstractStartRecordingDialog extends Component<IProps, IState> {
|
||||
* {@code StartRecordingDialog} component.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @param {any} _ownProps - Component's own props.
|
||||
* @private
|
||||
* @returns {{
|
||||
* _appKey: string,
|
||||
@@ -399,7 +402,7 @@ class AbstractStartRecordingDialog extends Component<IProps, IState> {
|
||||
* _token: string
|
||||
* }}
|
||||
*/
|
||||
export function mapStateToProps(state: IReduxState) {
|
||||
export function mapStateToProps(state: IReduxState, _ownProps: any) {
|
||||
const {
|
||||
transcription,
|
||||
recordingService,
|
||||
|
||||
@@ -92,7 +92,7 @@ export interface IProps extends WithTranslation {
|
||||
/**
|
||||
* Whether or not we should only record the local streams.
|
||||
*/
|
||||
localRecordingOnlySelf: boolean;
|
||||
localRecordingOnlySelf?: boolean;
|
||||
|
||||
/**
|
||||
* The function will be called when there are changes related to the
|
||||
@@ -103,7 +103,7 @@ export interface IProps extends WithTranslation {
|
||||
/**
|
||||
* Callback to change the local recording only self setting.
|
||||
*/
|
||||
onLocalRecordingSelfChange: () => void;
|
||||
onLocalRecordingSelfChange?: () => void;
|
||||
|
||||
/**
|
||||
* Callback to be invoked on sharing setting change.
|
||||
|
||||
@@ -1,35 +1,31 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import { translate } from '../../../../base/i18n/functions';
|
||||
import { IconHighlight } from '../../../../base/icons/svg';
|
||||
import Label from '../../../../base/label/components/native/Label';
|
||||
import BaseTheme from '../../../../base/ui/components/BaseTheme';
|
||||
import AbstractHighlightButton, {
|
||||
type Props as AbstractProps,
|
||||
IProps as AbstractProps,
|
||||
_abstractMapStateToProps
|
||||
} from '../AbstractHighlightButton';
|
||||
import styles from '../styles.native';
|
||||
|
||||
type Props = AbstractProps & {
|
||||
_disabled: boolean,
|
||||
interface IProps extends AbstractProps {
|
||||
|
||||
_disabled: boolean;
|
||||
|
||||
/**
|
||||
* Flag controlling visibility of the component.
|
||||
*/
|
||||
_visible: boolean,
|
||||
|
||||
dispatch: Dispatch<any>
|
||||
};
|
||||
_visible: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* React {@code Component} responsible for displaying an action that
|
||||
* allows users to highlight a meeting moment.
|
||||
*/
|
||||
export class HighlightButton extends AbstractHighlightButton<Props> {
|
||||
export class HighlightButton extends AbstractHighlightButton<IProps> {
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
@@ -52,7 +48,6 @@ export class HighlightButton extends AbstractHighlightButton<Props> {
|
||||
<Label
|
||||
icon = { IconHighlight }
|
||||
iconColor = { BaseTheme.palette.field01 }
|
||||
id = 'highlightMeetingLabel'
|
||||
style = { styles.highlightButton }
|
||||
text = { t('recording.highlight') }
|
||||
textStyle = { styles.highlightButtonText } />
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Text, View } from 'react-native';
|
||||
import { Text, View, ViewStyle } from 'react-native';
|
||||
import { batch, useDispatch } from 'react-redux';
|
||||
|
||||
import { hideSheet } from '../../../../base/dialog/actions';
|
||||
@@ -28,7 +28,7 @@ const HighlightDialog = () => {
|
||||
<Text style = { styles.highlightDialogText }>
|
||||
{ t('recording.highlightMomentSucessDescription') }
|
||||
</Text>
|
||||
<View style = { styles.highlightDialogButtonsContainer } >
|
||||
<View style = { styles.highlightDialogButtonsContainer as ViewStyle } >
|
||||
<Button
|
||||
accessibilityLabel = 'dialog.Cancel'
|
||||
labelKey = 'dialog.Cancel'
|
||||
@@ -1,8 +1,7 @@
|
||||
// @flow
|
||||
|
||||
import { Platform } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../../app/types';
|
||||
import { openDialog } from '../../../../base/dialog/actions';
|
||||
import { IOS_RECORDING_ENABLED, RECORDING_ENABLED } from '../../../../base/flags/constants';
|
||||
import { getFeatureFlag } from '../../../../base/flags/functions';
|
||||
@@ -10,12 +9,15 @@ import { translate } from '../../../../base/i18n/functions';
|
||||
import { navigate }
|
||||
from '../../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
|
||||
import { screen } from '../../../../mobile/navigation/routes';
|
||||
import type { Props } from '../../LiveStream/AbstractStartLiveStreamDialog';
|
||||
import AbstractRecordButton,
|
||||
{ _mapStateToProps as _abstractMapStateToProps } from '../AbstractRecordButton';
|
||||
import { IProps } from '../../LiveStream/AbstractStartLiveStreamDialog';
|
||||
import AbstractRecordButton, {
|
||||
IProps as AbstractProps,
|
||||
_mapStateToProps as _abstractMapStateToProps
|
||||
} from '../AbstractRecordButton';
|
||||
|
||||
import StopRecordingDialog from './StopRecordingDialog';
|
||||
|
||||
type Props = IProps & AbstractProps;
|
||||
|
||||
/**
|
||||
* Button for opening a screen where a recording session can be started.
|
||||
@@ -49,10 +51,10 @@ class RecordButton extends AbstractRecordButton<Props> {
|
||||
* @private
|
||||
* @returns {Props}
|
||||
*/
|
||||
export function mapStateToProps(state: Object, ownProps: Object) {
|
||||
export function mapStateToProps(state: IReduxState) {
|
||||
const enabled = getFeatureFlag(state, RECORDING_ENABLED, true);
|
||||
const iosEnabled = Platform.OS !== 'ios' || getFeatureFlag(state, IOS_RECORDING_ENABLED, false);
|
||||
const abstractProps = _abstractMapStateToProps(state, ownProps);
|
||||
const abstractProps = _abstractMapStateToProps(state);
|
||||
|
||||
return {
|
||||
...abstractProps,
|
||||
@@ -9,7 +9,7 @@ import { goBack } from
|
||||
'../../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
|
||||
import { RECORDING_TYPES } from '../../../constants';
|
||||
import AbstractStartRecordingDialog, {
|
||||
type Props,
|
||||
IProps,
|
||||
mapStateToProps
|
||||
} from '../AbstractStartRecordingDialog';
|
||||
import styles from '../styles.native';
|
||||
@@ -23,14 +23,14 @@ import StartRecordingDialogContent from './StartRecordingDialogContent';
|
||||
*
|
||||
* @augments Component
|
||||
*/
|
||||
class StartRecordingDialog extends AbstractStartRecordingDialog<Props> {
|
||||
class StartRecordingDialog extends AbstractStartRecordingDialog {
|
||||
|
||||
/**
|
||||
* Constructor of the component.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this._onStartPress = this._onStartPress.bind(this);
|
||||
@@ -66,7 +66,7 @@ class StartRecordingDialog extends AbstractStartRecordingDialog<Props> {
|
||||
* @inheritdoc
|
||||
* @returns {void}
|
||||
*/
|
||||
componentDidUpdate(prevProps) {
|
||||
componentDidUpdate(prevProps: IProps) {
|
||||
super.componentDidUpdate(prevProps);
|
||||
|
||||
const { navigation, t } = this.props;
|
||||
@@ -83,8 +83,6 @@ class StartRecordingDialog extends AbstractStartRecordingDialog<Props> {
|
||||
});
|
||||
}
|
||||
|
||||
_onStartPress: () => void;
|
||||
|
||||
/**
|
||||
* Starts recording session and goes back to the previous screen.
|
||||
*
|
||||
@@ -94,8 +92,6 @@ class StartRecordingDialog extends AbstractStartRecordingDialog<Props> {
|
||||
this._onSubmit() && goBack();
|
||||
}
|
||||
|
||||
isStartRecordingDisabled: () => boolean;
|
||||
|
||||
/**
|
||||
* Disables start recording button.
|
||||
*
|
||||
@@ -154,11 +150,6 @@ class StartRecordingDialog extends AbstractStartRecordingDialog<Props> {
|
||||
</JitsiScreen>
|
||||
);
|
||||
}
|
||||
|
||||
_areIntegrationsEnabled: () => boolean;
|
||||
_onSubmit: () => boolean;
|
||||
_onSelectedRecordingServiceChanged: (string) => void;
|
||||
_onSharingSettingChanged: () => void;
|
||||
}
|
||||
|
||||
export default translate(connect(mapStateToProps)(StartRecordingDialog));
|
||||
@@ -1,12 +1,10 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import ConfirmDialog from '../../../../base/dialog/components/native/ConfirmDialog';
|
||||
import { translate } from '../../../../base/i18n/functions';
|
||||
import AbstractStopRecordingDialog, {
|
||||
type Props,
|
||||
IProps,
|
||||
_mapStateToProps
|
||||
} from '../AbstractStopRecordingDialog';
|
||||
|
||||
@@ -16,7 +14,7 @@ import AbstractStopRecordingDialog, {
|
||||
*
|
||||
* @augments Component
|
||||
*/
|
||||
class StopRecordingDialog extends AbstractStopRecordingDialog<Props> {
|
||||
class StopRecordingDialog extends AbstractStopRecordingDialog<IProps> {
|
||||
|
||||
/**
|
||||
* Implements {@code Component#render}.
|
||||
@@ -30,8 +28,6 @@ class StopRecordingDialog extends AbstractStopRecordingDialog<Props> {
|
||||
onSubmit = { this._onSubmit } />
|
||||
);
|
||||
}
|
||||
|
||||
_onSubmit: () => boolean;
|
||||
}
|
||||
|
||||
export default translate(connect(_mapStateToProps)(StopRecordingDialog));
|
||||
@@ -1,23 +1,21 @@
|
||||
// @flow
|
||||
|
||||
import ColorSchemeRegistry from '../../../base/color-scheme/ColorSchemeRegistry';
|
||||
import { schemeColor } from '../../../base/color-scheme/functions';
|
||||
import { BoxModel } from '../../../base/styles/components/styles/BoxModel';
|
||||
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
export const DROPBOX_LOGO = require('../../../../../images/dropboxLogo_square.png');
|
||||
export const ICON_CLOUD = require('../../../../../images/icon-cloud.png');
|
||||
export const ICON_INFO = require('../../../../../images/icon-info.png');
|
||||
export const ICON_USERS = require('../../../../../images/icon-users.png');
|
||||
export const LOCAL_RECORDING = require('../../../../../images/downloadLocalRecording.png');
|
||||
export const TRACK_COLOR = BaseTheme.palette.ui07;
|
||||
|
||||
/* eslint-enable @typescript-eslint/no-var-requires */
|
||||
|
||||
// XXX The "standard" {@code BoxModel.padding} has been deemed insufficient in
|
||||
// the special case(s) of the recording feature below.
|
||||
const _PADDING = BoxModel.padding * 1.5;
|
||||
|
||||
|
||||
const header = {
|
||||
alignItems: 'center',
|
||||
flex: 0,
|
||||
@@ -109,11 +109,12 @@ class StartRecordingDialog extends AbstractStartRecordingDialog {
|
||||
* Maps redux state to component props.
|
||||
*
|
||||
* @param {Object} state - Redux state.
|
||||
* @param {any} ownProps - Component's own props.
|
||||
* @returns {Object}
|
||||
*/
|
||||
function mapStateToProps(state: IReduxState) {
|
||||
function mapStateToProps(state: IReduxState, ownProps: any) {
|
||||
return {
|
||||
...abstractMapStateToProps(state),
|
||||
...abstractMapStateToProps(state, ownProps),
|
||||
_screenshotCaptureEnabled: isScreenshotCaptureEnabled(state, true, false)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -23,6 +23,9 @@ import {
|
||||
LOCAL_RECORDING
|
||||
} from '../styles.web';
|
||||
|
||||
const EMPTY_FUNCTION = () => {
|
||||
// empty
|
||||
};
|
||||
|
||||
/**
|
||||
* The start recording dialog content for the mobile application.
|
||||
@@ -359,10 +362,10 @@ class StartRecordingDialogContent extends AbstractStartRecordingDialogContent<IP
|
||||
{t('recording.onlyRecordSelf')}
|
||||
</Text>
|
||||
<Switch
|
||||
checked = { localRecordingOnlySelf }
|
||||
checked = { Boolean(localRecordingOnlySelf) }
|
||||
className = 'recording-switch'
|
||||
disabled = { isValidating }
|
||||
onChange = { onLocalRecordingSelfChange } />
|
||||
onChange = { onLocalRecordingSelfChange ?? EMPTY_FUNCTION } />
|
||||
</Container>
|
||||
</Container>
|
||||
)}
|
||||
|
||||
@@ -1,35 +1,34 @@
|
||||
// @flow
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
import ExpandedLabel, { Props as AbstractProps } from '../../../base/label/components/native/ExpandedLabel';
|
||||
import ExpandedLabel, { IProps as AbstractProps } from '../../../base/label/components/native/ExpandedLabel';
|
||||
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
|
||||
import { getSessionStatusToShow } from '../../functions';
|
||||
|
||||
type Props = AbstractProps & {
|
||||
interface IProps extends AbstractProps {
|
||||
|
||||
/**
|
||||
* The status of the highermost priority session.
|
||||
*/
|
||||
_status: ?string,
|
||||
_status?: string;
|
||||
|
||||
/**
|
||||
* The recording mode this indicator should display.
|
||||
*/
|
||||
mode: string,
|
||||
mode: string;
|
||||
|
||||
/**
|
||||
* Function to be used to translate i18n labels.
|
||||
*/
|
||||
t: Function
|
||||
t: Function;
|
||||
}
|
||||
|
||||
/**
|
||||
* A react {@code Component} that implements an expanded label as tooltip-like
|
||||
* component to explain the meaning of the {@code RecordingLabel}.
|
||||
*/
|
||||
class RecordingExpandedLabel extends ExpandedLabel<Props> {
|
||||
class RecordingExpandedLabel extends ExpandedLabel<IProps> {
|
||||
|
||||
/**
|
||||
* Returns the label specific text of this {@code ExpandedLabel}.
|
||||
@@ -70,13 +69,13 @@ class RecordingExpandedLabel extends ExpandedLabel<Props> {
|
||||
* {@code RecordingExpandedLabel}'s props.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @param {Props} ownProps - The component's own props.
|
||||
* @param {IProps} ownProps - The component's own props.
|
||||
* @private
|
||||
* @returns {{
|
||||
* _status: ?string
|
||||
* }}
|
||||
*/
|
||||
function _mapStateToProps(state: Object, ownProps: Props) {
|
||||
function _mapStateToProps(state: IReduxState, ownProps: any) {
|
||||
const { mode } = ownProps;
|
||||
|
||||
return {
|
||||
@@ -1,11 +1,10 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
import Label from '../../../base/label/components/native/Label';
|
||||
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
|
||||
import { StyleType } from '../../../base/styles/functions.any';
|
||||
import AbstractRecordingLabel, {
|
||||
_mapStateToProps
|
||||
} from '../AbstractRecordingLabel';
|
||||
@@ -26,7 +25,7 @@ class RecordingLabel extends AbstractRecordingLabel {
|
||||
* @inheritdoc
|
||||
*/
|
||||
_renderLabel() {
|
||||
let status = 'on';
|
||||
let status: 'on' | 'in_progress' | 'off' = 'on';
|
||||
|
||||
switch (this.props._status) {
|
||||
case JitsiRecordingConstants.status.PENDING:
|
||||
@@ -40,12 +39,10 @@ class RecordingLabel extends AbstractRecordingLabel {
|
||||
return (
|
||||
<Label
|
||||
status = { status }
|
||||
style = { styles.indicatorStyle }
|
||||
text = { this.props.t(this._getLabelKey()) } />
|
||||
style = { styles.indicatorStyle as StyleType }
|
||||
text = { this.props.t(this._getLabelKey() ?? '') } />
|
||||
);
|
||||
}
|
||||
|
||||
_getLabelKey: () => ?string;
|
||||
}
|
||||
|
||||
export default translate(connect(_mapStateToProps)(RecordingLabel));
|
||||
@@ -1,5 +1,3 @@
|
||||
// @flow
|
||||
|
||||
import { createStyleSheet } from '../../../base/styles/functions.native';
|
||||
import BaseTheme from '../../../base/ui/components/BaseTheme';
|
||||
|
||||
@@ -13,6 +13,7 @@ import Checkbox from '../../../../base/ui/components/web/Checkbox';
|
||||
import ContextMenu from '../../../../base/ui/components/web/ContextMenu';
|
||||
import ContextMenuItem from '../../../../base/ui/components/web/ContextMenuItem';
|
||||
import ContextMenuItemGroup from '../../../../base/ui/components/web/ContextMenuItemGroup';
|
||||
import { checkBlurSupport } from '../../../../virtual-background/functions';
|
||||
import { openSettingsDialog } from '../../../actions';
|
||||
import { SETTINGS_TABS } from '../../../constants';
|
||||
import { createLocalVideoTracks } from '../../../functions.web';
|
||||
@@ -286,6 +287,8 @@ const VideoSettingsContent = ({
|
||||
}
|
||||
}, [ videoDeviceIds ]);
|
||||
|
||||
const virtualBackgroundSupported = checkBlurSupport();
|
||||
|
||||
return (
|
||||
<ContextMenu
|
||||
aria-labelledby = 'video-settings-button'
|
||||
@@ -298,11 +301,11 @@ const VideoSettingsContent = ({
|
||||
{trackData.map((data, i) => _renderPreviewEntry(data, i))}
|
||||
</ContextMenuItemGroup>
|
||||
<ContextMenuItemGroup>
|
||||
<ContextMenuItem
|
||||
{ virtualBackgroundSupported && <ContextMenuItem
|
||||
accessibilityLabel = 'virtualBackground.title'
|
||||
icon = { IconImage }
|
||||
onClick = { selectBackground }
|
||||
text = { t('virtualBackground.title') } />
|
||||
text = { t('virtualBackground.title') } /> }
|
||||
<div
|
||||
className = { classes.checkboxContainer }
|
||||
onClick = { stopPropagation }>
|
||||
|
||||
157
resources/prosody-plugins/mod_measure_message_count.lua
Normal file
157
resources/prosody-plugins/mod_measure_message_count.lua
Normal file
@@ -0,0 +1,157 @@
|
||||
-- Measure the number of messages used in a meeting. Sends amplitude event.
|
||||
-- Needs to be activated under the muc component where the limit needs to be applied (main muc and breakout muc)
|
||||
-- Copyright (C) 2023-present 8x8, Inc.
|
||||
|
||||
local jid = require 'util.jid';
|
||||
local http = require 'net.http';
|
||||
local cjson_safe = require 'cjson.safe'
|
||||
|
||||
local amplitude_endpoint = module:get_option_string('amplitude_endpoint', 'https://api2.amplitude.com/2/httpapi');
|
||||
local amplitude_api_key = module:get_option_string('amplitude_api_key');
|
||||
|
||||
if not amplitude_api_key then
|
||||
module:log("warn", "No 'amplitude_api_key' option set, disabling amplitude reporting");
|
||||
return
|
||||
end
|
||||
|
||||
local muc_domain_base = module:get_option_string('muc_mapper_domain_base');
|
||||
local isBreakoutRoom = module.host == 'breakout.' .. muc_domain_base;
|
||||
|
||||
local util = module:require 'util';
|
||||
local is_healthcheck_room = util.is_healthcheck_room;
|
||||
local extract_subdomain = util.extract_subdomain;
|
||||
|
||||
module:log('info', 'Loading measure message count');
|
||||
|
||||
local shard_name = module:context(muc_domain_base):get_option_string('shard_name');
|
||||
local region_name = module:context(muc_domain_base):get_option_string('region_name');
|
||||
local release_number = module:context(muc_domain_base):get_option_string('release_number');
|
||||
local http_headers = {
|
||||
['User-Agent'] = 'Prosody ('..prosody.version..'; '..prosody.platform..')',
|
||||
['Content-Type'] = 'application/json'
|
||||
};
|
||||
|
||||
local inspect = require "inspect"
|
||||
|
||||
function table.clone(t)
|
||||
return {table.unpack(t)}
|
||||
end
|
||||
|
||||
local function event_cb(content_, code_, response_, request_)
|
||||
if code_ == 200 or code_ == 204 then
|
||||
module:log('debug', 'URL Callback: Code %s, Content %s, Request (host %s, path %s, body %s), Response: %s',
|
||||
code_, content_, request_.host, request_.path, inspect(request_.body), inspect(response_));
|
||||
else
|
||||
module:log('warn', 'URL Callback non successful: Code %s, Content %s, Request (%s), Response: %s',
|
||||
code_, content_, inspect(request_), inspect(response_));
|
||||
end
|
||||
end
|
||||
|
||||
function send_event(room)
|
||||
local user_properties = {
|
||||
shard_name = shard_name;
|
||||
region_name = region_name;
|
||||
release_number = release_number;
|
||||
};
|
||||
|
||||
local node = jid.split(room.jid);
|
||||
local subdomain, room_name = extract_subdomain(node);
|
||||
user_properties.tenant = subdomain or '/';
|
||||
user_properties.conference_name = room_name or node;
|
||||
|
||||
local event_properties = {
|
||||
messages_count = room._muc_messages_count or 0;
|
||||
polls_count = room._muc_polls_count or 0;
|
||||
};
|
||||
|
||||
if room.created_timestamp then
|
||||
event_properties.duration = (os.time() * 1000 - room.created_timestamp) / 1000;
|
||||
end
|
||||
|
||||
local event = {
|
||||
api_key = amplitude_api_key;
|
||||
events = {
|
||||
{
|
||||
user_id = room._data.meetingId;
|
||||
device_id = room._data.meetingId;
|
||||
event_type = 'conference_ended';
|
||||
event_properties = event_properties;
|
||||
user_properties = user_properties;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
local request = http.request(amplitude_endpoint, {
|
||||
headers = http_headers,
|
||||
method = "POST",
|
||||
body = cjson_safe.encode(event)
|
||||
}, event_cb);
|
||||
end
|
||||
|
||||
function on_message(event)
|
||||
local stanza = event.stanza;
|
||||
local body = stanza:get_child('body');
|
||||
|
||||
if not body then
|
||||
-- we ignore messages without body - lobby, polls ...
|
||||
return;
|
||||
end
|
||||
|
||||
local session = event.origin;
|
||||
if not session or not session.jitsi_web_query_room then
|
||||
return false;
|
||||
end
|
||||
|
||||
-- get room name with tenant and find room.
|
||||
local room = get_room_by_name_and_subdomain(session.jitsi_web_query_room, session.jitsi_web_query_prefix);
|
||||
if not room then
|
||||
module:log('warn', 'No room found found for %s/%s',
|
||||
session.jitsi_web_query_prefix, session.jitsi_web_query_room);
|
||||
return false;
|
||||
end
|
||||
|
||||
if not room._muc_messages_count then
|
||||
room._muc_messages_count = 0;
|
||||
end
|
||||
|
||||
room._muc_messages_count = room._muc_messages_count + 1;
|
||||
end
|
||||
|
||||
-- Conference ended, send stats
|
||||
function room_destroyed(event)
|
||||
local room, session = event.room, event.origin;
|
||||
|
||||
if is_healthcheck_room(room.jid) then
|
||||
return;
|
||||
end
|
||||
|
||||
if isBreakoutRoom then
|
||||
return;
|
||||
end
|
||||
send_event(room);
|
||||
end
|
||||
|
||||
function poll_created(event)
|
||||
local session = event.event.origin;
|
||||
|
||||
-- get room name with tenant and find room.
|
||||
local room = get_room_by_name_and_subdomain(session.jitsi_web_query_room, session.jitsi_web_query_prefix);
|
||||
if not room then
|
||||
module:log('warn', 'No room found found for %s/%s',
|
||||
session.jitsi_web_query_prefix, session.jitsi_web_query_room);
|
||||
return false;
|
||||
end
|
||||
|
||||
if not room._muc_polls_count then
|
||||
room._muc_polls_count = 0;
|
||||
end
|
||||
|
||||
room._muc_polls_count = room._muc_polls_count + 1;
|
||||
end
|
||||
|
||||
module:hook('message/full', on_message); -- private messages
|
||||
module:hook('message/bare', on_message); -- room messages
|
||||
|
||||
module:hook('muc-room-destroyed', room_destroyed, -1);
|
||||
|
||||
module:hook('poll-created', poll_created);
|
||||
Reference in New Issue
Block a user