Skip to content

Commit a1afd43

Browse files
authored
End call experience (#4330)
* Add endCallButton option * - Add confirm window - Localized all stirngs - Add hover and pressed styles * Move confirm logic to composite
1 parent 7f6d201 commit a1afd43

56 files changed

Lines changed: 373 additions & 114 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"type": "prerelease",
3+
"area": "feature",
4+
"workstream": "End call experience",
5+
"comment": "Add endCallButton option",
6+
"packageName": "@azure/communication-react",
7+
"email": "jiangnanhello@live.com",
8+
"dependentChangeType": "patch"
9+
}

common/config/babel/features.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,9 @@ module.exports = {
8686
// Close captions feature for ACS calls
8787
"acs-close-captions",
8888
// Feature for local recording notification for teams meetings
89-
'local-recording-notification'
89+
'local-recording-notification',
90+
// Feature for end call options
91+
'end-call-options'
9092
],
9193
// A list of in progress beta feature.
9294
// These features are still beta feature but "in progress"

packages/communication-react/review/beta/communication-react.api.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -772,18 +772,24 @@ export interface CallCompositeStrings {
772772
dtmfDialerMoreButtonLabelOff?: string;
773773
dtmfDialerMoreButtonLabelOn?: string;
774774
dtmfDialpadPlaceholderText: string;
775+
endCallConfirmDialogContent?: string;
776+
endCallConfirmDialogTitle?: string;
775777
endOfSurveyText: string;
776778
exitSpotlightButtonLabel: string;
777779
exitSpotlightButtonTooltip: string;
778780
failedToJoinCallDueToNoNetworkMoreDetails?: string;
779781
failedToJoinCallDueToNoNetworkTitle: string;
780782
failedToJoinTeamsMeetingReasonAccessDeniedMoreDetails?: string;
781783
failedToJoinTeamsMeetingReasonAccessDeniedTitle: string;
784+
hangUpCancelButtonLabel?: string;
785+
hangUpConfirmButtonLabel?: string;
782786
holdScreenLabel: string;
783787
invalidMeetingIdentifier: string;
784788
inviteToRoomRemovedDetails?: string;
785789
inviteToRoomRemovedTitle: string;
786790
learnMore: string;
791+
leaveConfirmDialogContent?: string;
792+
leaveConfirmDialogTitle?: string;
787793
leavingCallTitle?: string;
788794
leftCallMoreDetails?: string;
789795
leftCallTitle: string;
@@ -1938,7 +1944,9 @@ export type CommonCallControlOptions = {
19381944
cameraButton?: boolean | /* @conditional-compile-remove(PSTN-calls) */ {
19391945
disabled: boolean;
19401946
};
1941-
endCallButton?: boolean;
1947+
endCallButton?: boolean | /* @conditional-compile-remove(end-call-options) */ {
1948+
hangUpForEveryone?: false | 'endCallOptions';
1949+
};
19421950
microphoneButton?: boolean | /* @conditional-compile-remove(PSTN-calls) */ {
19431951
disabled: boolean;
19441952
};
@@ -2876,13 +2884,16 @@ export const EndCallButton: (props: EndCallButtonProps) => JSX.Element;
28762884

28772885
// @public
28782886
export interface EndCallButtonProps extends ControlBarButtonProps {
2879-
onHangUp?: () => Promise<void>;
2887+
enableEndCallMenu?: boolean;
2888+
onHangUp?: (forEveryone?: boolean) => Promise<void>;
28802889
strings?: EndCallButtonStrings;
28812890
}
28822891

28832892
// @public
28842893
export interface EndCallButtonStrings {
2894+
endCallOption?: string;
28852895
label: string;
2896+
leaveOption?: string;
28862897
tooltipContent?: string;
28872898
}
28882899

packages/communication-react/review/stable/communication-react.api.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2292,7 +2292,7 @@ export const EndCallButton: (props: EndCallButtonProps) => JSX.Element;
22922292

22932293
// @public
22942294
export interface EndCallButtonProps extends ControlBarButtonProps {
2295-
onHangUp?: () => Promise<void>;
2295+
onHangUp?: (forEveryone?: boolean) => Promise<void>;
22962296
strings?: EndCallButtonStrings;
22972297
}
22982298

packages/react-components/src/components/EndCallButton.tsx

Lines changed: 125 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4-
import { concatStyleSets, Icon } from '@fluentui/react';
4+
import { concatStyleSets, Icon, IStyle, PartialTheme } from '@fluentui/react';
5+
/* @conditional-compile-remove(end-call-options) */
6+
import { IContextualMenuProps } from '@fluentui/react';
57
import React from 'react';
68
import { useLocale } from '../localization';
79
import { useTheme } from '../theming';
8-
import { darkTheme, lightTheme } from '../theming/themes';
10+
import { CallingTheme, darkTheme, lightTheme } from '../theming/themes';
911
import { isDarkThemed } from '../theming/themeUtils';
1012
import { ControlBarButton, ControlBarButtonProps } from './ControlBarButton';
13+
/* @conditional-compile-remove(end-call-options) */
14+
import { _preventDismissOnEvent as preventDismissOnEvent } from '@internal/acs-ui-common';
1115

1216
/**
1317
* Strings of {@link EndCallButton} that can be overridden.
@@ -21,6 +25,14 @@ export interface EndCallButtonStrings {
2125
label: string;
2226
/** Tooltip content. */
2327
tooltipContent?: string;
28+
29+
/* @conditional-compile-remove(end-call-options) */
30+
/** Label for leave option when ending call */
31+
leaveOption?: string;
32+
33+
/* @conditional-compile-remove(end-call-options) */
34+
/** Label for end the whole call option when ending call */
35+
endCallOption?: string;
2436
}
2537

2638
/**
@@ -33,7 +45,14 @@ export interface EndCallButtonProps extends ControlBarButtonProps {
3345
* Utility property for using this component with `communication react eventHandlers`.
3446
* Maps directly to the `onClick` property.
3547
*/
36-
onHangUp?: () => Promise<void>;
48+
onHangUp?: (forEveryone?: boolean) => Promise<void>;
49+
50+
/* @conditional-compile-remove(end-call-options) */
51+
/**
52+
* Set this to true to make it a split button.
53+
* The split arrow will trigger a contextual menu to allow end for everyone or just for the user.
54+
*/
55+
enableEndCallMenu?: boolean;
3756

3857
/**
3958
* Optional strings to override in component
@@ -51,7 +70,7 @@ const onRenderEndCallIcon = (): JSX.Element => <Icon iconName="ControlButtonEndC
5170
* @public
5271
*/
5372
export const EndCallButton = (props: EndCallButtonProps): JSX.Element => {
54-
const { styles } = props;
73+
const { styles, /* @conditional-compile-remove(end-call-options) */ enableEndCallMenu, onHangUp } = props;
5574

5675
const localeStrings = useLocale().strings.endCallButton;
5776
const strings = { ...localeStrings, ...props.strings };
@@ -63,71 +82,128 @@ export const EndCallButton = (props: EndCallButtonProps): JSX.Element => {
6382
styles ?? {}
6483
);
6584

85+
/* @conditional-compile-remove(end-call-options) */
86+
const defaultMenuProps: IContextualMenuProps = {
87+
items: [
88+
{
89+
key: 'endForSelf',
90+
text: localeStrings.leaveOption,
91+
title: localeStrings.leaveOption,
92+
onClick: () => {
93+
onHangUp && onHangUp(false);
94+
}
95+
},
96+
{
97+
key: 'endForEveryone',
98+
text: localeStrings.endCallOption,
99+
title: localeStrings.endCallOption,
100+
onClick: () => {
101+
onHangUp && onHangUp(true);
102+
}
103+
}
104+
],
105+
styles: props.styles,
106+
calloutProps: {
107+
styles: {
108+
root: {
109+
maxWidth: '95%'
110+
}
111+
},
112+
preventDismissOnEvent
113+
}
114+
};
115+
66116
return (
67-
<ControlBarButton
68-
{...props}
69-
onClick={props.onHangUp ?? props.onClick}
70-
styles={componentStyles}
71-
onRenderIcon={props.onRenderIcon ?? onRenderEndCallIcon}
72-
strings={strings}
73-
labelKey={props.labelKey ?? 'endCallButtonLabel'}
74-
/>
117+
<>
118+
<ControlBarButton
119+
{...props}
120+
/* @conditional-compile-remove(end-call-options) */
121+
menuProps={enableEndCallMenu ? defaultMenuProps : undefined}
122+
onClick={onHangUp ? () => onHangUp() : props.onClick}
123+
styles={componentStyles}
124+
onRenderIcon={props.onRenderIcon ?? onRenderEndCallIcon}
125+
strings={strings}
126+
labelKey={props.labelKey ?? 'endCallButtonLabel'}
127+
/>
128+
</>
75129
);
76130
};
77-
// using media query to prevent windows from overwriting the button color
78-
const darkThemeCallButtonStyles = {
79-
root: {
80-
color: darkTheme.callingPalette.iconWhite,
81-
background: darkTheme.callingPalette.callRed,
131+
132+
const getButtonStyles = (
133+
theme: PartialTheme & CallingTheme
134+
): { regular: IStyle; pressed: IStyle; hovered: IStyle } => ({
135+
regular: {
136+
color: theme.callingPalette.iconWhite,
137+
background: theme.callingPalette.callRed,
82138
'@media (forced-colors: active)': {
83139
forcedColorAdjust: 'auto',
84-
border: `1px ${lightTheme.palette?.neutralQuaternaryAlt} solid`
140+
border: `1px ${theme.palette?.neutralQuaternaryAlt} solid`
141+
},
142+
' i': {
143+
color: theme.callingPalette.iconWhite
85144
},
86-
':focus::after': { outlineColor: `${darkTheme.callingPalette.iconWhite} !important` } // added !important to avoid override by FluentUI button styles
145+
':focus::after': { outlineColor: `${theme.callingPalette.iconWhite} !important` } // added !important to avoid override by FluentUI button styles
87146
},
88-
rootHovered: {
89-
color: darkTheme.callingPalette.iconWhite,
90-
background: darkTheme.callingPalette.callRed,
147+
pressed: {
148+
color: theme.callingPalette.iconWhite,
149+
background: theme.callingPalette.callRedDarker,
150+
border: 'none',
151+
' i': {
152+
color: theme.callingPalette.iconWhite
153+
},
91154
'@media (forced-colors: active)': {
92155
forcedColorAdjust: 'auto'
93156
}
94157
},
95-
rootPressed: {
96-
color: darkTheme.callingPalette.iconWhite,
97-
background: darkTheme.callingPalette.callRed,
158+
hovered: {
159+
color: theme.callingPalette.iconWhite,
160+
background: theme.callingPalette.callRedDark,
161+
border: 'none',
162+
' i': {
163+
color: theme.callingPalette.iconWhite
164+
},
98165
'@media (forced-colors: active)': {
99166
forcedColorAdjust: 'auto'
100167
}
101-
},
168+
}
169+
});
170+
171+
const darkThemeButtonStyles = getButtonStyles(darkTheme);
172+
const lightThemeButtonStyles = getButtonStyles(lightTheme);
173+
174+
// using media query to prevent windows from overwriting the button color
175+
const darkThemeCallButtonStyles = {
176+
root: darkThemeButtonStyles.regular,
177+
rootHovered: darkThemeButtonStyles.hovered,
178+
rootPressed: darkThemeButtonStyles.pressed,
102179
label: {
103180
color: darkTheme.callingPalette.iconWhite
104-
}
181+
},
182+
splitButtonMenuButton: {
183+
...(darkThemeButtonStyles.regular as object),
184+
borderTop: 'none',
185+
borderRight: 'none',
186+
borderBottom: 'none',
187+
'&:hover': darkThemeButtonStyles.hovered
188+
},
189+
splitButtonMenuButtonChecked: darkThemeButtonStyles.hovered,
190+
splitButtonMenuButtonExpanded: darkThemeButtonStyles.pressed
105191
};
106192

107193
const lightThemeCallButtonStyles = {
108-
root: {
109-
color: lightTheme.callingPalette.iconWhite,
110-
background: lightTheme.callingPalette.callRed,
111-
'@media (forced-colors: active)': {
112-
forcedColorAdjust: 'auto',
113-
border: `1px ${lightTheme.palette?.neutralQuaternaryAlt} solid`
114-
},
115-
':focus::after': { outlineColor: `${lightTheme.callingPalette.iconWhite} !important` } // added !important to avoid override by FluentUI button styles
116-
},
117-
rootHovered: {
118-
color: lightTheme.callingPalette.iconWhite,
119-
background: lightTheme.callingPalette.callRed,
120-
'@media (forced-colors: active)': {
121-
forcedColorAdjust: 'auto'
122-
}
123-
},
124-
rootPressed: {
125-
color: lightTheme.callingPalette.iconWhite,
126-
background: lightTheme.callingPalette.callRed,
127-
'@media (forced-colors: active)': {
128-
forcedColorAdjust: 'auto'
129-
}
194+
root: lightThemeButtonStyles.regular,
195+
rootHovered: lightThemeButtonStyles.hovered,
196+
rootPressed: lightThemeButtonStyles.pressed,
197+
splitButtonMenuButton: {
198+
...(lightThemeButtonStyles.regular as object),
199+
borderTop: 'none',
200+
borderRight: 'none',
201+
borderBottom: 'none',
202+
'&:hover': lightThemeButtonStyles.hovered
130203
},
204+
splitButtonMenuButtonChecked: lightThemeButtonStyles.hovered,
205+
splitButtonMenuButtonExpanded: lightThemeButtonStyles.pressed,
206+
splitButtonMenuFocused: lightThemeButtonStyles.pressed,
131207
label: {
132208
color: lightTheme.callingPalette.iconWhite
133209
}

packages/react-components/src/localization/LocalizationProvider.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,6 @@ export interface ComponentStrings {
181181
/* @conditional-compile-remove(total-participant-count) */
182182
/** Strings for the participant list component */
183183
ParticipantList: ParticipantListStrings;
184-
/* @conditional-compile-remove(hide-attendee-name) */
185184
}
186185

187186
/**

packages/react-components/src/localization/locales/ar-SA/strings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -379,4 +379,4 @@
379379
"leftNavButtonAriaLabel": "الصفحة السابقة",
380380
"rightNavButtonAriaLabel": "الصفحة التالية"
381381
}
382-
}
382+
}

packages/react-components/src/localization/locales/cs-CZ/strings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -379,4 +379,4 @@
379379
"leftNavButtonAriaLabel": "předchozí stránka",
380380
"rightNavButtonAriaLabel": "další stránka"
381381
}
382-
}
382+
}

packages/react-components/src/localization/locales/de-DE/strings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -379,4 +379,4 @@
379379
"leftNavButtonAriaLabel": "Vorherige Seite",
380380
"rightNavButtonAriaLabel": "Nächste Seite"
381381
}
382-
}
382+
}

packages/react-components/src/localization/locales/en-GB/strings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -379,4 +379,4 @@
379379
"leftNavButtonAriaLabel": "previous page",
380380
"rightNavButtonAriaLabel": "next page"
381381
}
382-
}
382+
}

0 commit comments

Comments
 (0)