Support delete for call links
Co-authored-by: Fedor Indutny <79877362+indutny-signal@users.noreply.github.com>
This commit is contained in:
parent
11fed7e7f8
commit
9a9f9495f1
67 changed files with 853 additions and 345 deletions
40
ts/components/CallLinkDetails.stories.tsx
Normal file
40
ts/components/CallLinkDetails.stories.tsx
Normal file
|
@ -0,0 +1,40 @@
|
|||
// Copyright 2024 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
import React from 'react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import type { ComponentMeta } from '../storybook/types';
|
||||
import type { CallLinkDetailsProps } from './CallLinkDetails';
|
||||
import { CallLinkDetails } from './CallLinkDetails';
|
||||
import { setupI18n } from '../util/setupI18n';
|
||||
import enMessages from '../../_locales/en/messages.json';
|
||||
import {
|
||||
FAKE_CALL_LINK,
|
||||
FAKE_CALL_LINK_WITH_ADMIN_KEY,
|
||||
} from '../test-both/helpers/fakeCallLink';
|
||||
import { getFakeCallLinkHistoryGroup } from '../test-both/helpers/getFakeCallHistoryGroup';
|
||||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
export default {
|
||||
title: 'Components/CallLinkDetails',
|
||||
component: CallLinkDetails,
|
||||
argTypes: {},
|
||||
args: {
|
||||
i18n,
|
||||
callHistoryGroup: getFakeCallLinkHistoryGroup(),
|
||||
callLink: FAKE_CALL_LINK_WITH_ADMIN_KEY,
|
||||
onDeleteCallLink: action('onDeleteCallLink'),
|
||||
onOpenCallLinkAddNameModal: action('onOpenCallLinkAddNameModal'),
|
||||
onStartCallLinkLobby: action('onStartCallLinkLobby'),
|
||||
onShareCallLinkViaSignal: action('onShareCallLinkViaSignal'),
|
||||
onUpdateCallLinkRestrictions: action('onUpdateCallLinkRestrictions'),
|
||||
},
|
||||
} satisfies ComponentMeta<CallLinkDetailsProps>;
|
||||
|
||||
export function Admin(args: CallLinkDetailsProps): JSX.Element {
|
||||
return <CallLinkDetails {...args} />;
|
||||
}
|
||||
|
||||
export function NonAdmin(args: CallLinkDetailsProps): JSX.Element {
|
||||
return <CallLinkDetails {...args} callLink={FAKE_CALL_LINK} />;
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright 2024 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import type { CallHistoryGroup } from '../types/CallDisposition';
|
||||
import type { LocalizerType } from '../types/I18N';
|
||||
import { CallHistoryGroupPanelSection } from './conversation/conversation-details/CallHistoryGroupPanelSection';
|
||||
|
@ -17,8 +17,9 @@ import { Avatar, AvatarSize } from './Avatar';
|
|||
import { Button, ButtonSize, ButtonVariant } from './Button';
|
||||
import { copyCallLink } from '../util/copyLinksWithToast';
|
||||
import { getColorForCallLink } from '../util/getColorForCallLink';
|
||||
import { isCallLinkAdmin } from '../util/callLinks';
|
||||
import { isCallLinkAdmin } from '../types/CallLink';
|
||||
import { CallLinkRestrictionsSelect } from './CallLinkRestrictionsSelect';
|
||||
import { ConfirmationDialog } from './ConfirmationDialog';
|
||||
|
||||
function toUrlWithoutProtocol(url: URL): string {
|
||||
return `${url.hostname}${url.pathname}${url.search}${url.hash}`;
|
||||
|
@ -28,6 +29,7 @@ export type CallLinkDetailsProps = Readonly<{
|
|||
callHistoryGroup: CallHistoryGroup;
|
||||
callLink: CallLinkType;
|
||||
i18n: LocalizerType;
|
||||
onDeleteCallLink: () => void;
|
||||
onOpenCallLinkAddNameModal: () => void;
|
||||
onStartCallLinkLobby: () => void;
|
||||
onShareCallLinkViaSignal: () => void;
|
||||
|
@ -38,11 +40,14 @@ export function CallLinkDetails({
|
|||
callHistoryGroup,
|
||||
callLink,
|
||||
i18n,
|
||||
onDeleteCallLink,
|
||||
onOpenCallLinkAddNameModal,
|
||||
onStartCallLinkLobby,
|
||||
onShareCallLinkViaSignal,
|
||||
onUpdateCallLinkRestrictions,
|
||||
}: CallLinkDetailsProps): JSX.Element {
|
||||
const [isDeleteCallLinkModalOpen, setIsDeleteCallLinkModalOpen] =
|
||||
useState(false);
|
||||
const webUrl = linkCallRoute.toWebUrl({
|
||||
key: callLink.rootKey,
|
||||
});
|
||||
|
@ -144,6 +149,43 @@ export function CallLinkDetails({
|
|||
onClick={onShareCallLinkViaSignal}
|
||||
/>
|
||||
</PanelSection>
|
||||
{isCallLinkAdmin(callLink) && (
|
||||
<PanelSection>
|
||||
<PanelRow
|
||||
className="CallLinkDetails__DeleteLink"
|
||||
icon={
|
||||
<ConversationDetailsIcon
|
||||
ariaLabel={i18n('icu:CallLinkDetails__DeleteLink')}
|
||||
icon={IconType.trash}
|
||||
/>
|
||||
}
|
||||
label={i18n('icu:CallLinkDetails__DeleteLink')}
|
||||
onClick={() => {
|
||||
setIsDeleteCallLinkModalOpen(true);
|
||||
}}
|
||||
/>
|
||||
</PanelSection>
|
||||
)}
|
||||
{isDeleteCallLinkModalOpen && (
|
||||
<ConfirmationDialog
|
||||
i18n={i18n}
|
||||
dialogName="CallLinkDetails__DeleteLinkModal"
|
||||
title={i18n('icu:CallLinkDetails__DeleteLinkModal__Title')}
|
||||
cancelText={i18n('icu:CallLinkDetails__DeleteLinkModal__Cancel')}
|
||||
actions={[
|
||||
{
|
||||
text: i18n('icu:CallLinkDetails__DeleteLinkModal__Delete'),
|
||||
style: 'affirmative',
|
||||
action: onDeleteCallLink,
|
||||
},
|
||||
]}
|
||||
onClose={() => {
|
||||
setIsDeleteCallLinkModalOpen(false);
|
||||
}}
|
||||
>
|
||||
{i18n('icu:CallLinkDetails__DeleteLinkModal__Body')}
|
||||
</ConfirmationDialog>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -8,12 +8,12 @@ import type { PropsType } from './CallManager';
|
|||
import { CallManager } from './CallManager';
|
||||
import {
|
||||
CallEndedReason,
|
||||
CallMode,
|
||||
CallState,
|
||||
CallViewMode,
|
||||
GroupCallConnectionState,
|
||||
GroupCallJoinState,
|
||||
} from '../types/Calling';
|
||||
import { CallMode } from '../types/CallDisposition';
|
||||
import type {
|
||||
ActiveGroupCallType,
|
||||
GroupCallRemoteParticipantType,
|
||||
|
|
|
@ -20,11 +20,11 @@ import type {
|
|||
} from '../types/Calling';
|
||||
import {
|
||||
CallEndedReason,
|
||||
CallMode,
|
||||
CallState,
|
||||
GroupCallConnectionState,
|
||||
GroupCallJoinState,
|
||||
} from '../types/Calling';
|
||||
import { CallMode } from '../types/CallDisposition';
|
||||
import type { ConversationType } from '../state/ducks/conversations';
|
||||
import type {
|
||||
AcceptCallType,
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import { CallMode } from '../types/Calling';
|
||||
import { CallMode } from '../types/CallDisposition';
|
||||
|
||||
export type PropsType = {
|
||||
callMode: CallMode;
|
||||
|
|
|
@ -12,12 +12,12 @@ import type {
|
|||
GroupCallRemoteParticipantType,
|
||||
} from '../types/Calling';
|
||||
import {
|
||||
CallMode,
|
||||
CallViewMode,
|
||||
CallState,
|
||||
GroupCallConnectionState,
|
||||
GroupCallJoinState,
|
||||
} from '../types/Calling';
|
||||
import { CallMode } from '../types/CallDisposition';
|
||||
import { generateAci } from '../types/ServiceId';
|
||||
import type { ConversationType } from '../state/ducks/conversations';
|
||||
import { AvatarColors } from '../types/Colors';
|
||||
|
|
|
@ -33,12 +33,12 @@ import type {
|
|||
} from '../types/Calling';
|
||||
import {
|
||||
CALLING_REACTIONS_LIFETIME,
|
||||
CallMode,
|
||||
CallViewMode,
|
||||
CallState,
|
||||
GroupCallConnectionState,
|
||||
GroupCallJoinState,
|
||||
} from '../types/Calling';
|
||||
import { CallMode } from '../types/CallDisposition';
|
||||
import type { ServiceIdString } from '../types/ServiceId';
|
||||
import { AvatarColors } from '../types/Colors';
|
||||
import type { ConversationType } from '../state/ducks/conversations';
|
||||
|
|
|
@ -19,7 +19,7 @@ import {
|
|||
getDefaultConversationWithServiceId,
|
||||
} from '../test-both/helpers/getDefaultConversation';
|
||||
import { CallingToastProvider } from './CallingToast';
|
||||
import { CallMode } from '../types/Calling';
|
||||
import { CallMode } from '../types/CallDisposition';
|
||||
import { getDefaultCallLinkConversation } from '../test-both/helpers/fakeCallLink';
|
||||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
|
|
@ -19,7 +19,7 @@ import {
|
|||
CallingLobbyJoinButton,
|
||||
CallingLobbyJoinButtonVariant,
|
||||
} from './CallingLobbyJoinButton';
|
||||
import { CallMode } from '../types/Calling';
|
||||
import { CallMode } from '../types/CallDisposition';
|
||||
import type { CallingConversationType } from '../types/Calling';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import { useIsOnline } from '../hooks/useIsOnline';
|
||||
|
|
|
@ -11,12 +11,12 @@ import type { PropsType } from './CallingPip';
|
|||
import { CallingPip } from './CallingPip';
|
||||
import type { ActiveDirectCallType } from '../types/Calling';
|
||||
import {
|
||||
CallMode,
|
||||
CallViewMode,
|
||||
CallState,
|
||||
GroupCallConnectionState,
|
||||
GroupCallJoinState,
|
||||
} from '../types/Calling';
|
||||
import { CallMode } from '../types/CallDisposition';
|
||||
import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation';
|
||||
import { fakeGetGroupCallVideoFrameSource } from '../test-both/helpers/fakeGetGroupCallVideoFrameSource';
|
||||
import { setupI18n } from '../util/setupI18n';
|
||||
|
|
|
@ -14,7 +14,8 @@ import type {
|
|||
GroupCallRemoteParticipantType,
|
||||
GroupCallVideoRequest,
|
||||
} from '../types/Calling';
|
||||
import { CallMode, GroupCallJoinState } from '../types/Calling';
|
||||
import { GroupCallJoinState } from '../types/Calling';
|
||||
import { CallMode } from '../types/CallDisposition';
|
||||
import { AvatarColors } from '../types/Colors';
|
||||
import type { SetRendererCanvasType } from '../state/ducks/calling';
|
||||
import { useGetCallingFrameBuffer } from '../calling/useGetCallingFrameBuffer';
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import React, { useEffect, useMemo, useRef } from 'react';
|
||||
import type { ActiveCallType } from '../types/Calling';
|
||||
import { CallMode } from '../types/Calling';
|
||||
import { CallMode } from '../types/CallDisposition';
|
||||
import type { ConversationType } from '../state/ducks/conversations';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import { CallingToastProvider, useCallingToasts } from './CallingToast';
|
||||
|
|
|
@ -28,6 +28,7 @@ import {
|
|||
DirectCallStatus,
|
||||
GroupCallStatus,
|
||||
isSameCallHistoryGroup,
|
||||
CallMode,
|
||||
} from '../types/CallDisposition';
|
||||
import { formatDateTimeShort, isMoreRecentThan } from '../util/timestamp';
|
||||
import type { ConversationType } from '../state/ducks/conversations';
|
||||
|
@ -47,7 +48,6 @@ import { CallsNewCallButton } from './CallsNewCall';
|
|||
import { Tooltip, TooltipPlacement } from './Tooltip';
|
||||
import { Theme } from '../util/theme';
|
||||
import type { CallingConversationType } from '../types/Calling';
|
||||
import { CallMode } from '../types/Calling';
|
||||
import type { CallLinkType } from '../types/CallLink';
|
||||
import {
|
||||
callLinkToConversation,
|
||||
|
|
|
@ -62,7 +62,8 @@ type CallsTabProps = Readonly<{
|
|||
preferredLeftPaneWidth: number;
|
||||
renderCallLinkDetails: (
|
||||
roomId: string,
|
||||
callHistoryGroup: CallHistoryGroup
|
||||
callHistoryGroup: CallHistoryGroup,
|
||||
onClose: () => void
|
||||
) => JSX.Element;
|
||||
renderConversationDetails: (
|
||||
conversationId: string,
|
||||
|
@ -152,6 +153,10 @@ export function CallsTab({
|
|||
[updateSelectedView]
|
||||
);
|
||||
|
||||
const onCloseSelectedView = useCallback(() => {
|
||||
updateSelectedView(null);
|
||||
}, [updateSelectedView]);
|
||||
|
||||
useEscapeHandling(
|
||||
sidebarView === CallsTabSidebarView.NewCallView
|
||||
? () => {
|
||||
|
@ -328,7 +333,8 @@ export function CallsTab({
|
|||
{selectedView.type === 'callLink' &&
|
||||
renderCallLinkDetails(
|
||||
selectedView.roomId,
|
||||
selectedView.callHistoryGroup
|
||||
selectedView.callHistoryGroup,
|
||||
onCloseSelectedView
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
|
|
@ -6,7 +6,7 @@ import { action } from '@storybook/addon-actions';
|
|||
import type { Meta } from '@storybook/react';
|
||||
import type { PropsType } from './IncomingCallBar';
|
||||
import { IncomingCallBar } from './IncomingCallBar';
|
||||
import { CallMode } from '../types/Calling';
|
||||
import { CallMode } from '../types/CallDisposition';
|
||||
import { setupI18n } from '../util/setupI18n';
|
||||
import enMessages from '../../_locales/en/messages.json';
|
||||
import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation';
|
||||
|
|
|
@ -11,7 +11,7 @@ import { getParticipantName } from '../util/callingGetParticipantName';
|
|||
import { ContactName } from './conversation/ContactName';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import { AvatarColors } from '../types/Colors';
|
||||
import { CallMode } from '../types/Calling';
|
||||
import { CallMode } from '../types/CallDisposition';
|
||||
import type { ConversationType } from '../state/ducks/conversations';
|
||||
import type { AcceptCallType, DeclineCallType } from '../state/ducks/calling';
|
||||
import { missingCaseError } from '../util/missingCaseError';
|
||||
|
|
|
@ -6,7 +6,13 @@ import { action } from '@storybook/addon-actions';
|
|||
import type { Meta } from '@storybook/react';
|
||||
import { setupI18n } from '../../util/setupI18n';
|
||||
import enMessages from '../../../_locales/en/messages.json';
|
||||
import { CallMode } from '../../types/Calling';
|
||||
import {
|
||||
CallMode,
|
||||
CallType,
|
||||
CallDirection,
|
||||
GroupCallStatus,
|
||||
DirectCallStatus,
|
||||
} from '../../types/CallDisposition';
|
||||
import { generateAci } from '../../types/ServiceId';
|
||||
import { CallingNotification, type PropsType } from './CallingNotification';
|
||||
import {
|
||||
|
@ -14,12 +20,6 @@ import {
|
|||
getDefaultGroup,
|
||||
} from '../../test-both/helpers/getDefaultConversation';
|
||||
import type { CallStatus } from '../../types/CallDisposition';
|
||||
import {
|
||||
CallType,
|
||||
CallDirection,
|
||||
GroupCallStatus,
|
||||
DirectCallStatus,
|
||||
} from '../../types/CallDisposition';
|
||||
import type { ConversationType } from '../../state/ducks/conversations';
|
||||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
|
|
@ -10,7 +10,13 @@ import { SystemMessage, SystemMessageKind } from './SystemMessage';
|
|||
import { Button, ButtonSize, ButtonVariant } from '../Button';
|
||||
import { MessageTimestamp } from './MessageTimestamp';
|
||||
import type { LocalizerType } from '../../types/Util';
|
||||
import { CallMode } from '../../types/Calling';
|
||||
import {
|
||||
CallMode,
|
||||
CallDirection,
|
||||
CallType,
|
||||
DirectCallStatus,
|
||||
GroupCallStatus,
|
||||
} from '../../types/CallDisposition';
|
||||
import type { CallingNotificationType } from '../../util/callingNotification';
|
||||
import {
|
||||
getCallingIcon,
|
||||
|
@ -19,12 +25,6 @@ import {
|
|||
import { missingCaseError } from '../../util/missingCaseError';
|
||||
import { Tooltip, TooltipPlacement } from '../Tooltip';
|
||||
import * as log from '../../logging/log';
|
||||
import {
|
||||
CallDirection,
|
||||
CallType,
|
||||
DirectCallStatus,
|
||||
GroupCallStatus,
|
||||
} from '../../types/CallDisposition';
|
||||
import {
|
||||
type ContextMenuTriggerType,
|
||||
MessageContextMenu,
|
||||
|
|
|
@ -11,7 +11,7 @@ import enMessages from '../../../_locales/en/messages.json';
|
|||
import type { PropsType as TimelineItemProps } from './TimelineItem';
|
||||
import { TimelineItem } from './TimelineItem';
|
||||
import { UniversalTimerNotification } from './UniversalTimerNotification';
|
||||
import { CallMode } from '../../types/Calling';
|
||||
import { CallMode } from '../../types/CallDisposition';
|
||||
import { AvatarColors } from '../../types/Colors';
|
||||
import { getDefaultConversation } from '../../test-both/helpers/getDefaultConversation';
|
||||
import { WidthBreakpoint } from '../_util';
|
||||
|
|
|
@ -19,12 +19,7 @@ import { makeFakeLookupConversationWithoutServiceId } from '../../../test-both/h
|
|||
import { ThemeType } from '../../../types/Util';
|
||||
import { DurationInSeconds } from '../../../util/durations';
|
||||
import { NavTab } from '../../../state/ducks/nav';
|
||||
import { CallMode } from '../../../types/Calling';
|
||||
import {
|
||||
CallDirection,
|
||||
CallType,
|
||||
DirectCallStatus,
|
||||
} from '../../../types/CallDisposition';
|
||||
import { getFakeCallHistoryGroup } from '../../../test-both/helpers/getFakeCallHistoryGroup';
|
||||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
|
@ -224,30 +219,15 @@ export const _11 = (): JSX.Element => (
|
|||
<ConversationDetails {...createProps()} isGroup={false} />
|
||||
);
|
||||
|
||||
function mins(n: number) {
|
||||
return DurationInSeconds.toMillis(DurationInSeconds.fromMinutes(n));
|
||||
}
|
||||
|
||||
export function WithCallHistoryGroup(): JSX.Element {
|
||||
const props = createProps();
|
||||
|
||||
return (
|
||||
<ConversationDetails
|
||||
{...props}
|
||||
callHistoryGroup={{
|
||||
peerId: props.conversation?.serviceId ?? '',
|
||||
mode: CallMode.Direct,
|
||||
type: CallType.Video,
|
||||
direction: CallDirection.Incoming,
|
||||
status: DirectCallStatus.Accepted,
|
||||
timestamp: Date.now(),
|
||||
children: [
|
||||
{ callId: '123', timestamp: Date.now() },
|
||||
{ callId: '122', timestamp: Date.now() - mins(30) },
|
||||
{ callId: '121', timestamp: Date.now() - mins(45) },
|
||||
{ callId: '121', timestamp: Date.now() - mins(60) },
|
||||
],
|
||||
}}
|
||||
callHistoryGroup={getFakeCallHistoryGroup({
|
||||
peerId: props.conversation?.serviceId,
|
||||
})}
|
||||
selectedNavTab={NavTab.Calls}
|
||||
/>
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue