mirror of https://github.com/jitsi/jitsi-meet
- opens as a modal - lastn is 0, mutes local video while open - long press to talk - and morepull/11506/head jitsi-meet_7290
parent
e628d99544
commit
61abf0d882
Before Width: | Height: | Size: 844 B After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 124 B |
@ -1,5 +1,6 @@ |
||||
// @flow
|
||||
|
||||
export { default as Chat } from './Chat'; |
||||
export { default as ChatAndPolls } from './ChatAndPolls'; |
||||
export { default as ChatButton } from './ChatButton'; |
||||
export { default as ChatPrivacyDialog } from './ChatPrivacyDialog'; |
||||
|
@ -1 +1,30 @@ |
||||
export * from './functions.any'; |
||||
|
||||
/** |
||||
* Returns whether the conference is in connecting state. |
||||
* |
||||
* @param {Object} state - The redux state. |
||||
* @returns {boolean} Whether conference is connecting. |
||||
*/ |
||||
export const isConnecting = (state: Object) => { |
||||
const { connecting, connection } = state['features/base/connection']; |
||||
const { |
||||
conference, |
||||
joining, |
||||
membersOnly, |
||||
leaving |
||||
} = state['features/base/conference']; |
||||
|
||||
// XXX There is a window of time between the successful establishment of the
|
||||
// XMPP connection and the subsequent commencement of joining the MUC during
|
||||
// which the app does not appear to be doing anything according to the redux
|
||||
// state. In order to not toggle the _connecting props during the window of
|
||||
// time in question, define _connecting as follows:
|
||||
// - the XMPP connection is connecting, or
|
||||
// - the XMPP connection is connected and the conference is joining, or
|
||||
// - the XMPP connection is connected and we have no conference yet, nor we
|
||||
// are leaving one.
|
||||
return Boolean( |
||||
connecting || (connection && (!membersOnly && (joining || (!conference && !leaving)))) |
||||
); |
||||
}; |
||||
|
@ -0,0 +1,17 @@ |
||||
import React from 'react'; |
||||
|
||||
import { Icon, IconVolumeEmpty } from '../../../../base/icons'; |
||||
import BaseTheme from '../../../../base/ui/components/BaseTheme.native'; |
||||
|
||||
/** |
||||
* React component for Audio icon. |
||||
*
|
||||
* @returns {JSX.Element} - the Audio icon. |
||||
*
|
||||
*/ |
||||
const AudioIcon = () : JSX.Element => (<Icon |
||||
color = { BaseTheme.palette.text06 } |
||||
size = { 20 } |
||||
src = { IconVolumeEmpty } />); |
||||
|
||||
export default AudioIcon; |
@ -0,0 +1,82 @@ |
||||
import React, { useEffect } from 'react'; |
||||
import { useTranslation } from 'react-i18next'; |
||||
import { Text, SafeAreaView, View } from 'react-native'; |
||||
import { withSafeAreaInsets } from 'react-native-safe-area-context'; |
||||
import { useDispatch, useSelector } from 'react-redux'; |
||||
import JitsiScreen from '../../../../base/modal/components/JitsiScreen'; |
||||
|
||||
import { LoadingIndicator, TintedView } from '../../../../base/react'; |
||||
import { setIsCarmode } from '../../../../video-layout/actions'; |
||||
import ConferenceTimer from '../../ConferenceTimer'; |
||||
import { isConnecting } from '../../functions'; |
||||
|
||||
import EndMeetingButton from './EndMeetingButton'; |
||||
import MicrophoneButton from './MicrophoneButton'; |
||||
import SoundDeviceButton from './SoundDeviceButton'; |
||||
import TitleBar from './TitleBar'; |
||||
import styles from './styles'; |
||||
import { isLocalVideoTrackDesktop } from '../../../../base/tracks'; |
||||
import { setPictureInPictureDisabled } from '../../../../mobile/picture-in-picture/functions'; |
||||
|
||||
/** |
||||
* Implements the carmode tab. |
||||
* |
||||
* @returns { JSX.Element} - The carmode tab. |
||||
*/ |
||||
const CarmodeTab = (): JSX.Element => { |
||||
const dispatch = useDispatch(); |
||||
const { t } = useTranslation(); |
||||
const connecting = useSelector(isConnecting); |
||||
const isSharing = useSelector(isLocalVideoTrackDesktop); |
||||
|
||||
useEffect(() => { |
||||
dispatch(setIsCarmode(true)); |
||||
setPictureInPictureDisabled(true); |
||||
|
||||
return () => { |
||||
dispatch(setIsCarmode(false)); |
||||
if (!isSharing) { |
||||
setPictureInPictureDisabled(false); |
||||
} |
||||
} |
||||
}, []); |
||||
|
||||
return ( |
||||
<JitsiScreen style = { styles.conference }> |
||||
{/* |
||||
* The activity/loading indicator goes above everything, except |
||||
* the toolbox/toolbars and the dialogs. |
||||
*/ |
||||
connecting |
||||
&& <TintedView> |
||||
<LoadingIndicator /> |
||||
</TintedView> |
||||
} |
||||
<View |
||||
pointerEvents = 'box-none' |
||||
style = { styles.titleBarSafeViewColor }> |
||||
<View |
||||
style = { styles.titleBar }> |
||||
<TitleBar /> |
||||
</View> |
||||
<ConferenceTimer textStyle = { styles.roomTimer } /> |
||||
</View> |
||||
<View |
||||
pointerEvents = 'box-none' |
||||
style = { styles.microphoneContainer }> |
||||
<MicrophoneButton /> |
||||
</View> |
||||
<View |
||||
pointerEvents = 'box-none' |
||||
style = { styles.bottomContainer }> |
||||
<Text style = { styles.videoStoppedLabel }> |
||||
{t('carmode.labels.videoStopped')} |
||||
</Text> |
||||
<SoundDeviceButton /> |
||||
<EndMeetingButton /> |
||||
</View> |
||||
</JitsiScreen> |
||||
); |
||||
}; |
||||
|
||||
export default withSafeAreaInsets(CarmodeTab); |
@ -0,0 +1,39 @@ |
||||
import React, { useCallback } from 'react'; |
||||
import { useTranslation } from 'react-i18next'; |
||||
import { Button } from 'react-native-paper'; |
||||
import { useDispatch } from 'react-redux'; |
||||
|
||||
import { createToolbarEvent, sendAnalytics } from '../../../../analytics'; |
||||
import { appNavigate } from '../../../../app/actions'; |
||||
|
||||
import EndMeetingIcon from './EndMeetingIcon'; |
||||
import styles from './styles'; |
||||
|
||||
/** |
||||
* Button for ending meeting from carmode. |
||||
* |
||||
* @returns {JSX.Element} - The end meeting button. |
||||
*/ |
||||
const EndMeetingButton = () : JSX.Element => { |
||||
const { t } = useTranslation(); |
||||
const dispatch = useDispatch(); |
||||
|
||||
const onSelect = useCallback(() => { |
||||
sendAnalytics(createToolbarEvent('hangup')); |
||||
|
||||
dispatch(appNavigate(undefined)); |
||||
}, [ dispatch ]); |
||||
|
||||
return ( |
||||
<Button |
||||
accessibilityLabel = { t('carmode.actions.leaveMeeting') } |
||||
children = { t('carmode.actions.leaveMeeting') } |
||||
icon = { EndMeetingIcon } |
||||
labelStyle = { styles.endMeetingButtonLabel } |
||||
mode = 'contained' |
||||
onPress = { onSelect } |
||||
style = { styles.endMeetingButton } /> |
||||
); |
||||
}; |
||||
|
||||
export default EndMeetingButton; |
@ -0,0 +1,16 @@ |
||||
import React from 'react'; |
||||
|
||||
import { Icon, IconHangup } from '../../../../base/icons'; |
||||
import BaseTheme from '../../../../base/ui/components/BaseTheme.native'; |
||||
|
||||
/** |
||||
* Implements an end meeting icon. |
||||
*
|
||||
* @returns {JSX.Element} - the end meeting icon. |
||||
*/ |
||||
const EndMeetingIcon = () : JSX.Element => (<Icon |
||||
color = { BaseTheme.palette.icon01 } |
||||
size = { 20 } |
||||
src = { IconHangup } />); |
||||
|
||||
export default EndMeetingIcon; |
@ -0,0 +1,88 @@ |
||||
import React, { useCallback } from 'react'; |
||||
import { useState } from 'react'; |
||||
import { View, TouchableOpacity } from 'react-native'; |
||||
import { useDispatch, useSelector } from 'react-redux'; |
||||
import { |
||||
createShortcutEvent, |
||||
sendAnalytics, |
||||
ACTION_SHORTCUT_PRESSED as PRESSED, |
||||
ACTION_SHORTCUT_RELEASED as RELEASED |
||||
} from '../../../../analytics'; |
||||
|
||||
import { getFeatureFlag, AUDIO_MUTE_BUTTON_ENABLED } from '../../../../base/flags'; |
||||
import { Icon, IconMicrophone, IconMicrophoneEmptySlash } from '../../../../base/icons'; |
||||
import { MEDIA_TYPE } from '../../../../base/media'; |
||||
import { isLocalTrackMuted } from '../../../../base/tracks'; |
||||
import { isAudioMuteButtonDisabled } from '../../../../toolbox/functions.any'; |
||||
import { muteLocal } from '../../../../video-menu/actions'; |
||||
|
||||
import styles from './styles'; |
||||
|
||||
const LONG_PRESS = 'long.press'; |
||||
|
||||
/** |
||||
* Implements a round audio mute/unmute button of a custom size. |
||||
* |
||||
* @returns {JSX.Element} - The audio mute round button. |
||||
*/ |
||||
const MicrophoneButton = () : JSX.Element => { |
||||
const dispatch = useDispatch(); |
||||
const audioMuted = useSelector(state => isLocalTrackMuted(state['features/base/tracks'], MEDIA_TYPE.AUDIO)); |
||||
const disabled = useSelector(isAudioMuteButtonDisabled); |
||||
const enabledFlag = useSelector(state => getFeatureFlag(state, AUDIO_MUTE_BUTTON_ENABLED, true)); |
||||
const [ longPress, setLongPress ] = useState(false); |
||||
|
||||
if (!enabledFlag) { |
||||
return null; |
||||
} |
||||
|
||||
const onPressIn = useCallback(() => { |
||||
!disabled && dispatch(muteLocal(!audioMuted, MEDIA_TYPE.AUDIO)); |
||||
}, [ audioMuted, disabled ]); |
||||
|
||||
const onLongPress = useCallback(() => { |
||||
if ( !disabled && !audioMuted) { |
||||
sendAnalytics(createShortcutEvent( |
||||
'push.to.talk', |
||||
PRESSED, |
||||
{}, |
||||
LONG_PRESS)); |
||||
setLongPress(true); |
||||
} |
||||
}, [audioMuted, disabled, setLongPress]); |
||||
|
||||
const onPressOut = useCallback(() => { |
||||
if (longPress) { |
||||
setLongPress(false); |
||||
sendAnalytics(createShortcutEvent( |
||||
'push.to.talk', |
||||
RELEASED, |
||||
{}, |
||||
LONG_PRESS |
||||
)); |
||||
dispatch(muteLocal(true, MEDIA_TYPE.AUDIO)); |
||||
} |
||||
}, [longPress, setLongPress]); |
||||
|
||||
return ( |
||||
<TouchableOpacity |
||||
onPressIn = { onPressIn } |
||||
onLongPress={ onLongPress } |
||||
onPressOut={ onPressOut } > |
||||
<View |
||||
style = { [ |
||||
styles.microphoneStyles.container, |
||||
!audioMuted && styles.microphoneStyles.unmuted |
||||
] }> |
||||
<View |
||||
style = { styles.microphoneStyles.iconContainer }> |
||||
<Icon |
||||
src = { audioMuted ? IconMicrophoneEmptySlash : IconMicrophone } |
||||
style = { styles.microphoneStyles.icon } /> |
||||
</View> |
||||
</View> |
||||
</TouchableOpacity> |
||||
); |
||||
}; |
||||
|
||||
export default MicrophoneButton; |
@ -0,0 +1,37 @@ |
||||
import React, { useCallback } from 'react'; |
||||
import { useTranslation } from 'react-i18next'; |
||||
import { Button } from 'react-native-paper'; |
||||
import { useDispatch } from 'react-redux'; |
||||
|
||||
import { openDialog } from '../../../../base/dialog/actions'; |
||||
import AudioRoutePickerDialog from '../../../../mobile/audio-mode/components/AudioRoutePickerDialog'; |
||||
|
||||
import AudioIcon from './AudioIcon'; |
||||
import styles from './styles'; |
||||
|
||||
/** |
||||
* Button for selecting sound device in carmode. |
||||
* |
||||
* @returns {JSX.Element} - The sound device button. |
||||
*/ |
||||
const SelectSoundDevice = () : JSX.Element => { |
||||
const { t } = useTranslation(); |
||||
const dispatch = useDispatch(); |
||||
|
||||
const onSelect = useCallback(() => |
||||
dispatch(openDialog(AudioRoutePickerDialog)) |
||||
, [ dispatch ]); |
||||
|
||||
return ( |
||||
<Button |
||||
accessibilityLabel = { t('carmode.actions.selectSoundDevice') } |
||||
children = { t('carmode.actions.selectSoundDevice') } |
||||
icon = { AudioIcon } |
||||
labelStyle = { styles.soundDeviceButtonLabel } |
||||
mode = 'contained' |
||||
onPress = { onSelect } |
||||
style = { styles.soundDeviceButton } /> |
||||
); |
||||
}; |
||||
|
||||
export default SelectSoundDevice; |
@ -0,0 +1,81 @@ |
||||
import React from 'react'; |
||||
import { Text, View } from 'react-native'; |
||||
|
||||
import { getConferenceName } from '../../../../base/conference/functions'; |
||||
import { getFeatureFlag, MEETING_NAME_ENABLED } from '../../../../base/flags'; |
||||
import { JitsiRecordingConstants } from '../../../../base/lib-jitsi-meet'; |
||||
import { connect } from '../../../../base/redux';; |
||||
import { RecordingLabel } from '../../../../recording'; |
||||
import { VideoQualityLabel } from '../../../../video-quality'; |
||||
|
||||
import styles from './styles'; |
||||
|
||||
|
||||
type Props = { |
||||
|
||||
/** |
||||
* Name of the meeting we're currently in. |
||||
*/ |
||||
_meetingName: string, |
||||
|
||||
/** |
||||
* Whether displaying the current meeting name is enabled or not. |
||||
*/ |
||||
_meetingNameEnabled: boolean, |
||||
|
||||
}; |
||||
|
||||
/** |
||||
* Implements a navigation bar component that is rendered on top of the |
||||
* carmode screen. |
||||
* |
||||
* @param {Props} props - The React props passed to this component. |
||||
* @returns {JSX.Element} |
||||
*/ |
||||
const TitleBar = (props: Props) : JSX.Element => (<> |
||||
<View |
||||
pointerEvents = 'box-none' |
||||
style = { styles.titleBarWrapper }> |
||||
<View |
||||
pointerEvents = 'box-none' |
||||
style = { styles.roomNameWrapper }> |
||||
<View style = { styles.qualityLabelContainer }> |
||||
<VideoQualityLabel /> |
||||
</View> |
||||
|
||||
<View style = { styles.headerLabels }> |
||||
<RecordingLabel mode = { JitsiRecordingConstants.mode.FILE } /> |
||||
<RecordingLabel mode = { JitsiRecordingConstants.mode.STREAM } /> |
||||
</View> |
||||
|
||||
{ |
||||
props._meetingNameEnabled |
||||
&& <View style = { styles.roomNameView }> |
||||
<Text |
||||
numberOfLines = { 1 } |
||||
style = { styles.roomName }> |
||||
{props._meetingName} |
||||
</Text> |
||||
</View> |
||||
} |
||||
</View> |
||||
</View> |
||||
</>); |
||||
|
||||
/** |
||||
* Maps part of the Redux store to the props of this component. |
||||
* |
||||
* @param {Object} state - The Redux state. |
||||
* @returns {Props} |
||||
*/ |
||||
function _mapStateToProps(state: Object) { |
||||
const { hideConferenceSubject } = state['features/base/config']; |
||||
|
||||
return { |
||||
_meetingName: getConferenceName(state), |
||||
_meetingNameEnabled: |
||||
getFeatureFlag(state, MEETING_NAME_ENABLED, true) && !hideConferenceSubject |
||||
}; |
||||
} |
||||
|
||||
export default connect(_mapStateToProps)(TitleBar); |
@ -0,0 +1,207 @@ |
||||
import BaseTheme from '../../../../base/ui/components/BaseTheme.native'; |
||||
|
||||
/** |
||||
* The size of the microphone icon. |
||||
*/ |
||||
const MICROPHONE_SIZE = 180; |
||||
|
||||
/** |
||||
* Base button style. |
||||
*/ |
||||
const baseButton = { |
||||
borderRadius: BaseTheme.shape.borderRadius, |
||||
height: BaseTheme.spacing[7], |
||||
marginTop: BaseTheme.spacing[3], |
||||
marginLeft: BaseTheme.spacing[10], |
||||
marginRight: BaseTheme.spacing[10], |
||||
display: 'flex', |
||||
justifyContent: 'space-around', |
||||
width: 300 |
||||
}; |
||||
|
||||
/** |
||||
* Base label style. |
||||
*/ |
||||
const baseLabel = { |
||||
display: 'flex', |
||||
fontSize: 16, |
||||
textTransform: 'capitalize' |
||||
}; |
||||
|
||||
/** |
||||
* The styles of the safe area view that contains the title bar. |
||||
*/ |
||||
const titleBarSafeView = { |
||||
left: 0, |
||||
position: 'absolute', |
||||
right: 0, |
||||
top: 0 |
||||
}; |
||||
|
||||
/** |
||||
* The styles of the native components of Carmode. |
||||
*/ |
||||
export default { |
||||
|
||||
bottomContainer: { |
||||
display: 'flex', |
||||
flexDirection: 'column', |
||||
alignItems: 'center', |
||||
bottom: 0, |
||||
left: 0, |
||||
right: 0, |
||||
position: 'absolute' |
||||
}, |
||||
|
||||
/** |
||||
* {@code Conference} Style. |
||||
*/ |
||||
conference: { |
||||
backgroundColor: BaseTheme.palette.uiBackground, |
||||
flex: 1, |
||||
justifyContent: 'center' |
||||
}, |
||||
|
||||
microphoneStyles: { |
||||
container: { |
||||
borderRadius: MICROPHONE_SIZE / 2, |
||||
height: MICROPHONE_SIZE, |
||||
maxHeight: MICROPHONE_SIZE, |
||||
justifyContent: 'center', |
||||
overflow: 'hidden', |
||||
width: MICROPHONE_SIZE, |
||||
maxWidth: MICROPHONE_SIZE, |
||||
flex: 1, |
||||
zIndex: 1, |
||||
elevation: 1 |
||||
}, |
||||
|
||||
icon: { |
||||
color: BaseTheme.palette.text01, |
||||
fontSize: MICROPHONE_SIZE * 0.45, |
||||
fontWeight: '100' |
||||
}, |
||||
|
||||
iconContainer: { |
||||
alignItems: 'center', |
||||
alignSelf: 'stretch', |
||||
flex: 1, |
||||
justifyContent: 'center', |
||||
backgroundColor: BaseTheme.palette.ui03 |
||||
}, |
||||
|
||||
unmuted: { |
||||
borderWidth: 4, |
||||
borderColor: BaseTheme.palette.success01 |
||||
} |
||||
}, |
||||
|
||||
qualityLabelContainer: { |
||||
borderBottomLeftRadius: 3, |
||||
borderTopLeftRadius: 3, |
||||
flexShrink: 1, |
||||
paddingHorizontal: 2, |
||||
justifyContent: 'center', |
||||
marginTop: 8 |
||||
}, |
||||
|
||||
roomTimer: { |
||||
color: BaseTheme.palette.text01, |
||||
...BaseTheme.typography.bodyShortBold, |
||||
paddingHorizontal: 8, |
||||
paddingVertical: 6, |
||||
textAlign: 'center' |
||||
}, |
||||
|
||||
titleView: { |
||||
width: 152, |
||||
height: 28, |
||||
backgroundColor: BaseTheme.palette.ui02, |
||||
borderRadius: 12, |
||||
alignSelf: 'center' |
||||
}, |
||||
|
||||
title: { |
||||
margin: 'auto', |
||||
textAlign: 'center', |
||||
paddingVertical: 4, |
||||
paddingHorizontal: 16, |
||||
color: BaseTheme.palette.text02 |
||||
}, |
||||
|
||||
soundDeviceButtonLabel: { |
||||
...baseLabel, |
||||
color: BaseTheme.palette.text06 |
||||
}, |
||||
|
||||
soundDeviceButton: { |
||||
...baseButton, |
||||
backgroundColor: BaseTheme.palette.section01 |
||||
}, |
||||
|
||||
endMeetingButton: { |
||||
...baseButton, |
||||
backgroundColor: BaseTheme.palette.actionDanger, |
||||
marginBottom: 60 |
||||
}, |
||||
|
||||
endMeetingButtonLabel: { |
||||
...baseLabel, |
||||
color: BaseTheme.palette.text01 |
||||
}, |
||||
|
||||
headerLabels: { |
||||
borderBottomLeftRadius: 3, |
||||
borderTopLeftRadius: 3, |
||||
flexShrink: 1, |
||||
paddingHorizontal: 2, |
||||
justifyContent: 'center' |
||||
}, |
||||
|
||||
titleBarSafeViewColor: { |
||||
...titleBarSafeView, |
||||
backgroundColor: BaseTheme.palette.uiBackground |
||||
}, |
||||
|
||||
microphoneContainer: { |
||||
flex: 1, |
||||
alignItems: 'center', |
||||
justifyContent: 'center' |
||||
}, |
||||
|
||||
titleBarWrapper: { |
||||
alignItems: 'center', |
||||
flex: 1, |
||||
flexDirection: 'row', |
||||
justifyContent: 'center' |
||||
}, |
||||
|
||||
roomNameWrapper: { |
||||
flexDirection: 'row', |
||||
marginRight: 10, |
||||
flexShrink: 1, |
||||
flexGrow: 1 |
||||
}, |
||||
|
||||
roomNameView: { |
||||
backgroundColor: 'rgba(0,0,0,0.6)', |
||||
flexShrink: 1, |
||||
justifyContent: 'center', |
||||
paddingHorizontal: 5 |
||||
}, |
||||
|
||||
roomName: { |
||||
color: BaseTheme.palette.text01, |
||||
...BaseTheme.typography.bodyShortBold |
||||
}, |
||||
|
||||
titleBar: { |
||||
alignSelf: 'center' |
||||
}, |
||||
|
||||
videoStoppedLabel: { |
||||
color: BaseTheme.palette.text01, |
||||
marginBottom: 32, |
||||
...BaseTheme.typography.bodyShortRegularLarge |
||||
} |
||||
}; |
@ -0,0 +1,46 @@ |
||||
import { CAR_MODE_ENABLED, getFeatureFlag } from '../../../base/flags'; |
||||
import { translate } from '../../../base/i18n'; |
||||
import { IconCar } from '../../../base/icons'; |
||||
import { connect } from '../../../base/redux'; |
||||
import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components'; |
||||
import { navigate } |
||||
from '../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef'; |
||||
import { screen } from '../../../mobile/navigation/routes'; |
||||
|
||||
/** |
||||
* Implements an {@link AbstractButton} to open the carmode. |
||||
*/ |
||||
class OpenCarmodeButton extends AbstractButton<AbstractButtonProps, any, any> { |
||||
accessibilityLabel = 'toolbar.accessibilityLabel.carmode'; |
||||
icon = IconCar; |
||||
label = 'carmode.labels.buttonLabel'; |
||||
|
||||
/** |
||||
* Handles clicking / pressing the button, and opens the carmode mode. |
||||
* |
||||
* @private |
||||
* @returns {void} |
||||
*/ |
||||
_handleClick() { |
||||
return navigate(screen.conference.carmode); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Maps part of the Redux state to the props of this component. |
||||
* |
||||
* @param {Object} state - The Redux state. |
||||
* @param {AbstractButtonProps} ownProps - The properties explicitly passed to the component instance. |
||||
* @private |
||||
* @returns {Object} |
||||
*/ |
||||
function _mapStateToProps(state: Object, ownProps: AbstractButtonProps): Object { |
||||
const enabled = getFeatureFlag(state, CAR_MODE_ENABLED, true); |
||||
const { visible = enabled } = ownProps; |
||||
|
||||
return { |
||||
visible |
||||
}; |
||||
} |
||||
|
||||
export default translate(connect(_mapStateToProps)(OpenCarmodeButton)); |
@ -0,0 +1,19 @@ |
||||
import { SET_CAR_MODE } from './actionTypes'; |
||||
|
||||
export * from './actions.any'; |
||||
|
||||
/** |
||||
* Creates a (redux) action which tells whether we are in carmode. |
||||
* |
||||
* @param {boolean} enabled - Whether we are in carmode. |
||||
* @returns {{ |
||||
* type: SET_CAR_MODE, |
||||
* enabled: boolean |
||||
* }} |
||||
*/ |
||||
export function setIsCarmode(enabled) { |
||||
return { |
||||
type: SET_CAR_MODE, |
||||
enabled |
||||
}; |
||||
} |
@ -0,0 +1 @@ |
||||
export * from './actions.any'; |
@ -1 +1,24 @@ |
||||
import { MEDIA_TYPE, setVideoMuted, VIDEO_MUTISM_AUTHORITY } from '../base/media'; |
||||
import { MiddlewareRegistry } from '../base/redux'; |
||||
|
||||
import { SET_CAR_MODE } from './actionTypes'; |
||||
import './middleware.any'; |
||||
|
||||
/** |
||||
* Middleware which intercepts actions and updates the legacy component. |
||||
* |
||||
* @param {Store} store - The redux store. |
||||
* @returns {Function} |
||||
*/ |
||||
MiddlewareRegistry.register(store => next => action => { |
||||
const result = next(action); |
||||
const { dispatch } = store; |
||||
|
||||
switch (action.type) { |
||||
case SET_CAR_MODE: |
||||
dispatch(setVideoMuted(action.enabled, MEDIA_TYPE.VIDEO, VIDEO_MUTISM_AUTHORITY.CAR_MODE)); |
||||
break; |
||||
} |
||||
|
||||
return result; |
||||
}); |
||||
|
Loading…
Reference in new issue