mirror of https://github.com/jitsi/jitsi-meet
pull/10896/head
jitsi-meet_6898
parent
b9f3448379
commit
debb63d3d6
@ -0,0 +1,57 @@ |
||||
import React from 'react'; |
||||
import { Text } from 'react-native'; |
||||
|
||||
import { brandedDialog as styles } from './native'; |
||||
|
||||
/** |
||||
* Renders a specific {@code string} which may contain HTML. |
||||
* |
||||
* @param {string|undefined} html - The {@code string} which may |
||||
* contain HTML to render. |
||||
* @returns {ReactElement[]|string} |
||||
*/ |
||||
export function renderHTML(html) { |
||||
if (typeof html === 'string') { |
||||
// At the time of this writing, the specified HTML contains a couple
|
||||
// of spaces one after the other. They do not cause a visible
|
||||
// problem on Web, because the specified HTML is rendered as, well,
|
||||
// HTML. However, we're not rendering HTML here.
|
||||
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
html = html.replace(/\s{2,}/gi, ' '); |
||||
|
||||
// Render text in <b>text</b> in bold.
|
||||
const opening = /<\s*b\s*>/gi; |
||||
const closing = /<\s*\/\s*b\s*>/gi; |
||||
let o; |
||||
let c; |
||||
let prevClosingLastIndex = 0; |
||||
const r = []; |
||||
|
||||
// eslint-disable-next-line no-cond-assign
|
||||
while (o = opening.exec(html)) { |
||||
closing.lastIndex = opening.lastIndex; |
||||
|
||||
// eslint-disable-next-line no-cond-assign
|
||||
if (c = closing.exec(html)) { |
||||
r.push(html.substring(prevClosingLastIndex, o.index)); |
||||
r.push( |
||||
<Text style = { styles.boldDialogText }> |
||||
{ html.substring(opening.lastIndex, c.index) } |
||||
</Text>); |
||||
opening.lastIndex |
||||
= prevClosingLastIndex |
||||
= closing.lastIndex; |
||||
} else { |
||||
break; |
||||
} |
||||
} |
||||
if (prevClosingLastIndex < html.length) { |
||||
r.push(html.substring(prevClosingLastIndex)); |
||||
} |
||||
|
||||
return r; |
||||
} |
||||
|
||||
return html; |
||||
} |
@ -1,107 +1,165 @@ |
||||
// @flow
|
||||
|
||||
import React from 'react'; |
||||
import { Text, TouchableOpacity } from 'react-native'; |
||||
import { Platform, View } from 'react-native'; |
||||
import Dialog from 'react-native-dialog'; |
||||
|
||||
import { translate } from '../../../i18n'; |
||||
import { connect } from '../../../redux'; |
||||
import { StyleType } from '../../../styles'; |
||||
import { _abstractMapStateToProps } from '../../functions'; |
||||
import AbstractDialog from '../AbstractDialog'; |
||||
import { renderHTML } from '../functions.native'; |
||||
|
||||
import { type Props as BaseProps } from './BaseDialog'; |
||||
import BaseSubmitDialog from './BaseSubmitDialog'; |
||||
import { brandedDialog } from './styles'; |
||||
import styles from './styles'; |
||||
|
||||
type Props = BaseProps & { |
||||
|
||||
/** |
||||
* The type of the React {@code Component} props of |
||||
* {@link ConfirmDialog}. |
||||
*/ |
||||
type Props = { |
||||
|
||||
/** |
||||
* The color-schemed stylesheet of the feature. |
||||
* The i18n key of the text label for the cancel button. |
||||
*/ |
||||
_dialogStyles: StyleType, |
||||
cancelLabel: string, |
||||
|
||||
/** |
||||
* Untranslated i18n key of the content to be displayed. |
||||
* |
||||
* NOTE: This dialog also adds support to Object type keys that will be |
||||
* translated using the provided params. See i18n function |
||||
* {@code translate(string, Object)} for more details. |
||||
* The React {@code Component} children. |
||||
*/ |
||||
contentKey: string | { key: string, params: Object}, |
||||
children?: React$Node, |
||||
|
||||
/** |
||||
* The handler for the event when clicking the 'confirmNo' button. |
||||
* Defaults to onCancel if absent. |
||||
* The i18n key of the text label for the confirm button. |
||||
*/ |
||||
onDecline?: Function, |
||||
confirmLabel: string, |
||||
|
||||
t: Function |
||||
} |
||||
/** |
||||
* Dialog description key for translations. |
||||
*/ |
||||
descriptionKey?: string | Object, |
||||
|
||||
/** |
||||
* Whether or not the nature of the confirm button is destructive. |
||||
*/ |
||||
isConfirmDestructive?: Boolean, |
||||
|
||||
/** |
||||
* Invoked to obtain translated strings. |
||||
*/ |
||||
t: Function, |
||||
|
||||
/** |
||||
* Dialog title. |
||||
*/ |
||||
title?: string, |
||||
}; |
||||
|
||||
/** |
||||
* Implements a confirm dialog component. |
||||
* React Component for getting confirmation to stop a file recording session in |
||||
* progress. |
||||
* |
||||
* @augments Component |
||||
*/ |
||||
class ConfirmDialog extends BaseSubmitDialog<Props, *> { |
||||
class ConfirmDialog extends AbstractDialog<Props> { |
||||
/** |
||||
* Returns the title key of the submit button. |
||||
* Default values for {@code ConfirmDialog} component's properties. |
||||
* |
||||
* @returns {string} |
||||
* @static |
||||
*/ |
||||
_getSubmitButtonKey() { |
||||
return this.props.okKey || 'dialog.confirmYes'; |
||||
} |
||||
|
||||
_onCancel: () => void; |
||||
static defaultProps = { |
||||
isConfirmDestructive: false |
||||
}; |
||||
|
||||
/** |
||||
* Renders the 'No' button. |
||||
* |
||||
* NOTE: The {@code ConfirmDialog} is the only dialog right now that |
||||
* renders 2 buttons, mainly for clarity. |
||||
* Renders the dialog description. |
||||
* |
||||
* @inheritdoc |
||||
* @returns {React$Component} |
||||
*/ |
||||
_renderAdditionalButtons() { |
||||
const { _dialogStyles, cancelKey, onDecline, t } = this.props; |
||||
_renderDescription() { |
||||
const { descriptionKey, t } = this.props; |
||||
const description |
||||
= typeof descriptionKey === 'string' |
||||
? t(descriptionKey) |
||||
: renderHTML( |
||||
t(descriptionKey?.key, descriptionKey?.params) |
||||
); |
||||
|
||||
return ( |
||||
<TouchableOpacity |
||||
onPress = { onDecline || this._onCancel } |
||||
style = { [ |
||||
_dialogStyles.button, |
||||
brandedDialog.buttonFarLeft, |
||||
_dialogStyles.buttonSeparator |
||||
] }> |
||||
<Text style = { _dialogStyles.buttonLabel }> |
||||
{ t(cancelKey || 'dialog.confirmNo') } |
||||
</Text> |
||||
</TouchableOpacity> |
||||
<Dialog.Description> |
||||
{ description } |
||||
</Dialog.Description> |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* Implements {@code BaseSubmitDialog._renderSubmittable}. |
||||
* Implements {@code Component#render}. |
||||
* |
||||
* @inheritdoc |
||||
*/ |
||||
_renderSubmittable() { |
||||
if (this.props.children) { |
||||
return this.props.children; |
||||
render() { |
||||
const { |
||||
cancelLabel, |
||||
children, |
||||
confirmLabel, |
||||
isConfirmDestructive, |
||||
t, |
||||
title |
||||
} = this.props; |
||||
|
||||
const dialogButtonStyle |
||||
= isConfirmDestructive |
||||
? styles.destructiveDialogButton : styles.dialogButton; |
||||
|
||||
let rightLabel; |
||||
let leftLabel; |
||||
let rightOnPress; |
||||
let leftOnPress; |
||||
let rightStyle; |
||||
let leftStyle; |
||||
|
||||
if (Platform.OS === 'android') { |
||||
rightLabel = confirmLabel || 'dialog.confirmYes'; |
||||
rightOnPress = () => this._onSubmit(); |
||||
rightStyle = dialogButtonStyle; |
||||
leftLabel = cancelLabel || 'dialog.confirmNo'; |
||||
leftOnPress = () => this._onCancel(); |
||||
leftStyle = styles.dialogButton; |
||||
} else { |
||||
rightLabel = cancelLabel || 'dialog.confirmNo'; |
||||
rightOnPress = () => this._onCancel(); |
||||
rightStyle = styles.dialogButton; |
||||
leftLabel = confirmLabel || 'dialog.confirmYes'; |
||||
leftOnPress = () => this._onSubmit(); |
||||
leftStyle = dialogButtonStyle; |
||||
} |
||||
|
||||
const { _dialogStyles, contentKey, t } = this.props; |
||||
const content |
||||
= typeof contentKey === 'string' |
||||
? t(contentKey) |
||||
: this._renderHTML(t(contentKey.key, contentKey.params)); |
||||
|
||||
return ( |
||||
<Text style = { _dialogStyles.text }> |
||||
{ content } |
||||
</Text> |
||||
<View> |
||||
<Dialog.Container |
||||
visible = { true }> |
||||
{ |
||||
title && <Dialog.Title> |
||||
{ t(title) } |
||||
</Dialog.Title> |
||||
} |
||||
{ this._renderDescription() } |
||||
{ children } |
||||
<Dialog.Button |
||||
label = { t(rightLabel) } |
||||
onPress = { rightOnPress } |
||||
style = { rightStyle } /> |
||||
<Dialog.Button |
||||
label = { t(leftLabel) } |
||||
onPress = { leftOnPress } |
||||
style = { leftStyle } /> |
||||
</Dialog.Container> |
||||
</View> |
||||
); |
||||
} |
||||
|
||||
_renderHTML: string => Object | string; |
||||
_onCancel: () => void; |
||||
|
||||
_onSubmit: (?string) => void; |
||||
} |
||||
|
||||
export default translate(connect(_abstractMapStateToProps)(ConfirmDialog)); |
||||
export default translate(connect()(ConfirmDialog)); |
||||
|
@ -1,46 +0,0 @@ |
||||
// @flow
|
||||
|
||||
import { Component } from 'react'; |
||||
|
||||
type Props = { |
||||
|
||||
/** |
||||
* The Redux dispatch function. |
||||
*/ |
||||
dispatch: Function, |
||||
|
||||
/** |
||||
* Function to translate i18n labels. |
||||
*/ |
||||
t: Function |
||||
}; |
||||
|
||||
/** |
||||
* Abstract dialog to confirm blocking mic and camera for all participants. |
||||
*/ |
||||
export default class AbstractBlockAudioVideoDialog |
||||
extends Component<Props> { |
||||
/** |
||||
* Initializes a new {@code AbstractBlockAudioVideoDialog} instance. |
||||
* |
||||
* @inheritdoc |
||||
*/ |
||||
constructor(props: Props) { |
||||
super(props); |
||||
|
||||
this._onSubmit = this._onSubmit.bind(this); |
||||
} |
||||
|
||||
_onSubmit: () => boolean; |
||||
|
||||
/** |
||||
* Callback for the confirm button. |
||||
* |
||||
* @private |
||||
* @returns {boolean} - True (to note that the modal should be closed). |
||||
*/ |
||||
_onSubmit() { |
||||
|
||||
return true; |
||||
} |
||||
} |
@ -1,32 +0,0 @@ |
||||
// @flow
|
||||
|
||||
import React from 'react'; |
||||
|
||||
import { ConfirmDialog } from '../../../base/dialog'; |
||||
import { translate } from '../../../base/i18n'; |
||||
import { connect } from '../../../base/redux'; |
||||
import AbstractBlockAudioVideoDialog |
||||
from '../AbstractBlockAudioVideoDialog'; |
||||
|
||||
/** |
||||
* Dialog to confirm a remote participant kick action. |
||||
*/ |
||||
class BlockAudioVideoDialog extends AbstractBlockAudioVideoDialog { |
||||
/** |
||||
* Implements React's {@link Component#render()}. |
||||
* |
||||
* @inheritdoc |
||||
* @returns {ReactElement} |
||||
*/ |
||||
render() { |
||||
return ( |
||||
<ConfirmDialog |
||||
contentKey = 'dialog.blockAudioVideoMsg' |
||||
onSubmit = { this._onSubmit } /> |
||||
); |
||||
} |
||||
|
||||
_onSubmit: () => boolean; |
||||
} |
||||
|
||||
export default translate(connect()(BlockAudioVideoDialog)); |
Loading…
Reference in new issue