mirror of https://github.com/jitsi/jitsi-meet
parent
612586ed1f
commit
19d1e3829d
@ -0,0 +1,81 @@ |
||||
// @flow
|
||||
|
||||
import type { Dispatch } from 'redux'; |
||||
|
||||
import { createToolbarEvent, sendAnalytics } from '../../analytics'; |
||||
import { translate } from '../../base/i18n'; |
||||
import { IconShareDoc } from '../../base/icons'; |
||||
import { connect } from '../../base/redux'; |
||||
import { AbstractButton, type AbstractButtonProps } from '../../base/toolbox'; |
||||
|
||||
import { toggleDocument } from '../actions'; |
||||
|
||||
|
||||
type Props = AbstractButtonProps & { |
||||
|
||||
/** |
||||
* Whether the shared document is being edited or not. |
||||
*/ |
||||
_editing: boolean, |
||||
|
||||
/** |
||||
* Redux dispatch function. |
||||
*/ |
||||
dispatch: Dispatch<any>, |
||||
}; |
||||
|
||||
/** |
||||
* Implements an {@link AbstractButton} to open the chat screen on mobile. |
||||
*/ |
||||
class SharedDocumentButton extends AbstractButton<Props, *> { |
||||
accessibilityLabel = 'toolbar.accessibilityLabel.document'; |
||||
icon = IconShareDoc; |
||||
label = 'toolbar.documentOpen'; |
||||
toggledLabel = 'toolbar.documentClose'; |
||||
|
||||
/** |
||||
* Handles clicking / pressing the button, and opens / closes the appropriate dialog. |
||||
* |
||||
* @private |
||||
* @returns {void} |
||||
*/ |
||||
_handleClick() { |
||||
sendAnalytics(createToolbarEvent( |
||||
'toggle.etherpad', |
||||
{ |
||||
enable: !this.props._editing |
||||
})); |
||||
this.props.dispatch(toggleDocument()); |
||||
} |
||||
|
||||
/** |
||||
* Indicates whether this button is in toggled state or not. |
||||
* |
||||
* @override |
||||
* @protected |
||||
* @returns {boolean} |
||||
*/ |
||||
_isToggled() { |
||||
return this.props._editing; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Maps part of the redux state to the component's props. |
||||
* |
||||
* @param {Object} state - The redux store/state. |
||||
* @param {Object} ownProps - The properties explicitly passed to the component |
||||
* instance. |
||||
* @returns {Object} |
||||
*/ |
||||
function _mapStateToProps(state: Object, ownProps: Object) { |
||||
const { documentUrl, editing } = state['features/etherpad']; |
||||
const { visible = Boolean(documentUrl) } = ownProps; |
||||
|
||||
return { |
||||
_editing: editing, |
||||
visible |
||||
}; |
||||
} |
||||
|
||||
export default translate(connect(_mapStateToProps)(SharedDocumentButton)); |
||||
@ -0,0 +1,2 @@ |
||||
export { default as SharedDocument } from './native/SharedDocument'; |
||||
export { default as SharedDocumentButton } from './SharedDocumentButton'; |
||||
@ -0,0 +1 @@ |
||||
export { default as SharedDocumentButton } from './SharedDocumentButton'; |
||||
@ -0,0 +1,178 @@ |
||||
// @flow
|
||||
|
||||
import React, { PureComponent } from 'react'; |
||||
import { SafeAreaView, View } from 'react-native'; |
||||
import { WebView } from 'react-native-webview'; |
||||
import type { Dispatch } from 'redux'; |
||||
|
||||
import { ColorSchemeRegistry } from '../../../base/color-scheme'; |
||||
import { translate } from '../../../base/i18n'; |
||||
import { HeaderWithNavigation, LoadingIndicator, SlidingView } from '../../../base/react'; |
||||
import { connect } from '../../../base/redux'; |
||||
|
||||
import { toggleDocument } from '../../actions'; |
||||
import { getSharedDocumentUrl } from '../../functions'; |
||||
|
||||
import styles, { INDICATOR_COLOR } from './styles'; |
||||
|
||||
/** |
||||
* The type of the React {@code Component} props of {@code ShareDocument}. |
||||
*/ |
||||
type Props = { |
||||
|
||||
/** |
||||
* URL for the shared document. |
||||
*/ |
||||
_documentUrl: string, |
||||
|
||||
/** |
||||
* Color schemed style of the header component. |
||||
*/ |
||||
_headerStyles: Object, |
||||
|
||||
/** |
||||
* True if the chat window should be rendered. |
||||
*/ |
||||
_isOpen: boolean, |
||||
|
||||
/** |
||||
* The Redux dispatch function. |
||||
*/ |
||||
dispatch: Dispatch<any>, |
||||
|
||||
/** |
||||
* Function to be used to translate i18n labels. |
||||
*/ |
||||
t: Function |
||||
}; |
||||
|
||||
/** |
||||
* Implements a React native component that renders the shared document window. |
||||
*/ |
||||
class SharedDocument extends PureComponent<Props> { |
||||
/** |
||||
* Instantiates a new instance. |
||||
* |
||||
* @inheritdoc |
||||
*/ |
||||
constructor(props: Props) { |
||||
super(props); |
||||
|
||||
this._onClose = this._onClose.bind(this); |
||||
this._onError = this._onError.bind(this); |
||||
this._renderLoading = this._renderLoading.bind(this); |
||||
} |
||||
|
||||
/** |
||||
* Implements React's {@link Component#render()}. |
||||
* |
||||
* @inheritdoc |
||||
*/ |
||||
render() { |
||||
const { _documentUrl, _isOpen } = this.props; |
||||
const webViewStyles = this._getWebViewStyles(); |
||||
|
||||
return ( |
||||
<SlidingView |
||||
onHide = { this._onClose } |
||||
position = 'bottom' |
||||
show = { _isOpen } > |
||||
<View style = { styles.webViewWrapper }> |
||||
<HeaderWithNavigation |
||||
headerLabelKey = 'documentSharing.title' |
||||
onPressBack = { this._onClose } /> |
||||
<SafeAreaView style = { webViewStyles }> |
||||
<WebView |
||||
onError = { this._onError } |
||||
renderLoading = { this._renderLoading } |
||||
source = {{ uri: _documentUrl }} |
||||
startInLoadingState = { true } /> |
||||
</SafeAreaView> |
||||
</View> |
||||
</SlidingView> |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* Computes the styles required for the WebView component. |
||||
* |
||||
* @returns {Object} |
||||
*/ |
||||
_getWebViewStyles() { |
||||
return { |
||||
...styles.webView, |
||||
backgroundColor: this.props._headerStyles.screenHeader.backgroundColor |
||||
}; |
||||
} |
||||
|
||||
_onClose: () => boolean |
||||
|
||||
/** |
||||
* Closes the window. |
||||
* |
||||
* @returns {boolean} |
||||
*/ |
||||
_onClose() { |
||||
const { _isOpen, dispatch } = this.props; |
||||
|
||||
if (_isOpen) { |
||||
dispatch(toggleDocument()); |
||||
|
||||
return true; |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
_onError: () => void; |
||||
|
||||
/** |
||||
* Callback to handle the error if the page fails to load. |
||||
* |
||||
* @returns {void} |
||||
*/ |
||||
_onError() { |
||||
const { _isOpen, dispatch } = this.props; |
||||
|
||||
if (_isOpen) { |
||||
dispatch(toggleDocument()); |
||||
} |
||||
} |
||||
|
||||
_renderLoading: () => React$Component<any>; |
||||
|
||||
/** |
||||
* Renders the loading indicator. |
||||
* |
||||
* @returns {React$Component<any>} |
||||
*/ |
||||
_renderLoading() { |
||||
return ( |
||||
<View style = { styles.indicatorWrapper }> |
||||
<LoadingIndicator |
||||
color = { INDICATOR_COLOR } |
||||
size = 'large' /> |
||||
</View> |
||||
); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Maps (parts of) the redux state to {@link SharedDocument} React {@code Component} props. |
||||
* |
||||
* @param {Object} state - The redux store/state. |
||||
* @private |
||||
* @returns {Object} |
||||
*/ |
||||
export function _mapStateToProps(state: Object) { |
||||
const { editing } = state['features/etherpad']; |
||||
const documentUrl = getSharedDocumentUrl(state); |
||||
|
||||
return { |
||||
_documentUrl: documentUrl, |
||||
_headerStyles: ColorSchemeRegistry.get(state, 'Header'), |
||||
_isOpen: editing |
||||
}; |
||||
} |
||||
|
||||
export default translate(connect(_mapStateToProps)(SharedDocument)); |
||||
@ -0,0 +1,24 @@ |
||||
// @flow
|
||||
|
||||
import { ColorPalette } from '../../../base/styles'; |
||||
|
||||
export const INDICATOR_COLOR = ColorPalette.lightGrey; |
||||
|
||||
export default { |
||||
|
||||
indicatorWrapper: { |
||||
alignItems: 'center', |
||||
backgroundColor: ColorPalette.white, |
||||
height: '100%', |
||||
justifyContent: 'center' |
||||
}, |
||||
|
||||
webView: { |
||||
flex: 1 |
||||
}, |
||||
|
||||
webViewWrapper: { |
||||
flex: 1, |
||||
flexDirection: 'column' |
||||
} |
||||
}; |
||||
@ -0,0 +1,34 @@ |
||||
// @flow
|
||||
|
||||
import { toState } from '../base/redux'; |
||||
|
||||
const ETHERPAD_OPTIONS = { |
||||
showControls: 'true', |
||||
showChat: 'false', |
||||
showLineNumbers: 'true', |
||||
useMonospaceFont: 'false' |
||||
}; |
||||
|
||||
/** |
||||
* Retrieves the current sahred document URL. |
||||
* |
||||
* @param {Function|Object} stateful - The redux store or {@code getState} function. |
||||
* @returns {?string} - Current shared document URL or undefined. |
||||
*/ |
||||
export function getSharedDocumentUrl(stateful: Function | Object) { |
||||
const state = toState(stateful); |
||||
const { documentUrl } = state['features/etherpad']; |
||||
const { displayName } = state['features/base/settings']; |
||||
|
||||
if (!documentUrl) { |
||||
return undefined; |
||||
} |
||||
|
||||
const params = new URLSearchParams(ETHERPAD_OPTIONS); |
||||
|
||||
if (displayName) { |
||||
params.append('userName', displayName); |
||||
} |
||||
|
||||
return `${documentUrl}?${params.toString()}`; |
||||
} |
||||
@ -1,5 +1,7 @@ |
||||
export * from './actions'; |
||||
export * from './actionTypes'; |
||||
export * from './components'; |
||||
export * from './functions'; |
||||
|
||||
import './middleware'; |
||||
import './reducer'; |
||||
|
||||
Loading…
Reference in new issue