mirror of https://github.com/jitsi/jitsi-meet
parent
9a9890f86c
commit
04690dfc8f
Binary file not shown.
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
Binary file not shown.
Binary file not shown.
@ -0,0 +1,204 @@ |
||||
/* @flow */ |
||||
|
||||
import React, { Component } from 'react'; |
||||
import { |
||||
Animated, |
||||
Dimensions, |
||||
TouchableWithoutFeedback, |
||||
View |
||||
} from 'react-native'; |
||||
|
||||
import styles, { SIDEBAR_WIDTH } from './styles'; |
||||
|
||||
|
||||
/** |
||||
* The type of the React {@code Component} props of {@link SideBar} |
||||
*/ |
||||
type Props = { |
||||
|
||||
/** |
||||
* The local participant's avatar |
||||
*/ |
||||
_avatar: string, |
||||
|
||||
/** |
||||
* The children of the Component |
||||
*/ |
||||
children: React$Node, |
||||
|
||||
/** |
||||
* Callback to notify the containing Component that the sidebar is |
||||
* closing. |
||||
*/ |
||||
onHide: Function, |
||||
|
||||
/** |
||||
* Sets the menu displayed or hidden. |
||||
*/ |
||||
show: boolean |
||||
} |
||||
|
||||
type State = { |
||||
|
||||
/** |
||||
* Indicates whether the side bar is visible or not. |
||||
*/ |
||||
showSideBar: boolean, |
||||
|
||||
/** |
||||
* Indicates whether the side overlay should be rendered or not. |
||||
*/ |
||||
showOverlay: boolean, |
||||
|
||||
/** |
||||
* The native animation object. |
||||
*/ |
||||
sliderAnimation: Object |
||||
} |
||||
|
||||
/** |
||||
* A generic animated side bar to be used for left side menus |
||||
*/ |
||||
export default class SideBar extends Component<Props, State> { |
||||
_mounted: boolean; |
||||
|
||||
/** |
||||
* Component's contructor. |
||||
* |
||||
* @inheritdoc |
||||
*/ |
||||
constructor(props: Props) { |
||||
super(props); |
||||
|
||||
this.state = { |
||||
showSideBar: false, |
||||
showOverlay: false, |
||||
sliderAnimation: new Animated.Value(-SIDEBAR_WIDTH) |
||||
}; |
||||
|
||||
this._setShow = this._setShow.bind(this); |
||||
|
||||
this._getContainerStyle = this._getContainerStyle.bind(this); |
||||
this._onHideMenu = this._onHideMenu.bind(this); |
||||
this._setShow(props.show); |
||||
} |
||||
|
||||
/** |
||||
* Implements the Component's componentDidMount method. |
||||
* |
||||
* @inheritdoc |
||||
*/ |
||||
componentDidMount() { |
||||
this._mounted = true; |
||||
} |
||||
|
||||
/** |
||||
* Implements the Component's componentWillReceiveProps method. |
||||
* |
||||
* @inheritdoc |
||||
*/ |
||||
componentWillReceiveProps(newProps: Props) { |
||||
if (newProps.show !== this.props.show) { |
||||
this._setShow(newProps.show); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Implements React's {@link Component#render()}. |
||||
* |
||||
* @inheritdoc |
||||
*/ |
||||
render() { |
||||
return ( |
||||
<Animated.View |
||||
style = { this._getContainerStyle() } > |
||||
<View style = { styles.sideMenuContent }> |
||||
{ |
||||
this.props.children |
||||
} |
||||
</View> |
||||
<TouchableWithoutFeedback |
||||
onPress = { this._onHideMenu } |
||||
style = { styles.sideMenuShadowTouchable } > |
||||
<View style = { styles.sideMenuShadow } /> |
||||
</TouchableWithoutFeedback> |
||||
</Animated.View> |
||||
); |
||||
} |
||||
|
||||
_getContainerStyle: () => Array<Object> |
||||
|
||||
/** |
||||
* Assembles a style array for the container. |
||||
* |
||||
* @private |
||||
* @returns {Array<Object>} |
||||
*/ |
||||
_getContainerStyle() { |
||||
const { sliderAnimation } = this.state; |
||||
const { height, width } = Dimensions.get('window'); |
||||
|
||||
return [ |
||||
styles.sideMenuContainer, |
||||
{ |
||||
left: sliderAnimation, |
||||
width: this.state.showOverlay |
||||
? Math.max(height, width) + SIDEBAR_WIDTH : SIDEBAR_WIDTH |
||||
} |
||||
]; |
||||
} |
||||
|
||||
_onHideMenu: () => void; |
||||
|
||||
/** |
||||
* Hides the menu. |
||||
* |
||||
* @private |
||||
* @returns {void} |
||||
*/ |
||||
_onHideMenu() { |
||||
this._setShow(false); |
||||
|
||||
const { onHide } = this.props; |
||||
|
||||
if (typeof onHide === 'function') { |
||||
onHide(); |
||||
} |
||||
} |
||||
|
||||
_setShow: (boolean) => void; |
||||
|
||||
/** |
||||
* Sets the side menu visible or hidden. |
||||
* |
||||
* @private |
||||
* @param {boolean} show - The new expected visibility value. |
||||
* @returns {void} |
||||
*/ |
||||
_setShow(show) { |
||||
if (this.state.showSideBar !== show) { |
||||
if (show) { |
||||
this.setState({ |
||||
showOverlay: true |
||||
}); |
||||
} |
||||
|
||||
Animated.timing(this.state.sliderAnimation, { |
||||
toValue: show ? 0 : -SIDEBAR_WIDTH |
||||
}).start(animationState => { |
||||
if (animationState.finished && !show) { |
||||
this.setState({ |
||||
showOverlay: false |
||||
}); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
if (this._mounted) { |
||||
this.setState({ |
||||
showSideBar: show |
||||
}); |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,4 @@ |
||||
/** |
||||
* Action type to signal the need to hide or show the side bar. |
||||
*/ |
||||
export const SET_SIDEBAR_VISIBILITY = Symbol('SET_SIDEBAR_VISIBILITY'); |
@ -0,0 +1,19 @@ |
||||
// @flow
|
||||
|
||||
import { SET_SIDEBAR_VISIBILITY } from './actionTypes'; |
||||
|
||||
/** |
||||
* Redux action to hide or show the status bar. |
||||
* |
||||
* @param {boolean} visible - The new value of the visibility. |
||||
* @returns {{ |
||||
* type: SET_SIDEBAR_VISIBILITY, |
||||
* sideBarVisible: boolean |
||||
* }} |
||||
*/ |
||||
export function setSideBarVisibility(visible: boolean) { |
||||
return { |
||||
type: SET_SIDEBAR_VISIBILITY, |
||||
sideBarVisible: visible |
||||
}; |
||||
} |
@ -0,0 +1,100 @@ |
||||
// @flow
|
||||
|
||||
import React, { Component } from 'react'; |
||||
import { Linking, Text, TouchableOpacity, View } from 'react-native'; |
||||
|
||||
import styles from './styles'; |
||||
|
||||
import { Icon } from '../../base/font-icons'; |
||||
import { translate } from '../../base/i18n'; |
||||
|
||||
type Props = { |
||||
|
||||
/** |
||||
* The i18n label of the item. |
||||
*/ |
||||
i18Label: string, |
||||
|
||||
/** |
||||
* The icon of the item. |
||||
*/ |
||||
icon: string, |
||||
|
||||
/** |
||||
* The function to be invoked when the item is pressed |
||||
* if the item is a button. |
||||
*/ |
||||
onPress: Function, |
||||
|
||||
/** |
||||
* The translate function. |
||||
*/ |
||||
t: Function, |
||||
|
||||
/** |
||||
* The URL of the link, if this item is a link. |
||||
*/ |
||||
url: string |
||||
}; |
||||
|
||||
/** |
||||
* A component rendering an item in the system sidebar. |
||||
*/ |
||||
class SideBarItem extends Component<Props> { |
||||
|
||||
/** |
||||
* Contructor of the SideBarItem Component. |
||||
* |
||||
* @inheritdoc |
||||
*/ |
||||
constructor(props: Props) { |
||||
super(props); |
||||
|
||||
this._onOpenURL = this._onOpenURL.bind(this); |
||||
} |
||||
|
||||
/** |
||||
* Implements React's {@link Component#render()}, renders the sidebar item. |
||||
* |
||||
* @inheritdoc |
||||
* @returns {ReactElement} |
||||
*/ |
||||
render() { |
||||
const { onPress, t } = this.props; |
||||
const onPressCalculated |
||||
= typeof onPress === 'function' ? onPress : this._onOpenURL; |
||||
|
||||
return ( |
||||
<TouchableOpacity |
||||
onPress = { onPressCalculated } |
||||
style = { styles.sideBarItem }> |
||||
<View style = { styles.sideBarItemButtonContainer }> |
||||
<Icon |
||||
name = { this.props.icon } |
||||
style = { styles.sideBarItemIcon } /> |
||||
<Text style = { styles.sideBarItemText }> |
||||
{ t(this.props.i18Label) } |
||||
</Text> |
||||
</View> |
||||
</TouchableOpacity> |
||||
); |
||||
} |
||||
|
||||
_onOpenURL: () => void; |
||||
|
||||
/** |
||||
* Opens the URL if one is provided. |
||||
* |
||||
* @private |
||||
* @returns {void} |
||||
*/ |
||||
_onOpenURL() { |
||||
const { url } = this.props; |
||||
|
||||
if (typeof url === 'string') { |
||||
Linking.openURL(url); |
||||
} |
||||
} |
||||
} |
||||
|
||||
export default translate(SideBarItem); |
@ -0,0 +1,167 @@ |
||||
// @flow
|
||||
|
||||
import React, { Component } from 'react'; |
||||
import { SafeAreaView, ScrollView, Text } from 'react-native'; |
||||
import { connect } from 'react-redux'; |
||||
|
||||
import SideBarItem from './SideBarItem'; |
||||
import styles from './styles'; |
||||
|
||||
import { setSideBarVisibility } from '../actions'; |
||||
|
||||
import { showAppSettings } from '../../app-settings'; |
||||
import { |
||||
Avatar, |
||||
getAvatarURL, |
||||
getLocalParticipant, |
||||
getParticipantDisplayName |
||||
} from '../../base/participants'; |
||||
import { |
||||
Header, |
||||
SideBar |
||||
} from '../../base/react'; |
||||
|
||||
/** |
||||
* The URL at which the privacy policy is available to the user. |
||||
*/ |
||||
const PRIVACY_URL = 'https://jitsi.org/meet/privacy'; |
||||
|
||||
/** |
||||
* The URL at which the user may send feedback. |
||||
*/ |
||||
const SEND_FEEDBACK_URL = 'mailto:support@jitsi.org'; |
||||
|
||||
/** |
||||
* The URL at which the terms (of service/use) are available to the user. |
||||
*/ |
||||
const TERMS_URL = 'https://jitsi.org/meet/terms'; |
||||
|
||||
type Props = { |
||||
|
||||
/** |
||||
* Redux dispatch action |
||||
*/ |
||||
dispatch: Function, |
||||
|
||||
/** |
||||
* The avatar URL to be rendered. |
||||
*/ |
||||
_avatar: string, |
||||
|
||||
/** |
||||
* Display name of the local participant. |
||||
*/ |
||||
_displayName: string, |
||||
|
||||
/** |
||||
* Sets the side bar visible or hidden. |
||||
*/ |
||||
_visible: boolean |
||||
}; |
||||
|
||||
/** |
||||
* A component rendering a welcome page sidebar. |
||||
*/ |
||||
class WelcomePageSideBar extends Component<Props> { |
||||
/** |
||||
* Constructs a new SideBar instance. |
||||
* |
||||
* @inheritdoc |
||||
*/ |
||||
constructor(props) { |
||||
super(props); |
||||
|
||||
this._onHideSideBar = this._onHideSideBar.bind(this); |
||||
this._onOpenSettings = this._onOpenSettings.bind(this); |
||||
} |
||||
|
||||
/** |
||||
* Implements React's {@link Component#render()}, renders the sidebar. |
||||
* |
||||
* @inheritdoc |
||||
* @returns {ReactElement} |
||||
*/ |
||||
render() { |
||||
return ( |
||||
<SideBar |
||||
onHide = { this._onHideSideBar } |
||||
show = { this.props._visible }> |
||||
<Header style = { styles.sideBarHeader }> |
||||
<Avatar |
||||
style = { styles.avatar } |
||||
uri = { this.props._avatar } /> |
||||
<Text style = { styles.displayName }> |
||||
{ this.props._displayName } |
||||
</Text> |
||||
</Header> |
||||
<SafeAreaView style = { styles.sideBarBody }> |
||||
<ScrollView |
||||
style = { styles.itemContainer }> |
||||
<SideBarItem |
||||
i18Label = 'settings.title' |
||||
icon = 'settings' |
||||
onPress = { this._onOpenSettings } /> |
||||
<SideBarItem |
||||
i18Label = 'welcomepage.terms' |
||||
icon = 'info' |
||||
url = { TERMS_URL } /> |
||||
<SideBarItem |
||||
i18Label = 'welcomepage.privacy' |
||||
icon = 'info' |
||||
url = { PRIVACY_URL } /> |
||||
<SideBarItem |
||||
i18Label = 'welcomepage.sendFeedback' |
||||
icon = 'info' |
||||
url = { SEND_FEEDBACK_URL } /> |
||||
</ScrollView> |
||||
</SafeAreaView> |
||||
</SideBar> |
||||
); |
||||
} |
||||
|
||||
_onHideSideBar: () => void; |
||||
|
||||
/** |
||||
* Invoked when the sidebar has closed itslef (e.g. overlay pressed). |
||||
* |
||||
* @private |
||||
* @returns {void} |
||||
*/ |
||||
_onHideSideBar() { |
||||
this.props.dispatch(setSideBarVisibility(false)); |
||||
} |
||||
|
||||
_onOpenSettings: () => void; |
||||
|
||||
/** |
||||
* Opens the settings screen. |
||||
* |
||||
* @private |
||||
* @returns {void} |
||||
*/ |
||||
_onOpenSettings() { |
||||
const { dispatch } = this.props; |
||||
|
||||
dispatch(setSideBarVisibility(false)); |
||||
dispatch(showAppSettings()); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Maps (parts of) the redux state to the React {@code Component} props. |
||||
* |
||||
* @param {Object} state - The redux state. |
||||
* @protected |
||||
* @returns {Object} |
||||
*/ |
||||
function _mapStateToProps(state: Object) { |
||||
const _localParticipant = getLocalParticipant(state); |
||||
|
||||
return { |
||||
_avatar: getAvatarURL(_localParticipant), |
||||
_displayName: getParticipantDisplayName(state, _localParticipant.id), |
||||
_visible: state['features/welcome'].sideBarVisible |
||||
}; |
||||
} |
||||
|
||||
export default connect(_mapStateToProps)(WelcomePageSideBar); |
@ -0,0 +1,23 @@ |
||||
import { ReducerRegistry } from '../base/redux'; |
||||
import { SET_SIDEBAR_VISIBILITY } from './actionTypes'; |
||||
|
||||
const DEFAULT_STATE = { |
||||
sideBarVisible: false |
||||
}; |
||||
|
||||
/** |
||||
* Reduces the Redux actions of the feature features/recording. |
||||
*/ |
||||
ReducerRegistry.register('features/welcome', |
||||
(state = DEFAULT_STATE, action) => { |
||||
switch (action.type) { |
||||
case SET_SIDEBAR_VISIBILITY: |
||||
return { |
||||
...state, |
||||
sideBarVisible: action.sideBarVisible |
||||
}; |
||||
|
||||
default: |
||||
return state; |
||||
} |
||||
}); |
Loading…
Reference in new issue