From 57d14d95177a551011fa6c0b11fb00e74c54d4ee Mon Sep 17 00:00:00 2001 From: Bettenbuk Zoltan Date: Mon, 30 Mar 2020 17:26:15 +0200 Subject: [PATCH] feat: help centre --- lang/main.json | 4 + react/features/app/components/AbstractApp.js | 12 ++ react/features/app/components/App.native.js | 12 ++ .../base/color-scheme/defaultScheme.js | 1 + react/features/base/modal/actionTypes.js | 6 + react/features/base/modal/actions.js | 19 +++ .../base/modal/components/JitsiModal.js | 151 ++++++++++++++++++ .../base/modal/components/index.native.js | 3 + .../base/modal/components/index.web.js | 6 + .../features/base/modal/components/styles.js | 15 ++ react/features/base/modal/index.js | 7 + react/features/base/modal/reducer.js | 17 ++ react/features/help/components/HelpView.js | 54 +++++++ react/features/help/components/index.js | 3 + react/features/help/constants.js | 3 + react/features/help/index.js | 4 + .../components/WelcomePageSideBar.native.js | 30 ++-- 17 files changed, 338 insertions(+), 9 deletions(-) create mode 100644 react/features/base/modal/actionTypes.js create mode 100644 react/features/base/modal/actions.js create mode 100644 react/features/base/modal/components/JitsiModal.js create mode 100644 react/features/base/modal/components/index.native.js create mode 100644 react/features/base/modal/components/index.web.js create mode 100644 react/features/base/modal/components/styles.js create mode 100644 react/features/base/modal/index.js create mode 100644 react/features/base/modal/reducer.js create mode 100644 react/features/help/components/HelpView.js create mode 100644 react/features/help/components/index.js create mode 100644 react/features/help/constants.js create mode 100644 react/features/help/index.js diff --git a/lang/main.json b/lang/main.json index 4be8f468e..05e4ec757 100644 --- a/lang/main.json +++ b/lang/main.json @@ -757,6 +757,7 @@ "connectCalendarButton": "Connect your calendar", "connectCalendarText": "Connect your calendar to view all your meetings in {{app}}. Plus, add {{provider}} meetings to your calendar and start them with one click.", "enterRoomTitle": "Start a new meeting", + "getHelp": "Get help", "roomNameAllowedChars": "Meeting name should not contain any of these characters: ?, &, :, ', \", %, #.", "go": "GO", "goSmall": "GO", @@ -776,5 +777,8 @@ "lonelyMeetingExperience": { "button": "Invite others", "youAreAlone": "You are the only one in the meeting" + }, + "helpView": { + "header": "Help centre" } } diff --git a/react/features/app/components/AbstractApp.js b/react/features/app/components/AbstractApp.js index fc236c6bb..325498950 100644 --- a/react/features/app/components/AbstractApp.js +++ b/react/features/app/components/AbstractApp.js @@ -89,10 +89,22 @@ export class AbstractApp extends BaseApp { return ( + { this._createExtraPlatformSpecificElement() } ); } + /** + * Renders platform specific extra elements to be added alongside with the main element, if need be. + * + * NOTE: Overridden by child components. + * + * @returns {React$Element} + */ + _createExtraPlatformSpecificElement() { + return null; + } + _createMainElement: (React$Element<*>, Object) => ?React$Element<*>; /** diff --git a/react/features/app/components/App.native.js b/react/features/app/components/App.native.js index 996e0d028..bc50c3fc5 100644 --- a/react/features/app/components/App.native.js +++ b/react/features/app/components/App.native.js @@ -12,6 +12,7 @@ import { Platform } from '../../base/react'; import '../../base/responsive-ui'; import { updateSettings } from '../../base/settings'; import '../../google-api'; +import { HelpView } from '../../help'; import '../../mobile/audio-mode'; import '../../mobile/back-button'; import '../../mobile/background'; @@ -107,6 +108,17 @@ export class App extends AbstractApp { }); } + /** + * Renders platform specific extra elements to be added alongside with the main element, if need be. + * + * @inheritdoc + */ + _createExtraPlatformSpecificElement() { + return ( + + ); + } + /** * Attempts to disable the use of React Native * {@link ExceptionsManager#handleException} on platforms and in diff --git a/react/features/base/color-scheme/defaultScheme.js b/react/features/base/color-scheme/defaultScheme.js index bcb22a718..8c232fc94 100644 --- a/react/features/base/color-scheme/defaultScheme.js +++ b/react/features/base/color-scheme/defaultScheme.js @@ -39,6 +39,7 @@ export default { statusBarContent: ColorPalette.white, text: ColorPalette.white }, + 'Modal': {}, 'LargeVideo': { background: 'rgb(42, 58, 75)' }, diff --git a/react/features/base/modal/actionTypes.js b/react/features/base/modal/actionTypes.js new file mode 100644 index 000000000..90fb8901f --- /dev/null +++ b/react/features/base/modal/actionTypes.js @@ -0,0 +1,6 @@ +// @flow + +/** + * Action type to set the ID of the active modal (or undefined if needs to be hidden). + */ +export const SET_ACTIVE_MODAL_ID = 'SET_ACTIVE_MODAL_ID'; diff --git a/react/features/base/modal/actions.js b/react/features/base/modal/actions.js new file mode 100644 index 000000000..70c0c7bf0 --- /dev/null +++ b/react/features/base/modal/actions.js @@ -0,0 +1,19 @@ +// @flow + +import { SET_ACTIVE_MODAL_ID } from './actionTypes'; + +/** + * Action to set the ID of the active modal (or undefined if needs to be hidden). + * + * @param {string} activeModalId - The new modal ID or undefined. + * @returns {{ + * activeModalId: string, + * type: SET_ACTIVE_MODAL_ID + * }} + */ +export function setActiveModalId(activeModalId: ?string) { + return { + activeModalId, + type: SET_ACTIVE_MODAL_ID + }; +} diff --git a/react/features/base/modal/components/JitsiModal.js b/react/features/base/modal/components/JitsiModal.js new file mode 100644 index 000000000..66f3a77be --- /dev/null +++ b/react/features/base/modal/components/JitsiModal.js @@ -0,0 +1,151 @@ +// @flow + +import React, { PureComponent } from 'react'; +import { SafeAreaView, View } from 'react-native'; + +import { ColorSchemeRegistry } from '../../color-scheme'; +import { HeaderWithNavigation, SlidingView } from '../../react'; +import { connect } from '../../redux'; +import { StyleType } from '../../styles'; + +import { setActiveModalId } from '../actions'; + +import styles from './styles'; + +type Props = { + + /** + * The color schemed style of the common header component. + */ + _headerStyles: StyleType, + + /** + * True if the modal should be shown, false otherwise. + */ + _show: boolean, + + /** + * The color schemed style of the modal. + */ + _styles: StyleType, + + /** + * The children component(s) of the Modal, to be rendered. + */ + children: React$Node, + + /** + * The Redux Dispatch function. + */ + dispatch: Function, + + /** + * The i18n label key of the header title. + */ + headerLabelKey: string, + + /** + * The ID of the modal that is being rendered. This is used to show/hide the modal. + */ + modalId: string, + + /** + * Callback to be invoked when the modal closes. + */ + onClose?: Function, + + /** + * The position from where the modal should be opened. This is derived from the + * props of the {@code SlidingView} with the same name. + */ + position?: string +}; + +/** + * Implements a custom Jitsi Modal that doesn't use the built in native + * Modal component of React Native. + */ +class JitsiModal extends PureComponent { + static defaultProps = { + position: 'bottom' + }; + + /** + * Instantiates a new component. + * + * @inheritdoc + */ + constructor(props: Props) { + super(props); + + this._onRequestClose = this._onRequestClose.bind(this); + } + + /** + * Implements {@code PureComponent#render}. + * + * @inheritdoc + */ + render() { + const { _headerStyles, _show, _styles, children, headerLabelKey, position } = this.props; + + return ( + + + + + { children } + + + + ); + } + + _onRequestClose: () => boolean; + + /** + * Callback to be invoked when the SlidingView requests closing. + * + * @returns {boolean} + */ + _onRequestClose() { + const { _show, dispatch, onClose } = this.props; + + if (_show) { + if (typeof onClose === 'function') { + onClose(); + } + dispatch(setActiveModalId()); + + return true; + } + + return false; + } +} + +/** + * Maps part of the Redix state to the props of this component. + * + * @param {Object} state - The Redux state. + * @param {Props} ownProps - The own props of the component. + * @returns {Props} + */ +function _mapStateToProps(state, ownProps): $Shape { + return { + _headerStyles: ColorSchemeRegistry.get(state, 'Header'), + _show: state['features/base/modal'].activeModalId === ownProps.modalId, + _styles: ColorSchemeRegistry.get(state, 'Modal') + }; +} + +export default connect(_mapStateToProps)(JitsiModal); diff --git a/react/features/base/modal/components/index.native.js b/react/features/base/modal/components/index.native.js new file mode 100644 index 000000000..524eacbdd --- /dev/null +++ b/react/features/base/modal/components/index.native.js @@ -0,0 +1,3 @@ +// @flow + +export { default as JitsiModal } from './JitsiModal'; diff --git a/react/features/base/modal/components/index.web.js b/react/features/base/modal/components/index.web.js new file mode 100644 index 000000000..125099d63 --- /dev/null +++ b/react/features/base/modal/components/index.web.js @@ -0,0 +1,6 @@ +// @flow + +import { Component } from 'react'; + +export const JitsiModal = Component; + diff --git a/react/features/base/modal/components/styles.js b/react/features/base/modal/components/styles.js new file mode 100644 index 000000000..8a4f96a28 --- /dev/null +++ b/react/features/base/modal/components/styles.js @@ -0,0 +1,15 @@ +// @flow + +import { ColorSchemeRegistry, schemeColor } from '../../color-scheme'; + +export default { + safeArea: { + flex: 1 + } +}; + +ColorSchemeRegistry.register('Modal', { + page: { + backgroundColor: schemeColor('background') + } +}); diff --git a/react/features/base/modal/index.js b/react/features/base/modal/index.js new file mode 100644 index 000000000..04b78b685 --- /dev/null +++ b/react/features/base/modal/index.js @@ -0,0 +1,7 @@ +// @flow + +import './reducer'; + +export * from './actions'; +export * from './actionTypes'; +export * from './components'; diff --git a/react/features/base/modal/reducer.js b/react/features/base/modal/reducer.js new file mode 100644 index 000000000..c60e0e9f1 --- /dev/null +++ b/react/features/base/modal/reducer.js @@ -0,0 +1,17 @@ +// @flow + +import { ReducerRegistry } from '../redux'; + +import { SET_ACTIVE_MODAL_ID } from './actionTypes'; + +ReducerRegistry.register('features/base/modal', (state = {}, action) => { + switch (action.type) { + case SET_ACTIVE_MODAL_ID: + return { + ...state, + activeModalId: action.activeModalId + }; + } + + return state; +}); diff --git a/react/features/help/components/HelpView.js b/react/features/help/components/HelpView.js new file mode 100644 index 000000000..d0e58f937 --- /dev/null +++ b/react/features/help/components/HelpView.js @@ -0,0 +1,54 @@ +// @flow + +import React, { PureComponent } from 'react'; +import WebView from 'react-native-webview'; + +import { JitsiModal } from '../../base/modal'; +import { connect } from '../../base/redux'; + +import { HELP_VIEW_MODAL_ID } from '../constants'; + +const DEFAULT_HELP_CENTRE_URL = 'https://web-cdn.jitsi.net/faq/meet-faq.html'; + +type Props = { + + /** + * The URL to display in the Help Centre. + */ + _url: string +} + +/** + * Implements a page that renders the help content for the app. + */ +class HelpView extends PureComponent { + /** + * Implements {@code PureComponent#render()}. + * + * @inheritdoc + * @returns {ReactElement} + */ + render() { + return ( + + + + ); + } +} + +/** + * Maps part of the Redux state to the props of this component. + * + * @param {Object} state - The Redux state. + * @returns {Props} + */ +function _mapStateToProps(state) { + return { + _url: state['features/base/config'].helpCentreURL || DEFAULT_HELP_CENTRE_URL + }; +} + +export default connect(_mapStateToProps)(HelpView); diff --git a/react/features/help/components/index.js b/react/features/help/components/index.js new file mode 100644 index 000000000..22e3c111e --- /dev/null +++ b/react/features/help/components/index.js @@ -0,0 +1,3 @@ +// @flow + +export { default as HelpView } from './HelpView'; diff --git a/react/features/help/constants.js b/react/features/help/constants.js new file mode 100644 index 000000000..701adae6c --- /dev/null +++ b/react/features/help/constants.js @@ -0,0 +1,3 @@ +// @flow + +export const HELP_VIEW_MODAL_ID = 'helpView'; diff --git a/react/features/help/index.js b/react/features/help/index.js new file mode 100644 index 000000000..4c3ff3b68 --- /dev/null +++ b/react/features/help/index.js @@ -0,0 +1,4 @@ +// @flow + +export * from './components'; +export * from './constants'; diff --git a/react/features/welcome/components/WelcomePageSideBar.native.js b/react/features/welcome/components/WelcomePageSideBar.native.js index f6f939392..615de98ed 100644 --- a/react/features/welcome/components/WelcomePageSideBar.native.js +++ b/react/features/welcome/components/WelcomePageSideBar.native.js @@ -4,7 +4,8 @@ import React, { Component } from 'react'; import { SafeAreaView, ScrollView, Text } from 'react-native'; import { Avatar } from '../../base/avatar'; -import { IconInfo, IconSettings } from '../../base/icons'; +import { IconInfo, IconSettings, IconHelp } from '../../base/icons'; +import { setActiveModalId } from '../../base/modal'; import { getLocalParticipant, getParticipantDisplayName @@ -14,6 +15,7 @@ import { SlidingView } from '../../base/react'; import { connect } from '../../base/redux'; +import { HELP_VIEW_MODAL_ID } from '../../help'; import { setSettingsViewVisible } from '../../settings'; import { setSideBarVisible } from '../actions'; @@ -25,11 +27,6 @@ import styles, { SIDEBAR_AVATAR_SIZE } from './styles'; */ 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. */ @@ -72,6 +69,7 @@ class WelcomePageSideBar extends Component { // Bind event handlers so they are only bound once per instance. this._onHideSideBar = this._onHideSideBar.bind(this); + this._onOpenHelpPage = this._onOpenHelpPage.bind(this); this._onOpenSettings = this._onOpenSettings.bind(this); } @@ -112,9 +110,9 @@ class WelcomePageSideBar extends Component { label = 'welcomepage.privacy' url = { PRIVACY_URL } /> + icon = { IconHelp } + label = 'welcomepage.getHelp' + onPress = { this._onOpenHelpPage } /> @@ -133,6 +131,20 @@ class WelcomePageSideBar extends Component { this.props.dispatch(setSideBarVisible(false)); } + _onOpenHelpPage: () => void; + + /** + * Shows the {@link HelpView}. + * + * @returns {void} + */ + _onOpenHelpPage() { + const { dispatch } = this.props; + + dispatch(setSideBarVisible(false)); + dispatch(setActiveModalId(HELP_VIEW_MODAL_ID)); + } + _onOpenSettings: () => void; /**