Prevent deletion of active call links and style join button
This commit is contained in:
parent
5835e9033d
commit
dec06209e7
8 changed files with 198 additions and 65 deletions
|
@ -7357,6 +7357,10 @@
|
|||
"messageformat": "Approve all members",
|
||||
"description": "Call History > Call Link Details > Approve All Members > Label"
|
||||
},
|
||||
"icu:CallLinkDetails__SettingTooltip--disabled-for-active-call": {
|
||||
"messageformat": "This setting can't be changed while the call is active",
|
||||
"description": "Call History > Call Link Details > Approve All Members > Tooltip when disabled due to active call"
|
||||
},
|
||||
"icu:CallLinkDetails__CopyLink": {
|
||||
"messageformat": "Copy link",
|
||||
"description": "Call History > Call Link Details > Copy Link Button"
|
||||
|
@ -7385,6 +7389,10 @@
|
|||
"messageformat": "Delete",
|
||||
"description": "Call History > Call Link Details > Delete Link Modal > Delete Button"
|
||||
},
|
||||
"icu:CallLinkDetails__DeleteLinkTooltip--disabled-for-active-call": {
|
||||
"messageformat": "This link can't be deleted while the call is active",
|
||||
"description": "Call History > Call Link Details > Delete Link Button > Tooltip when disabled due to active call"
|
||||
},
|
||||
"icu:CallLinkEditModal__Title": {
|
||||
"messageformat": "Call link details",
|
||||
"description": "Call Link Edit Modal > Title"
|
||||
|
|
|
@ -974,3 +974,50 @@ $rtl-icon-map: (
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin button-active-call {
|
||||
$background: $color-accent-green;
|
||||
|
||||
@include font-body-2-bold;
|
||||
@include rounded-corners;
|
||||
|
||||
display: flex;
|
||||
width: auto;
|
||||
align-items: center;
|
||||
background-color: $background;
|
||||
color: $color-white;
|
||||
outline: none;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
user-select: none;
|
||||
|
||||
&:before {
|
||||
$icon-size: 16px;
|
||||
|
||||
@include color-svg(
|
||||
'../images/icons/v3/video/video-compact-fill.svg',
|
||||
$color-white
|
||||
);
|
||||
content: '';
|
||||
display: block;
|
||||
height: $icon-size;
|
||||
margin-inline-end: 4px;
|
||||
min-width: $icon-size;
|
||||
width: $icon-size;
|
||||
}
|
||||
|
||||
&:not(:disabled) {
|
||||
&:hover {
|
||||
@include any-theme {
|
||||
background-color: darken($background, 16%);
|
||||
}
|
||||
}
|
||||
|
||||
&:focus {
|
||||
@include keyboard-mode {
|
||||
background-color: darken($background, 16%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,6 +46,10 @@
|
|||
font-weight: 600;
|
||||
}
|
||||
|
||||
.CallLinkDetails__HeaderButton--active-call {
|
||||
@include button-active-call;
|
||||
}
|
||||
|
||||
.CallLinkDetails__DeleteLink {
|
||||
// Override the default icon color
|
||||
.ConversationDetails-icon__icon--trash::after {
|
||||
|
@ -59,3 +63,19 @@
|
|||
color: $color-accent-red;
|
||||
}
|
||||
}
|
||||
|
||||
.CallLinkDetails__DeleteLink--disabled-for-active-call {
|
||||
.ConversationDetails-icon__icon--trash::after {
|
||||
@include any-theme {
|
||||
background-color: $color-gray-45;
|
||||
}
|
||||
}
|
||||
.ConversationDetails-panel-row__label {
|
||||
color: $color-gray-45;
|
||||
}
|
||||
}
|
||||
|
||||
.CallLinkDetails__ApproveAllMembersDisabledTooltip,
|
||||
.CallLinkDetails__DeleteLinkTooltip {
|
||||
@include tooltip;
|
||||
}
|
||||
|
|
|
@ -356,53 +356,10 @@
|
|||
}
|
||||
|
||||
.CallsNewCall__ItemActionButton--join-call {
|
||||
$background: $color-accent-green;
|
||||
|
||||
@include font-body-2-bold;
|
||||
@include rounded-corners;
|
||||
|
||||
display: flex;
|
||||
width: auto;
|
||||
@include button-active-call;
|
||||
height: 26px;
|
||||
padding-block: 4px;
|
||||
padding-inline: 10px;
|
||||
align-items: center;
|
||||
background-color: $background;
|
||||
color: $color-white;
|
||||
outline: none;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
user-select: none;
|
||||
|
||||
&:before {
|
||||
$icon-size: 16px;
|
||||
|
||||
@include color-svg(
|
||||
'../images/icons/v3/video/video-compact-fill.svg',
|
||||
$color-white
|
||||
);
|
||||
content: '';
|
||||
display: block;
|
||||
height: $icon-size;
|
||||
margin-inline-end: 4px;
|
||||
min-width: $icon-size;
|
||||
width: $icon-size;
|
||||
}
|
||||
|
||||
&:not(:disabled) {
|
||||
&:hover {
|
||||
@include any-theme {
|
||||
background-color: darken($background, 16%);
|
||||
}
|
||||
}
|
||||
|
||||
&:focus {
|
||||
@include keyboard-mode {
|
||||
background-color: darken($background, 16%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.CallsNewCall__ItemActionButton--join-call-disabled {
|
||||
|
|
|
@ -23,7 +23,9 @@ export default {
|
|||
i18n,
|
||||
callHistoryGroup: getFakeCallLinkHistoryGroup(),
|
||||
callLink: FAKE_CALL_LINK_WITH_ADMIN_KEY,
|
||||
hasActiveCall: false,
|
||||
isAnybodyInCall: false,
|
||||
isInCall: false,
|
||||
isInAnotherCall: false,
|
||||
onDeleteCallLink: action('onDeleteCallLink'),
|
||||
onOpenCallLinkAddNameModal: action('onOpenCallLinkAddNameModal'),
|
||||
onStartCallLinkLobby: action('onStartCallLinkLobby'),
|
||||
|
@ -36,10 +38,39 @@ export function Admin(args: CallLinkDetailsProps): JSX.Element {
|
|||
return <CallLinkDetails {...args} />;
|
||||
}
|
||||
|
||||
export function AdminAndCallActive(args: CallLinkDetailsProps): JSX.Element {
|
||||
return <CallLinkDetails {...args} isAnybodyInCall />;
|
||||
}
|
||||
|
||||
export function AdminAndInCall(args: CallLinkDetailsProps): JSX.Element {
|
||||
return <CallLinkDetails {...args} isAnybodyInCall isInCall />;
|
||||
}
|
||||
|
||||
export function NonAdmin(args: CallLinkDetailsProps): JSX.Element {
|
||||
return <CallLinkDetails {...args} callLink={FAKE_CALL_LINK} />;
|
||||
}
|
||||
|
||||
export function InAnotherCall(args: CallLinkDetailsProps): JSX.Element {
|
||||
return <CallLinkDetails {...args} callLink={FAKE_CALL_LINK} hasActiveCall />;
|
||||
export function NonAdminAndCallActive(args: CallLinkDetailsProps): JSX.Element {
|
||||
return (
|
||||
<CallLinkDetails {...args} callLink={FAKE_CALL_LINK} isAnybodyInCall />
|
||||
);
|
||||
}
|
||||
|
||||
export function InAnotherCall(args: CallLinkDetailsProps): JSX.Element {
|
||||
return (
|
||||
<CallLinkDetails {...args} callLink={FAKE_CALL_LINK} isInAnotherCall />
|
||||
);
|
||||
}
|
||||
|
||||
export function InAnotherCallAndCallActive(
|
||||
args: CallLinkDetailsProps
|
||||
): JSX.Element {
|
||||
return (
|
||||
<CallLinkDetails
|
||||
{...args}
|
||||
callLink={FAKE_CALL_LINK}
|
||||
isAnybodyInCall
|
||||
isInAnotherCall
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright 2024 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
import React, { useState } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import type { CallHistoryGroup } from '../types/CallDisposition';
|
||||
import type { LocalizerType } from '../types/I18N';
|
||||
import { CallHistoryGroupPanelSection } from './conversation/conversation-details/CallHistoryGroupPanelSection';
|
||||
|
@ -21,6 +22,8 @@ import { isCallLinkAdmin } from '../types/CallLink';
|
|||
import { CallLinkRestrictionsSelect } from './CallLinkRestrictionsSelect';
|
||||
import { ConfirmationDialog } from './ConfirmationDialog';
|
||||
import { InAnotherCallTooltip } from './conversation/InAnotherCallTooltip';
|
||||
import { offsetDistanceModifier } from '../util/popperUtil';
|
||||
import { Tooltip, TooltipPlacement } from './Tooltip';
|
||||
|
||||
function toUrlWithoutProtocol(url: URL): string {
|
||||
return `${url.hostname}${url.pathname}${url.search}${url.hash}`;
|
||||
|
@ -29,7 +32,9 @@ function toUrlWithoutProtocol(url: URL): string {
|
|||
export type CallLinkDetailsProps = Readonly<{
|
||||
callHistoryGroup: CallHistoryGroup;
|
||||
callLink: CallLinkType;
|
||||
hasActiveCall: boolean;
|
||||
isAnybodyInCall: boolean;
|
||||
isInCall: boolean;
|
||||
isInAnotherCall: boolean;
|
||||
i18n: LocalizerType;
|
||||
onDeleteCallLink: () => void;
|
||||
onOpenCallLinkAddNameModal: () => void;
|
||||
|
@ -42,7 +47,9 @@ export function CallLinkDetails({
|
|||
callHistoryGroup,
|
||||
callLink,
|
||||
i18n,
|
||||
hasActiveCall,
|
||||
isAnybodyInCall,
|
||||
isInCall,
|
||||
isInAnotherCall,
|
||||
onDeleteCallLink,
|
||||
onOpenCallLinkAddNameModal,
|
||||
onStartCallLinkLobby,
|
||||
|
@ -56,15 +63,32 @@ export function CallLinkDetails({
|
|||
});
|
||||
const joinButton = (
|
||||
<Button
|
||||
className="CallLinkDetails__HeaderButton"
|
||||
variant={ButtonVariant.SecondaryAffirmative}
|
||||
discouraged={hasActiveCall}
|
||||
className={classNames({
|
||||
CallLinkDetails__HeaderButton: true,
|
||||
'CallLinkDetails__HeaderButton--active-call': isAnybodyInCall,
|
||||
})}
|
||||
variant={
|
||||
isAnybodyInCall
|
||||
? ButtonVariant.Calling
|
||||
: ButtonVariant.SecondaryAffirmative
|
||||
}
|
||||
discouraged={isInAnotherCall}
|
||||
size={ButtonSize.Small}
|
||||
onClick={onStartCallLinkLobby}
|
||||
>
|
||||
{i18n('icu:CallLinkDetails__Join')}
|
||||
{isInCall
|
||||
? i18n('icu:CallsNewCallButton--return')
|
||||
: i18n('icu:CallLinkDetails__Join')}
|
||||
</Button>
|
||||
);
|
||||
const callLinkRestrictionsSelect = (
|
||||
<CallLinkRestrictionsSelect
|
||||
disabled={isAnybodyInCall}
|
||||
i18n={i18n}
|
||||
value={callLink.restrictions}
|
||||
onChange={onUpdateCallLinkRestrictions}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="CallLinkDetails__Container">
|
||||
|
@ -92,7 +116,7 @@ export function CallLinkDetails({
|
|||
</p>
|
||||
</div>
|
||||
<div className="CallLinkDetails__HeaderActions">
|
||||
{hasActiveCall ? (
|
||||
{isInAnotherCall ? (
|
||||
<InAnotherCallTooltip i18n={i18n}>
|
||||
{joinButton}
|
||||
</InAnotherCallTooltip>
|
||||
|
@ -130,11 +154,20 @@ export function CallLinkDetails({
|
|||
}
|
||||
label={i18n('icu:CallLinkDetails__ApproveAllMembersLabel')}
|
||||
right={
|
||||
<CallLinkRestrictionsSelect
|
||||
i18n={i18n}
|
||||
value={callLink.restrictions}
|
||||
onChange={onUpdateCallLinkRestrictions}
|
||||
/>
|
||||
isAnybodyInCall ? (
|
||||
<Tooltip
|
||||
className="CallLinkDetails__ApproveAllMembersDisabledTooltip"
|
||||
content={i18n(
|
||||
'icu:CallLinkDetails__SettingTooltip--disabled-for-active-call'
|
||||
)}
|
||||
direction={TooltipPlacement.Top}
|
||||
popperModifiers={[offsetDistanceModifier(5)]}
|
||||
>
|
||||
{callLinkRestrictionsSelect}
|
||||
</Tooltip>
|
||||
) : (
|
||||
callLinkRestrictionsSelect
|
||||
)
|
||||
}
|
||||
/>
|
||||
</PanelSection>
|
||||
|
@ -166,14 +199,34 @@ export function CallLinkDetails({
|
|||
{isCallLinkAdmin(callLink) && (
|
||||
<PanelSection>
|
||||
<PanelRow
|
||||
className="CallLinkDetails__DeleteLink"
|
||||
className={classNames({
|
||||
CallLinkDetails__DeleteLink: true,
|
||||
'CallLinkDetails__DeleteLink--disabled-for-active-call':
|
||||
isAnybodyInCall,
|
||||
})}
|
||||
disabled={isAnybodyInCall}
|
||||
icon={
|
||||
<ConversationDetailsIcon
|
||||
ariaLabel={i18n('icu:CallLinkDetails__DeleteLink')}
|
||||
icon={IconType.trash}
|
||||
/>
|
||||
}
|
||||
label={i18n('icu:CallLinkDetails__DeleteLink')}
|
||||
label={
|
||||
isAnybodyInCall ? (
|
||||
<Tooltip
|
||||
className="CallLinkDetails__DeleteLinkTooltip"
|
||||
content={i18n(
|
||||
'icu:CallLinkDetails__DeleteLinkTooltip--disabled-for-active-call'
|
||||
)}
|
||||
direction={TooltipPlacement.Top}
|
||||
popperModifiers={[offsetDistanceModifier(5)]}
|
||||
>
|
||||
{i18n('icu:CallLinkDetails__DeleteLink')}
|
||||
</Tooltip>
|
||||
) : (
|
||||
i18n('icu:CallLinkDetails__DeleteLink')
|
||||
)
|
||||
}
|
||||
onClick={() => {
|
||||
setIsDeleteCallLinkModalOpen(true);
|
||||
}}
|
||||
|
|
|
@ -9,6 +9,7 @@ import type { LocalizerType } from '../types/I18N';
|
|||
import { Select } from './Select';
|
||||
|
||||
export type CallLinkRestrictionsSelectProps = Readonly<{
|
||||
disabled?: boolean;
|
||||
i18n: LocalizerType;
|
||||
id?: string;
|
||||
value: CallLinkRestrictions;
|
||||
|
@ -16,6 +17,7 @@ export type CallLinkRestrictionsSelectProps = Readonly<{
|
|||
}>;
|
||||
|
||||
export function CallLinkRestrictionsSelect({
|
||||
disabled,
|
||||
i18n,
|
||||
id,
|
||||
value,
|
||||
|
@ -23,6 +25,7 @@ export function CallLinkRestrictionsSelect({
|
|||
}: CallLinkRestrictionsSelectProps): JSX.Element {
|
||||
return (
|
||||
<Select
|
||||
disabled={disabled}
|
||||
id={id}
|
||||
value={String(value)}
|
||||
moduleClassName="CallLinkRestrictionsSelect"
|
||||
|
|
|
@ -5,12 +5,17 @@ import { useSelector } from 'react-redux';
|
|||
import type { CallHistoryGroup } from '../../types/CallDisposition';
|
||||
import { getIntl } from '../selectors/user';
|
||||
import { CallLinkDetails } from '../../components/CallLinkDetails';
|
||||
import { getActiveCallState, getCallLinkSelector } from '../selectors/calling';
|
||||
import {
|
||||
getActiveCallState,
|
||||
getAdhocCallSelector,
|
||||
getCallLinkSelector,
|
||||
} from '../selectors/calling';
|
||||
import { useGlobalModalActions } from '../ducks/globalModals';
|
||||
import { useCallingActions } from '../ducks/calling';
|
||||
import * as log from '../../logging/log';
|
||||
import { strictAssert } from '../../util/assert';
|
||||
import type { CallLinkRestrictions } from '../../types/CallLink';
|
||||
import { isAnybodyInGroupCall } from '../ducks/callingHelpers';
|
||||
|
||||
export type SmartCallLinkDetailsProps = Readonly<{
|
||||
roomId: string;
|
||||
|
@ -60,9 +65,16 @@ export const SmartCallLinkDetails = memo(function SmartCallLinkDetails({
|
|||
[roomId, updateCallLinkRestrictions]
|
||||
);
|
||||
|
||||
const adhocCallSelector = useSelector(getAdhocCallSelector);
|
||||
const adhocCall = adhocCallSelector(roomId);
|
||||
const hasActiveCall = isAnybodyInGroupCall(adhocCall?.peekInfo);
|
||||
|
||||
const activeCall = useSelector(getActiveCallState);
|
||||
const hasActiveCall = Boolean(
|
||||
activeCall && callLink && activeCall?.conversationId !== callLink?.roomId
|
||||
const isInAnotherCall = Boolean(
|
||||
activeCall && callLink && activeCall.conversationId !== callLink.roomId
|
||||
);
|
||||
const isInCall = Boolean(
|
||||
activeCall && callLink && activeCall.conversationId === callLink.roomId
|
||||
);
|
||||
|
||||
if (callLink == null) {
|
||||
|
@ -74,7 +86,9 @@ export const SmartCallLinkDetails = memo(function SmartCallLinkDetails({
|
|||
<CallLinkDetails
|
||||
callHistoryGroup={callHistoryGroup}
|
||||
callLink={callLink}
|
||||
hasActiveCall={hasActiveCall}
|
||||
isAnybodyInCall={hasActiveCall}
|
||||
isInCall={isInCall}
|
||||
isInAnotherCall={isInAnotherCall}
|
||||
i18n={i18n}
|
||||
onDeleteCallLink={handleDeleteCallLink}
|
||||
onOpenCallLinkAddNameModal={handleOpenCallLinkAddNameModal}
|
||||
|
|
Loading…
Reference in a new issue