refactor(html5): recording start/stop modal (#20679)
* refactor(html5): recording start/stop modal * Add suport for errors
This commit is contained in:
parent
1683f4c3fe
commit
1d737fbada
@ -18,12 +18,14 @@ const propTypes = {
|
||||
confirmButtonColor: PropTypes.string,
|
||||
disableConfirmButton: PropTypes.bool,
|
||||
description: PropTypes.string,
|
||||
hideConfirmButton: PropTypes.bool,
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
confirmButtonColor: 'primary',
|
||||
disableConfirmButton: false,
|
||||
description: '',
|
||||
hideConfirmButton: false,
|
||||
};
|
||||
|
||||
class ConfirmationModal extends Component {
|
||||
@ -46,6 +48,8 @@ class ConfirmationModal extends Component {
|
||||
checkboxMessageId,
|
||||
confirmButtonColor,
|
||||
confirmButtonLabel,
|
||||
cancelButtonLabel,
|
||||
hideConfirmButton,
|
||||
confirmButtonDataTest,
|
||||
confirmParam,
|
||||
disableConfirmButton,
|
||||
@ -92,18 +96,20 @@ class ConfirmationModal extends Component {
|
||||
</Styled.Description>
|
||||
|
||||
<Styled.Footer>
|
||||
<Styled.ConfirmationButton
|
||||
color={confirmButtonColor}
|
||||
label={confirmButtonLabel ? confirmButtonLabel : intl.formatMessage(messages.yesLabel)}
|
||||
disabled={disableConfirmButton}
|
||||
data-test={confirmButtonDataTest}
|
||||
onClick={() => {
|
||||
onConfirm(confirmParam, checked);
|
||||
setIsOpen(false);
|
||||
}}
|
||||
/>
|
||||
{!hideConfirmButton && (
|
||||
<Styled.ConfirmationButton
|
||||
color={confirmButtonColor}
|
||||
label={confirmButtonLabel || intl.formatMessage(messages.yesLabel)}
|
||||
disabled={disableConfirmButton}
|
||||
data-test={confirmButtonDataTest}
|
||||
onClick={() => {
|
||||
onConfirm(confirmParam, checked);
|
||||
setIsOpen(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<Styled.CancelButton
|
||||
label={intl.formatMessage(messages.noLabel)}
|
||||
label={cancelButtonLabel || intl.formatMessage(messages.noLabel)}
|
||||
onClick={() => setIsOpen(false)}
|
||||
/>
|
||||
</Styled.Footer>
|
||||
|
@ -1,95 +0,0 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
import ConfirmationModal from '/imports/ui/components/common/modal/confirmation/component';
|
||||
|
||||
const intlMessages = defineMessages({
|
||||
startTitle: {
|
||||
id: 'app.recording.startTitle',
|
||||
description: 'start recording title',
|
||||
},
|
||||
stopTitle: {
|
||||
id: 'app.recording.stopTitle',
|
||||
description: 'stop recording title',
|
||||
},
|
||||
resumeTitle: {
|
||||
id: 'app.recording.resumeTitle',
|
||||
description: 'resume recording title',
|
||||
},
|
||||
startDescription: {
|
||||
id: 'app.recording.startDescription',
|
||||
description: 'start recording description',
|
||||
},
|
||||
stopDescription: {
|
||||
id: 'app.recording.stopDescription',
|
||||
description: 'stop recording description',
|
||||
},
|
||||
});
|
||||
|
||||
const propTypes = {
|
||||
intl: PropTypes.object.isRequired,
|
||||
toggleRecording: PropTypes.func.isRequired,
|
||||
recordingTime: PropTypes.number,
|
||||
recordingStatus: PropTypes.bool,
|
||||
amIModerator: PropTypes.bool,
|
||||
isMeteorConnected: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
recordingTime: -1,
|
||||
recordingStatus: false,
|
||||
amIModerator: false,
|
||||
};
|
||||
|
||||
class RecordingComponent extends PureComponent {
|
||||
render() {
|
||||
const {
|
||||
intl,
|
||||
recordingStatus,
|
||||
recordingTime,
|
||||
amIModerator,
|
||||
toggleRecording,
|
||||
isMeteorConnected,
|
||||
isOpen,
|
||||
onRequestClose,
|
||||
priority,
|
||||
setIsOpen,
|
||||
} = this.props;
|
||||
|
||||
let title;
|
||||
|
||||
if (!recordingStatus) {
|
||||
title = recordingTime > 0 ? intl.formatMessage(intlMessages.resumeTitle)
|
||||
: intl.formatMessage(intlMessages.startTitle);
|
||||
} else {
|
||||
title = intl.formatMessage(intlMessages.stopTitle);
|
||||
}
|
||||
|
||||
if (!amIModerator) return null;
|
||||
|
||||
const description = intl.formatMessage(!recordingStatus
|
||||
? intlMessages.startDescription
|
||||
: intlMessages.stopDescription);
|
||||
|
||||
return (
|
||||
<ConfirmationModal
|
||||
intl={intl}
|
||||
onConfirm={toggleRecording}
|
||||
title={title}
|
||||
description={description}
|
||||
disableConfirmButton={!isMeteorConnected}
|
||||
{...{
|
||||
isOpen,
|
||||
onRequestClose,
|
||||
priority,
|
||||
setIsOpen,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
RecordingComponent.propTypes = propTypes;
|
||||
RecordingComponent.defaultProps = defaultProps;
|
||||
|
||||
export default injectIntl(RecordingComponent);
|
122
bigbluebutton-html5/imports/ui/components/recording/component.tsx
Executable file
122
bigbluebutton-html5/imports/ui/components/recording/component.tsx
Executable file
@ -0,0 +1,122 @@
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import ConfirmationModal from '/imports/ui/components/common/modal/confirmation/component';
|
||||
|
||||
const intlMessages = defineMessages({
|
||||
startTitle: {
|
||||
id: 'app.recording.startTitle',
|
||||
description: 'start recording title',
|
||||
},
|
||||
stopTitle: {
|
||||
id: 'app.recording.stopTitle',
|
||||
description: 'stop recording title',
|
||||
},
|
||||
resumeTitle: {
|
||||
id: 'app.recording.resumeTitle',
|
||||
description: 'resume recording title',
|
||||
},
|
||||
startDescription: {
|
||||
id: 'app.recording.startDescription',
|
||||
description: 'start recording description',
|
||||
},
|
||||
stopDescription: {
|
||||
id: 'app.recording.stopDescription',
|
||||
description: 'stop recording description',
|
||||
},
|
||||
loadingTitle: {
|
||||
id: 'app.recording.loadingTitle',
|
||||
description: 'recording data is loading',
|
||||
},
|
||||
loadingDescription: {
|
||||
id: 'app.recording.loadingDescription',
|
||||
description: 'recording data is loading',
|
||||
},
|
||||
errorTitle: {
|
||||
id: 'app.recording.errorTitle',
|
||||
description: 'recording data error',
|
||||
},
|
||||
errorDescription: {
|
||||
id: 'app.recording.errorDescription',
|
||||
description: 'recording data error',
|
||||
},
|
||||
cancelLabel: {
|
||||
id: 'app.recording.cancelLabel',
|
||||
description: 'cancel button label',
|
||||
},
|
||||
});
|
||||
|
||||
interface RecordingComponentProps {
|
||||
amIModerator: boolean;
|
||||
connected: boolean;
|
||||
isOpen: boolean;
|
||||
recordingError: Error | null,
|
||||
recordingLoading: boolean;
|
||||
recordingStatus: boolean,
|
||||
priority: string;
|
||||
recordingTime: number,
|
||||
onRequestClose: () => void;
|
||||
toggleRecording: () => void,
|
||||
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
}
|
||||
|
||||
const RecordingComponent: React.FC<RecordingComponentProps> = (props) => {
|
||||
const {
|
||||
amIModerator,
|
||||
connected,
|
||||
isOpen,
|
||||
recordingError,
|
||||
recordingLoading,
|
||||
recordingStatus,
|
||||
recordingTime,
|
||||
priority,
|
||||
onRequestClose,
|
||||
toggleRecording,
|
||||
setIsOpen,
|
||||
} = props;
|
||||
|
||||
const intl = useIntl();
|
||||
|
||||
let title;
|
||||
let description;
|
||||
let cancelButtonLabel;
|
||||
|
||||
if (!amIModerator) return null;
|
||||
|
||||
if (recordingError) {
|
||||
title = intl.formatMessage(intlMessages.errorTitle);
|
||||
description = intl.formatMessage(intlMessages.errorDescription);
|
||||
cancelButtonLabel = intl.formatMessage(intlMessages.cancelLabel);
|
||||
} else if (recordingLoading) {
|
||||
title = intl.formatMessage(intlMessages.loadingTitle);
|
||||
description = intl.formatMessage(intlMessages.loadingDescription);
|
||||
cancelButtonLabel = intl.formatMessage(intlMessages.cancelLabel);
|
||||
} else if (recordingStatus) {
|
||||
description = intl.formatMessage(intlMessages.stopDescription);
|
||||
title = intl.formatMessage(intlMessages.stopTitle);
|
||||
} else {
|
||||
description = intl.formatMessage(intlMessages.startDescription);
|
||||
title = recordingTime > 0
|
||||
? intl.formatMessage(intlMessages.resumeTitle)
|
||||
: intl.formatMessage(intlMessages.startTitle);
|
||||
}
|
||||
|
||||
return (
|
||||
<ConfirmationModal
|
||||
intl={intl}
|
||||
onConfirm={toggleRecording}
|
||||
title={title}
|
||||
description={description}
|
||||
disableConfirmButton={!connected}
|
||||
hideConfirmButton={recordingLoading || recordingError}
|
||||
cancelButtonLabel={cancelButtonLabel}
|
||||
{...{
|
||||
isOpen,
|
||||
onRequestClose,
|
||||
priority,
|
||||
setIsOpen,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default RecordingComponent;
|
@ -1,40 +0,0 @@
|
||||
import React from 'react';
|
||||
import { useMutation } from '@apollo/client';
|
||||
import RecordingComponent from './component';
|
||||
import { SET_RECORDING_STATUS } from './mutations';
|
||||
import { GET_RECORDINGS } from './queries';
|
||||
import useDeduplicatedSubscription from '../../core/hooks/useDeduplicatedSubscription';
|
||||
|
||||
const RecordingContainer = (props) => {
|
||||
const { setIsOpen } = props;
|
||||
const [setRecordingStatus] = useMutation(SET_RECORDING_STATUS);
|
||||
const {
|
||||
data: recordingData,
|
||||
} = useDeduplicatedSubscription(GET_RECORDINGS);
|
||||
|
||||
const recording = recordingData?.meeting_recording[0]?.isRecording ?? false;
|
||||
const time = recordingData?.meeting_recording[0]?.previousRecordedTimeInSeconds ?? 0;
|
||||
|
||||
const toggleRecording = () => {
|
||||
setRecordingStatus({
|
||||
variables: {
|
||||
recording: !recording,
|
||||
},
|
||||
});
|
||||
setIsOpen(false);
|
||||
};
|
||||
return (
|
||||
<RecordingComponent
|
||||
{...{
|
||||
recordingStatus: recording,
|
||||
recordingTime: time,
|
||||
toggleRecording,
|
||||
...props,
|
||||
}}
|
||||
// TODO: Remove this
|
||||
isMeteorConnected
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default RecordingContainer;
|
60
bigbluebutton-html5/imports/ui/components/recording/container.tsx
Executable file
60
bigbluebutton-html5/imports/ui/components/recording/container.tsx
Executable file
@ -0,0 +1,60 @@
|
||||
import React from 'react';
|
||||
import { useMutation, useReactiveVar } from '@apollo/client';
|
||||
import RecordingComponent from './component';
|
||||
import { SET_RECORDING_STATUS } from './mutations';
|
||||
import { GET_RECORDINGS, GetRecordingResponse } from './queries';
|
||||
import useDeduplicatedSubscription from '/imports/ui/core/hooks/useDeduplicatedSubscription';
|
||||
import ConnectionStatus from '/imports/ui/core/graphql/singletons/connectionStatus';
|
||||
|
||||
interface RecordingContainerProps {
|
||||
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
amIModerator: boolean;
|
||||
onRequestClose: () => void;
|
||||
priority: string;
|
||||
isOpen: boolean;
|
||||
}
|
||||
|
||||
const RecordingContainer: React.FC<RecordingContainerProps> = (props) => {
|
||||
const {
|
||||
amIModerator, isOpen, onRequestClose, priority, setIsOpen,
|
||||
} = props;
|
||||
const [setRecordingStatus] = useMutation(SET_RECORDING_STATUS);
|
||||
const connected = useReactiveVar(ConnectionStatus.getConnectedStatusVar());
|
||||
const {
|
||||
data: recordingData,
|
||||
loading: recordingLoading,
|
||||
error: recordingError,
|
||||
} = useDeduplicatedSubscription<GetRecordingResponse>(GET_RECORDINGS);
|
||||
|
||||
const recording = recordingData?.meeting_recording[0]?.isRecording ?? false;
|
||||
const time = recordingData?.meeting_recording[0]?.previousRecordedTimeInSeconds ?? 0;
|
||||
|
||||
const toggleRecording = () => {
|
||||
setRecordingStatus({
|
||||
variables: {
|
||||
recording: !recording,
|
||||
},
|
||||
});
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<RecordingComponent
|
||||
{...{
|
||||
amIModerator,
|
||||
connected,
|
||||
isOpen,
|
||||
recordingError,
|
||||
recordingLoading,
|
||||
onRequestClose,
|
||||
priority,
|
||||
recordingTime: time,
|
||||
recordingStatus: recording,
|
||||
setIsOpen,
|
||||
toggleRecording,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default RecordingContainer;
|
@ -1,5 +1,12 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export interface GetRecordingResponse {
|
||||
meeting_recording: {
|
||||
isRecording: boolean;
|
||||
previousRecordedTimeInSeconds: number;
|
||||
}[]
|
||||
}
|
||||
|
||||
export const GET_RECORDINGS = gql`
|
||||
subscription getRecordingData {
|
||||
meeting_recording {
|
||||
|
@ -1045,6 +1045,11 @@
|
||||
"app.recording.resumeTitle": "Resume recording",
|
||||
"app.recording.startDescription": "You can select the record button again later to pause the recording.",
|
||||
"app.recording.stopDescription": "Are you sure you want to pause the recording? You can resume by selecting the record button again.",
|
||||
"app.recording.cancelLabel": "Cancel",
|
||||
"app.recording.loadingTitle": "Loading...",
|
||||
"app.recording.loadingDescription": "Please wait. We are checking the latest recording status.",
|
||||
"app.recording.errorTitle": "Something went wrong...",
|
||||
"app.recording.errorDescription": "Something went wrong while checking recording status.",
|
||||
"app.recording.notify.title": "Recording has started",
|
||||
"app.recording.notify.description": "A recording will be available based on the remainder of this session",
|
||||
"app.recording.notify.continue": "Continue",
|
||||
|
Loading…
Reference in New Issue
Block a user