123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382 |
- /* eslint-disable react/jsx-no-bind */
- import React, { useRef, useState } from 'react';
- import { WithTranslation } from 'react-i18next';
-
- import { translate } from '../../../../base/i18n/functions';
- import { copyText } from '../../../../base/util/copyText.web';
- import { LOCKED_LOCALLY } from '../../../../room-lock/constants';
- import { NOTIFY_CLICK_MODE } from '../../../../toolbox/constants';
-
- import PasswordForm from './PasswordForm';
- import { INotifyClick } from './SecurityDialog';
-
- const DIGITS_ONLY = /^\d+$/;
- const KEY = 'add-passcode';
-
- interface IProps extends WithTranslation {
-
- /**
- * Toolbar buttons which have their click exposed through the API.
- */
- buttonsWithNotifyClick: Array<string | INotifyClick>;
-
- /**
- * Whether or not the current user can modify the current password.
- */
- canEditPassword: boolean;
-
- /**
- * The JitsiConference for which to display a lock state and change the
- * password.
- */
- conference: any;
-
- /**
- * The value for how the conference is locked (or undefined if not locked)
- * as defined by room-lock constants.
- */
- locked: string;
-
- /**
- * The current known password for the JitsiConference.
- */
- password: string;
-
- /**
- * Whether or not to show the password in editing mode.
- */
- passwordEditEnabled: boolean;
-
- /**
- * The number of digits to be used in the password.
- */
- passwordNumberOfDigits?: number;
-
- /**
- * Action that sets the conference password.
- */
- setPassword: Function;
-
- /**
- * Method that sets whether the password editing is enabled or not.
- */
- setPasswordEditEnabled: Function;
- }
-
- /**
- * Component that handles the password manipulation from the invite dialog.
- *
- * @returns {React$Element<any>}
- */
- function PasswordSection({
- buttonsWithNotifyClick,
- canEditPassword,
- conference,
- locked,
- password,
- passwordEditEnabled,
- passwordNumberOfDigits,
- setPassword,
- setPasswordEditEnabled,
- t }: IProps) {
-
- const formRef = useRef<HTMLDivElement>(null);
- const [ passwordVisible, setPasswordVisible ] = useState(false);
-
- /**
- * Callback invoked to set a password on the current JitsiConference.
- *
- * @param {string} enteredPassword - The new password to be used to lock the
- * current JitsiConference.
- * @private
- * @returns {void}
- */
- function onPasswordSubmit(enteredPassword: string) {
- if (enteredPassword && passwordNumberOfDigits && !DIGITS_ONLY.test(enteredPassword)) {
- // Don't set the password.
- return;
- }
- setPassword(conference, conference.lock, enteredPassword);
- }
-
- /**
- * Toggles whether or not the password should currently be shown as being
- * edited locally.
- *
- * @private
- * @returns {void}
- */
- function onTogglePasswordEditState() {
- if (typeof APP === 'undefined' || !buttonsWithNotifyClick?.length) {
- setPasswordEditEnabled(!passwordEditEnabled);
-
- return;
- }
-
- let notifyMode;
- const notify = buttonsWithNotifyClick.find(
- (btn: string | INotifyClick) =>
- (typeof btn === 'string' && btn === KEY)
- || (typeof btn === 'object' && btn.key === KEY)
- );
-
- if (notify) {
- notifyMode = typeof notify === 'string' || notify.preventExecution
- ? NOTIFY_CLICK_MODE.PREVENT_AND_NOTIFY
- : NOTIFY_CLICK_MODE.ONLY_NOTIFY;
- APP.API.notifyToolbarButtonClicked(
- KEY, notifyMode === NOTIFY_CLICK_MODE.PREVENT_AND_NOTIFY
- );
- }
-
- if (notifyMode === NOTIFY_CLICK_MODE.ONLY_NOTIFY) {
- setPasswordEditEnabled(!passwordEditEnabled);
- }
- }
-
- /**
- * Method to remotely submit the password from outside of the password form.
- *
- * @returns {void}
- */
- function onPasswordSave() {
- if (formRef.current) {
- // @ts-ignore
- const { value } = formRef.current.querySelector('div > input');
-
- if (value) {
- onPasswordSubmit(value);
- }
- }
- }
-
- /**
- * Callback invoked to unlock the current JitsiConference.
- *
- * @returns {void}
- */
- function onPasswordRemove() {
- onPasswordSubmit('');
- }
-
- /**
- * Copies the password to the clipboard.
- *
- * @returns {void}
- */
- function onPasswordCopy() {
- copyText(password);
- }
-
- /**
- * Toggles whether or not the password should currently be shown as being
- * edited locally.
- *
- * @param {Object} e - The key event to handle.
- *
- * @private
- * @returns {void}
- */
- function onTogglePasswordEditStateKeyPressHandler(e: React.KeyboardEvent) {
- if (e.key === ' ' || e.key === 'Enter') {
- e.preventDefault();
- onTogglePasswordEditState();
- }
- }
-
- /**
- * Method to remotely submit the password from outside of the password form.
- *
- * @param {Object} e - The key event to handle.
- *
- * @private
- * @returns {void}
- */
- function onPasswordSaveKeyPressHandler(e: React.KeyboardEvent) {
- if (e.key === ' ' || e.key === 'Enter') {
- e.preventDefault();
- onPasswordSave();
- }
- }
-
- /**
- * Callback invoked to unlock the current JitsiConference.
- *
- * @param {Object} e - The key event to handle.
- *
- * @private
- * @returns {void}
- */
- function onPasswordRemoveKeyPressHandler(e: React.KeyboardEvent) {
- if (e.key === ' ' || e.key === 'Enter') {
- e.preventDefault();
- onPasswordRemove();
- }
- }
-
- /**
- * Copies the password to the clipboard.
- *
- * @param {Object} e - The key event to handle.
- *
- * @private
- * @returns {void}
- */
- function onPasswordCopyKeyPressHandler(e: React.KeyboardEvent) {
- if (e.key === ' ' || e.key === 'Enter') {
- e.preventDefault();
- onPasswordCopy();
- }
- }
-
- /**
- * Callback invoked to show the current password.
- *
- * @returns {void}
- */
- function onPasswordShow() {
- setPasswordVisible(true);
- }
-
- /**
- * Callback invoked to show the current password.
- *
- * @param {Object} e - The key event to handle.
- *
- * @returns {void}
- */
- function onPasswordShowKeyPressHandler(e: React.KeyboardEvent) {
- if (e.key === ' ' || e.key === 'Enter') {
- e.preventDefault();
- setPasswordVisible(true);
- }
- }
-
- /**
- * Callback invoked to hide the current password.
- *
- * @returns {void}
- */
- function onPasswordHide() {
- setPasswordVisible(false);
- }
-
- /**
- * Callback invoked to hide the current password.
- *
- * @param {Object} e - The key event to handle.
- *
- * @returns {void}
- */
- function onPasswordHideKeyPressHandler(e: React.KeyboardEvent) {
- if (e.key === ' ' || e.key === 'Enter') {
- e.preventDefault();
- setPasswordVisible(false);
- }
- }
-
- /**
- * Method that renders the password action(s) based on the current
- * locked-status of the conference.
- *
- * @returns {React$Element<any>}
- */
- function renderPasswordActions() {
- if (!canEditPassword) {
- return null;
- }
-
- if (passwordEditEnabled) {
- return (
- <>
- <a
- aria-label = { t('dialog.Cancel') }
- onClick = { onTogglePasswordEditState }
- onKeyPress = { onTogglePasswordEditStateKeyPressHandler }
- role = 'button'
- tabIndex = { 0 }>{ t('dialog.Cancel') }</a>
- <a
- aria-label = { t('dialog.add') }
- onClick = { onPasswordSave }
- onKeyPress = { onPasswordSaveKeyPressHandler }
- role = 'button'
- tabIndex = { 0 }>{ t('dialog.add') }</a>
- </>
- );
- }
-
- if (locked) {
- return (
- <>
- <a
- aria-label = { t('dialog.Remove') }
- className = 'remove-password'
- onClick = { onPasswordRemove }
- onKeyPress = { onPasswordRemoveKeyPressHandler }
- role = 'button'
- tabIndex = { 0 }>{ t('dialog.Remove') }</a>
- {
-
- // There are cases like lobby and grant moderator when password is not available
- password ? <>
- <a
- aria-label = { t('dialog.copy') }
- className = 'copy-password'
- onClick = { onPasswordCopy }
- onKeyPress = { onPasswordCopyKeyPressHandler }
- role = 'button'
- tabIndex = { 0 }>{ t('dialog.copy') }</a>
- </> : null
- }
- {locked === LOCKED_LOCALLY && (
- <a
- aria-label = { t(passwordVisible ? 'dialog.hide' : 'dialog.show') }
- onClick = { passwordVisible ? onPasswordHide : onPasswordShow }
- onKeyPress = { passwordVisible
- ? onPasswordHideKeyPressHandler
- : onPasswordShowKeyPressHandler
- }
- role = 'button'
- tabIndex = { 0 }>{t(passwordVisible ? 'dialog.hide' : 'dialog.show')}</a>
- )}
- </>
- );
- }
-
- return (
- <a
- aria-label = { t('info.addPassword') }
- className = 'add-password'
- onClick = { onTogglePasswordEditState }
- onKeyPress = { onTogglePasswordEditStateKeyPressHandler }
- role = 'button'
- tabIndex = { 0 }>{ t('info.addPassword') }</a>
- );
- }
-
- return (
- <div className = 'security-dialog password-section'>
- <p className = 'description'>
- { t(canEditPassword ? 'security.about' : 'security.aboutReadOnly') }
- </p>
- <div className = 'security-dialog password'>
- <div
- className = 'info-dialog info-dialog-column info-dialog-password'
- ref = { formRef }>
- <PasswordForm
- editEnabled = { passwordEditEnabled }
- locked = { locked }
- onSubmit = { onPasswordSubmit }
- password = { password }
- passwordNumberOfDigits = { passwordNumberOfDigits }
- visible = { passwordVisible } />
- </div>
- <div className = 'security-dialog password-actions'>
- { renderPasswordActions() }
- </div>
- </div>
- </div>
- );
- }
-
- export default translate(PasswordSection);
|