From 7d29cb5edfa8a572dae2fb9a9d9449d5c0f68871 Mon Sep 17 00:00:00 2001 From: Josh Perez <60019601+josh-signal@users.noreply.github.com> Date: Thu, 15 Oct 2020 15:53:21 -0400 Subject: [PATCH] Calling participants list --- _locales/en/messages.json | 14 +++ stylesheets/_modules.scss | 96 ++++++++++++++++ .../CallingParticipantsList.stories.tsx | 64 +++++++++++ ts/components/CallingParticipantsList.tsx | 104 ++++++++++++++++++ 4 files changed, 278 insertions(+) create mode 100644 ts/components/CallingParticipantsList.stories.tsx create mode 100644 ts/components/CallingParticipantsList.tsx diff --git a/_locales/en/messages.json b/_locales/en/messages.json index b4026776cea..9d1b1a8ce28 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1186,6 +1186,20 @@ "message": "Your video is off", "description": "Label in the calling lobby indicating that your camera is off" }, + "calling__in-this-call--one": { + "message": "In this call · 1 person", + "description": "Shown in the participants list to describe how many people are in the call" + }, + "calling__in-this-call--many": { + "message": "In this call · $people$ people", + "description": "Shown in the participants list to describe how many people are in the call", + "placeholders": { + "people": { + "content": "$1", + "example": "15" + } + } + }, "alwaysRelayCallsDescription": { "message": "Always relay calls", "description": "Description of the always relay calls setting" diff --git a/stylesheets/_modules.scss b/stylesheets/_modules.scss index 13ba1ed26fe..22547bf7e8d 100644 --- a/stylesheets/_modules.scss +++ b/stylesheets/_modules.scss @@ -6376,6 +6376,102 @@ button.module-image__border-overlay:focus { } } +.module-calling-participants-list { + background-color: $color-gray-80; + border-radius: 8px; + color: $color-white; + margin-right: 12px; + margin-top: 54px; + padding: 14px; + padding-bottom: 0; + width: 280px; + + &__overlay { + display: flex; + height: 100vh; + justify-content: flex-end; + left: 0; + position: absolute; + top: 0; + width: 100vw; + z-index: 2; + } + + &__title { + @include font-body-2-bold; + } + + &__list { + margin: 0; + margin-top: 22px; + padding: 0; + } + + &__contact { + @include font-body-1; + align-items: center; + display: flex; + justify-content: space-between; + list-style-type: none; + margin-bottom: 16px; + } + + &__name { + display: inline-block; + margin-left: 8px; + max-width: 144px; + overflow: hidden; + text-overflow: ellipsis; + vertical-align: middle; + white-space: nowrap; + } + + &__header { + display: flex; + justify-content: space-between; + } + + &__close { + @include button-reset; + + @include color-svg('../images/x-shadow-16.svg', $color-gray-15); + + height: 16px; + width: 16px; + z-index: 2; + + @include keyboard-mode { + &:focus { + outline: 2px solid $ultramarine-ui-light; + } + } + } + + &__muted { + &--video { + @include color-svg( + '../images/icons/v2/video-off-solid-28.svg', + $color-white + ); + display: inline-block; + margin-left: 18px; + height: 16px; + width: 16px; + } + + &--audio { + @include color-svg( + '../images/icons/v2/mic-off-solid-28.svg', + $color-white + ); + display: inline-block; + margin-left: 18px; + height: 16px; + width: 16px; + } + } +} + .module-call-need-permission-screen { align-items: center; background-color: $color-gray-95; diff --git a/ts/components/CallingParticipantsList.stories.tsx b/ts/components/CallingParticipantsList.stories.tsx new file mode 100644 index 00000000000..50197b36077 --- /dev/null +++ b/ts/components/CallingParticipantsList.stories.tsx @@ -0,0 +1,64 @@ +import * as React from 'react'; +import { storiesOf } from '@storybook/react'; +import { action } from '@storybook/addon-actions'; + +import { CallingParticipantsList, PropsType } from './CallingParticipantsList'; +import { setup as setupI18n } from '../../js/modules/i18n'; +import enMessages from '../../_locales/en/messages.json'; + +const i18n = setupI18n('en', enMessages); + +const participant = { + title: 'Bardock', +}; + +const createProps = (overrideProps: Partial = {}): PropsType => ({ + i18n, + onClose: action('on-close'), + participants: overrideProps.participants || [participant], +}); + +const story = storiesOf('Components/CallingParticipantsList', module); + +story.add('Default', () => { + const props = createProps(); + return ; +}); + +story.add('Many Participants', () => { + const props = createProps({ + participants: [ + { + color: 'blue', + profileName: 'Son Goku', + title: 'Son Goku', + audioMuted: true, + videoMuted: true, + }, + { + color: 'deep_orange', + profileName: 'Rage Trunks', + title: 'Rage Trunks', + }, + { + color: 'indigo', + profileName: 'Prince Vegeta', + title: 'Prince Vegeta', + videoMuted: true, + }, + { + color: 'pink', + profileName: 'Goku Black', + title: 'Goku Black', + }, + { + color: 'green', + profileName: 'Supreme Kai Zamasu', + title: 'Supreme Kai Zamasu', + audioMuted: true, + videoMuted: true, + }, + ], + }); + return ; +}); diff --git a/ts/components/CallingParticipantsList.tsx b/ts/components/CallingParticipantsList.tsx new file mode 100644 index 00000000000..17e00bd5030 --- /dev/null +++ b/ts/components/CallingParticipantsList.tsx @@ -0,0 +1,104 @@ +/* eslint-disable react/no-array-index-key */ + +import React from 'react'; +import { createPortal } from 'react-dom'; +import { Avatar } from './Avatar'; +import { ColorType } from '../types/Colors'; +import { ContactName } from './conversation/ContactName'; +import { LocalizerType } from '../types/Util'; + +type ParticipantType = { + audioMuted?: boolean; + avatarPath?: string; + color?: ColorType; + profileName?: string; + title: string; + videoMuted?: boolean; +}; + +export type PropsType = { + readonly i18n: LocalizerType; + readonly onClose: () => void; + readonly participants: Array; +}; + +export const CallingParticipantsList = React.memo( + ({ i18n, onClose, participants }: PropsType) => { + const [root, setRoot] = React.useState(null); + + React.useEffect(() => { + const div = document.createElement('div'); + document.body.appendChild(div); + setRoot(div); + + return () => { + document.body.removeChild(div); + setRoot(null); + }; + }, []); + + if (!root) { + return null; + } + + return createPortal( +
+
+
+
+ {participants.length > 1 + ? i18n('calling__in-this-call--many', [ + String(participants.length), + ]) + : i18n('calling__in-this-call--one')} +
+
+
    + {participants.map((participant: ParticipantType, index: number) => ( +
  • +
    + + +
    +
    + {participant.audioMuted ? ( + + ) : null} + {participant.videoMuted ? ( + + ) : null} +
    +
  • + ))} +
+
+
, + root + ); + } +);