214 lines
6.6 KiB
TypeScript
214 lines
6.6 KiB
TypeScript
// Copyright 2024 Signal Messenger, LLC
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
import React, { useCallback, useMemo, useState } from 'react';
|
|
import { v4 as generateUuid } from 'uuid';
|
|
import { Modal } from './Modal';
|
|
import type { LocalizerType } from '../types/I18N';
|
|
import {
|
|
CallLinkRestrictions,
|
|
toCallLinkRestrictions,
|
|
type CallLinkType,
|
|
} from '../types/CallLink';
|
|
import { Input } from './Input';
|
|
import { Select } from './Select';
|
|
import { linkCallRoute } from '../util/signalRoutes';
|
|
import { Button, ButtonSize, ButtonVariant } from './Button';
|
|
import { Avatar, AvatarSize } from './Avatar';
|
|
import { formatUrlWithoutProtocol } from '../util/url';
|
|
|
|
export type CallLinkEditModalProps = {
|
|
i18n: LocalizerType;
|
|
callLink: CallLinkType;
|
|
onClose: () => void;
|
|
onCopyCallLink: () => void;
|
|
onUpdateCallLinkName: (name: string) => void;
|
|
onUpdateCallLinkRestrictions: (restrictions: CallLinkRestrictions) => void;
|
|
onShareCallLinkViaSignal: () => void;
|
|
onStartCallLinkLobby: () => void;
|
|
};
|
|
|
|
export function CallLinkEditModal({
|
|
i18n,
|
|
callLink,
|
|
onClose,
|
|
onCopyCallLink,
|
|
onUpdateCallLinkName,
|
|
onUpdateCallLinkRestrictions,
|
|
onShareCallLinkViaSignal,
|
|
onStartCallLinkLobby,
|
|
}: CallLinkEditModalProps): JSX.Element {
|
|
const { name: savedName, restrictions: savedRestrictions } = callLink;
|
|
|
|
const [nameId] = useState(() => generateUuid());
|
|
const [restrictionsId] = useState(() => generateUuid());
|
|
|
|
const [nameInput, setNameInput] = useState(savedName);
|
|
const [restrictionsInput, setRestrictionsInput] = useState(savedRestrictions);
|
|
|
|
// We only want to use the default name "Signal Call" as a value if the user
|
|
// modified the input and then chose that name. Doesn't revert when saved.
|
|
const [nameTouched, setNameTouched] = useState(false);
|
|
|
|
const callLinkWebUrl = useMemo(() => {
|
|
return formatUrlWithoutProtocol(
|
|
linkCallRoute.toWebUrl({ key: callLink.rootKey })
|
|
);
|
|
}, [callLink.rootKey]);
|
|
|
|
const onSaveName = useCallback(
|
|
(newName: string) => {
|
|
if (!nameTouched) {
|
|
return;
|
|
}
|
|
if (newName === savedName) {
|
|
return;
|
|
}
|
|
onUpdateCallLinkName(newName);
|
|
},
|
|
[nameTouched, savedName, onUpdateCallLinkName]
|
|
);
|
|
|
|
const onSaveRestrictions = useCallback(
|
|
(newRestrictions: CallLinkRestrictions) => {
|
|
if (newRestrictions === savedRestrictions) {
|
|
return;
|
|
}
|
|
onUpdateCallLinkRestrictions(newRestrictions);
|
|
},
|
|
[savedRestrictions, onUpdateCallLinkRestrictions]
|
|
);
|
|
|
|
return (
|
|
<Modal
|
|
i18n={i18n}
|
|
modalName="CallLinkEditModal"
|
|
moduleClassName="CallLinkEditModal"
|
|
title={i18n('icu:CallLinkEditModal__Title')}
|
|
hasXButton
|
|
onClose={() => {
|
|
// Save the modal in case the user hits escape
|
|
onSaveName(nameInput);
|
|
onClose();
|
|
}}
|
|
>
|
|
<div className="CallLinkEditModal__Header">
|
|
<Avatar
|
|
i18n={i18n}
|
|
badge={undefined}
|
|
conversationType="callLink"
|
|
size={AvatarSize.SIXTY_FOUR}
|
|
acceptedMessageRequest
|
|
isMe={false}
|
|
sharedGroupNames={[]}
|
|
title={callLink.name ?? i18n('icu:calling__call-link-default-title')}
|
|
/>
|
|
<div className="CallLinkEditModal__Header__Details">
|
|
<label htmlFor={nameId} className="CallLinkEditModal__SrOnly">
|
|
{i18n('icu:CallLinkEditModal__InputLabel--Name--SrOnly')}
|
|
</label>
|
|
<Input
|
|
moduleClassName="CallLinkEditModal__Input--Name"
|
|
i18n={i18n}
|
|
value={
|
|
nameInput === '' && !nameTouched
|
|
? i18n('icu:calling__call-link-default-title')
|
|
: nameInput
|
|
}
|
|
maxByteCount={120}
|
|
onChange={value => {
|
|
setNameTouched(true);
|
|
setNameInput(value);
|
|
}}
|
|
onBlur={() => {
|
|
onSaveName(nameInput);
|
|
}}
|
|
onEnter={() => {
|
|
onSaveName(nameInput);
|
|
}}
|
|
placeholder={i18n('icu:calling__call-link-default-title')}
|
|
/>
|
|
|
|
<div className="CallLinkEditModal__CallLinkAndJoinButton">
|
|
<button
|
|
className="CallLinkEditModal__CopyUrlTextButton"
|
|
type="button"
|
|
onClick={onCopyCallLink}
|
|
aria-label={i18n('icu:CallLinkDetails__CopyLink')}
|
|
>
|
|
{callLinkWebUrl}
|
|
</button>
|
|
<Button
|
|
onClick={onStartCallLinkLobby}
|
|
size={ButtonSize.Small}
|
|
variant={ButtonVariant.SecondaryAffirmative}
|
|
className="CallLinkEditModal__JoinButton"
|
|
>
|
|
{i18n('icu:CallLinkEditModal__JoinButtonLabel')}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
className="CallLinkEditModal__ApproveAllMembers__Row"
|
|
// For testing, to easily check the restrictions saved
|
|
data-restrictions={savedRestrictions}
|
|
>
|
|
<label
|
|
htmlFor={restrictionsId}
|
|
className="CallLinkEditModal__ApproveAllMembers__Label"
|
|
>
|
|
{i18n('icu:CallLinkEditModal__InputLabel--ApproveAllMembers')}
|
|
</label>
|
|
<Select
|
|
id={restrictionsId}
|
|
value={restrictionsInput}
|
|
options={[
|
|
{
|
|
value: CallLinkRestrictions.None,
|
|
text: i18n(
|
|
'icu:CallLinkEditModal__ApproveAllMembers__Option--Off'
|
|
),
|
|
},
|
|
{
|
|
value: CallLinkRestrictions.AdminApproval,
|
|
text: i18n(
|
|
'icu:CallLinkEditModal__ApproveAllMembers__Option--On'
|
|
),
|
|
},
|
|
]}
|
|
onChange={value => {
|
|
const newRestrictions = toCallLinkRestrictions(value);
|
|
setRestrictionsInput(newRestrictions);
|
|
onSaveRestrictions(newRestrictions);
|
|
}}
|
|
/>
|
|
</div>
|
|
|
|
<button
|
|
type="button"
|
|
className="CallLinkEditModal__ActionButton"
|
|
onClick={onCopyCallLink}
|
|
>
|
|
<i
|
|
role="presentation"
|
|
className="CallLinkEditModal__ActionButton__Icon CallLinkEditModal__ActionButton__Icon--Copy"
|
|
/>
|
|
{i18n('icu:CallLinkDetails__CopyLink')}
|
|
</button>
|
|
|
|
<button
|
|
type="button"
|
|
className="CallLinkEditModal__ActionButton"
|
|
onClick={onShareCallLinkViaSignal}
|
|
>
|
|
<i
|
|
role="presentation"
|
|
className="CallLinkEditModal__ActionButton__Icon CallLinkEditModal__ActionButton__Icon--Share"
|
|
/>
|
|
{i18n('icu:CallLinkDetails__ShareLinkViaSignal')}
|
|
</button>
|
|
</Modal>
|
|
);
|
|
}
|