From dfa5005e7d782389046b1b596e7d8e42b438b144 Mon Sep 17 00:00:00 2001 From: Evan Hahn <69474926+EvanHahn-Signal@users.noreply.github.com> Date: Mon, 1 Mar 2021 14:08:37 -0600 Subject: [PATCH] Update conversation header design --- stylesheets/_modules.scss | 330 ------------------ .../components/ConversationHeader.scss | 330 ++++++++++++++++++ stylesheets/manifest.scss | 1 + ts/background.ts | 2 +- ts/components/Tooltip.tsx | 23 +- .../ConversationHeader.stories.tsx | 14 + .../conversation/ConversationHeader.tsx | 297 ++++++++-------- ts/util/lint/exceptions.json | 4 +- 8 files changed, 514 insertions(+), 487 deletions(-) create mode 100644 stylesheets/components/ConversationHeader.scss diff --git a/stylesheets/_modules.scss b/stylesheets/_modules.scss index cff75a61beb2..e9098d8c35d2 100644 --- a/stylesheets/_modules.scss +++ b/stylesheets/_modules.scss @@ -2853,336 +2853,6 @@ $timer-icons: '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '05', } } -// Module: Conversation Header - -.module-conversation-header { - padding-left: 16px; - padding-right: 16px; - padding-top: var(--title-bar-drag-area-height); - display: flex; - flex-direction: row; - align-items: center; - - height: calc(#{$header-height} + var(--title-bar-drag-area-height)); - - @include light-theme { - color: $color-gray-90; - background-color: $color-white; - } - @include dark-theme { - color: $color-gray-02; - background-color: $color-gray-95; - } -} - -.module-conversation-header__back-icon { - $transition: 250ms ease-out; - - display: inline-block; - margin-left: -10px; - margin-right: -10px; - width: 24px; - height: 24px; - min-width: 24px; - vertical-align: text-bottom; - border: none; - opacity: 0; - transition: margin-right $transition, opacity $transition; - - &:disabled { - cursor: default; - } - - &--show { - opacity: 1; - margin-right: 6px; - } - - @include light-theme { - @include color-svg( - '../images/icons/v2/chevron-left-24.svg', - $color-gray-90 - ); - } - @include dark-theme { - @include color-svg( - '../images/icons/v2/chevron-left-24.svg', - $color-gray-02 - ); - } -} - -.module-conversation-header__title-container { - flex-grow: 1; - flex-shrink: 1; - min-width: 0; - display: block; - - height: $header-height; -} - -.module-conversation-header__title-flex { - margin-left: auto; - margin-right: auto; - display: inline-flex; - flex-direction: row; - align-items: center; - height: $header-height; - max-width: 100%; -} - -.module-conversation-header__title-clickable { - cursor: pointer; - - &:focus { - @include mouse-mode { - outline: none; - } - } -} - -.module-conversation-header__note-to-self { - @include dark-theme { - color: $color-gray-02; - } -} - -.module-conversation-header__avatar { - min-width: 32px; - margin-right: 4px; -} - -.module-conversation-header__title { - margin-left: 6px; - min-width: 0; - - @include font-body-1-bold; - - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - - -webkit-user-select: text; - - @include light-theme { - color: $color-gray-90; - } - @include dark-theme { - color: $color-gray-02; - } -} - -.module-conversation-header__contacts-icon { - display: inline-block; - height: 15px; - width: 15px; - - margin-bottom: 3px; - vertical-align: middle; - - @include light-theme { - @include color-svg( - '../images/icons/v2/profile-circle-outline-24.svg', - $color-gray-60 - ); - } - @include dark-theme { - @include color-svg( - '../images/icons/v2/profile-circle-outline-24.svg', - $color-gray-25 - ); - } - - @include keyboard-mode { - &:focus { - @include color-svg( - '../images/icons/v2/profile-circle-outline-24.svg', - $ultramarine-ui-light - ); - } - } -} - -.module-conversation-header__title__profile-name { - @include font-body-1-bold-italic; -} - -.module-conversation-header__title__verified-icon { - display: inline-block; - width: 1.25em; - height: 1.25em; - vertical-align: text-bottom; - - @include light-theme { - @include color-svg('../images/icons/v2/check-24.svg', $color-gray-90); - } - @include dark-theme { - @include color-svg('../images/icons/v2/check-24.svg', $color-gray-02); - } -} - -.module-conversation-header__expiration { - display: flex; - flex-direction: row; - align-items: center; - padding-left: 8px; - padding-right: 8px; - transition: opacity 250ms ease-out; - - &--hidden { - opacity: 0; - } -} - -.module-conversation-header__expiration__clock-icon { - height: 24px; - width: 24px; - display: inline-block; - - @include light-theme { - @include color-svg('../images/icons/v2/timer-24.svg', $color-gray-60); - } - @include dark-theme { - @include color-svg('../images/icons/v2/timer-24.svg', $color-gray-25); - } -} - -.module-conversation-header__expiration__setting { - margin-left: 5px; - text-align: center; -} - -.module-conversation-header__more-button { - height: 24px; - width: 24px; - margin-left: 12px; - border: none; - opacity: 0; - transition: opacity 250ms ease-out; - - &:disabled { - cursor: default; - } - - &--show { - opacity: 1; - } - - @include light-theme { - @include color-svg( - '../images/icons/v2/chevron-down-24.svg', - $color-gray-75 - ); - } - @include dark-theme { - @include color-svg( - '../images/icons/v2/chevron-down-24.svg', - $color-gray-15 - ); - } -} - -.module-conversation-header__search-button { - height: 24px; - width: 24px; - margin-left: 12px; - border: none; - opacity: 0; - transition: opacity 250ms ease-out; - - &:disabled { - cursor: default; - } - - &--show { - opacity: 1; - } - - @include light-theme { - @include color-svg('../images/icons/v2/search-24.svg', $color-gray-75); - } - @include dark-theme { - @include color-svg('../images/icons/v2/search-24.svg', $color-gray-15); - } -} - -.module-conversation-header__calling-button { - $icon-size: 24px; - - margin-left: 12px; - border: none; - opacity: 0; - transition: opacity 250ms ease-out; - - &:disabled { - cursor: default; - } - - &--show { - opacity: 1; - } - - &--video { - @include light-theme { - @include color-svg( - '../images/icons/v2/video-outline-24.svg', - $color-gray-75 - ); - } - @include dark-theme { - @include color-svg( - '../images/icons/v2/video-solid-24.svg', - $color-gray-15 - ); - } - height: $icon-size; - width: $icon-size; - } - - &--audio { - @include light-theme { - @include color-svg( - '../images/icons/v2/phone-right-outline-24.svg', - $color-gray-75 - ); - } - @include dark-theme { - @include color-svg( - '../images/icons/v2/phone-right-solid-24.svg', - $color-gray-15 - ); - } - height: $icon-size; - width: $icon-size; - } - - &--join { - @include font-body-1; - align-items: center; - background-color: $color-accent-green; - border-radius: 9999px; // This ensures the borders are completely rounded. (A value like 100% would make it an ellipse.) - color: $color-white; - display: flex; - outline: none; - padding: 5px 18px; - - @include keyboard-mode { - &:focus { - box-shadow: 0px 0px 0px 4px $ultramarine-ui-light; - } - } - - &:before { - @include color-svg('../images/icons/v2/video-solid-24.svg', $color-white); - content: ''; - display: block; - height: $icon-size; - margin-right: 5px; - width: $icon-size; - } - } -} - // Module: Conversation Details .conversation-details-panel { diff --git a/stylesheets/components/ConversationHeader.scss b/stylesheets/components/ConversationHeader.scss new file mode 100644 index 000000000000..6bdfba60d179 --- /dev/null +++ b/stylesheets/components/ConversationHeader.scss @@ -0,0 +1,330 @@ +// Copyright 2021 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +.module-ConversationHeader { + --button-spacing: 24px; + + &.module-ConversationHeader--narrow { + --button-spacing: 16px; + } + + padding-top: var(--title-bar-drag-area-height); + display: flex; + flex-direction: row; + align-items: center; + + height: calc(#{$header-height} + var(--title-bar-drag-area-height)); + + @include light-theme { + color: $color-gray-90; + background-color: $color-white; + } + @include dark-theme { + color: $color-gray-02; + background-color: $color-gray-95; + } + + &__back-icon { + $transition: 250ms ease-out; + + display: inline-block; + width: 24px; + height: 24px; + min-width: 24px; + margin-left: -24px; + vertical-align: text-bottom; + border: none; + opacity: 0; + transition: margin-left $transition, opacity $transition; + + &:disabled { + cursor: default; + } + + &--show { + opacity: 1; + margin-right: 6px; + margin-left: 12px; + } + + @include light-theme { + @include color-svg( + '../images/icons/v2/chevron-left-24.svg', + $color-gray-90 + ); + } + @include dark-theme { + @include color-svg( + '../images/icons/v2/chevron-left-24.svg', + $color-gray-02 + ); + } + } + + &__header { + $padding: 4px 12px; + + align-items: center; + display: flex; + flex-direction: row; + flex-grow: 1; + margin-left: 4px; + margin-right: var(--button-spacing); + padding: $padding; + overflow: hidden; + min-width: 0; + transition: margin-right 200ms ease-out; + + &--clickable { + @include button-reset; + border-radius: 4px; + + // These are clobbered by button-reset: + margin-left: 4px; + margin-right: var(--button-spacing); + padding: $padding; + + @include keyboard-mode { + &:focus { + color: $ultramarine-ui-light; + } + } + @include dark-keyboard-mode { + &:focus { + color: $ultramarine-ui-dark; + } + } + } + + &__avatar { + min-width: 32px; + margin-right: 12px; + padding-top: 4px; + padding-bottom: 4px; + } + + &__info { + display: flex; + flex-direction: column; + min-width: 0; + + &__title { + @include font-body-1-bold; + + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + user-select: none; + + &__in-contacts-icon { + margin-left: 4px; + } + } + + &__subtitle { + display: flex; + @include font-body-2; + + @include light-theme { + color: $color-gray-60; + } + @include dark-theme { + color: $color-gray-25; + } + + @mixin subtitle-element($icon) { + display: flex; + align-items: center; + user-select: none; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + + &::before { + content: ''; + width: 13px; + height: 13px; + display: block; + margin-right: 4px; + + @include light-theme { + @include color-svg($icon, $color-gray-60); + } + @include dark-theme { + @include color-svg($icon, $color-gray-25); + } + } + } + + &__expiration { + @include subtitle-element('../images/icons/v2/timer-24.svg'); + margin-right: 12px; + } + + &__verified { + @include subtitle-element('../images/icons/v2/check-24.svg'); + } + } + } + } + + &__calling-button--video, + &__calling-button--audio, + &__calling-button--join, + &__search-button, + &__more-button { + margin-right: var(--button-spacing); + transition: margin-right 200ms ease-out; + + @include keyboard-mode { + &:focus { + background: $ultramarine-ui-light; + } + } + @include dark-keyboard-mode { + &:focus { + background: $ultramarine-ui-dark; + } + } + } + + &__calling-button { + $icon-size: 24px; + + border: none; + opacity: 0; + transition: opacity 250ms ease-out; + + &:disabled { + cursor: default; + } + + &--show { + opacity: 1; + } + + &--video { + @include light-theme { + @include color-svg( + '../images/icons/v2/video-outline-24.svg', + $color-gray-75 + ); + } + @include dark-theme { + @include color-svg( + '../images/icons/v2/video-solid-24.svg', + $color-gray-15 + ); + } + height: $icon-size; + width: $icon-size; + min-width: $icon-size; + } + + &--audio { + @include light-theme { + @include color-svg( + '../images/icons/v2/phone-right-outline-24.svg', + $color-gray-75 + ); + } + @include dark-theme { + @include color-svg( + '../images/icons/v2/phone-right-solid-24.svg', + $color-gray-15 + ); + } + height: $icon-size; + width: $icon-size; + min-width: $icon-size; + } + + &--join { + @include font-body-1; + align-items: center; + background-color: $color-accent-green; + border-radius: 9999px; // This ensures the borders are completely rounded. (A value like 100% would make it an ellipse.) + color: $color-white; + display: flex; + outline: none; + overflow: hidden; + padding: 5px 18px; + text-overflow: ellipsis; + white-space: nowrap; + user-select: none; + + @include keyboard-mode { + &:focus { + box-shadow: 0px 0px 0px 4px $ultramarine-ui-light; + } + } + + &:before { + @include color-svg( + '../images/icons/v2/video-solid-24.svg', + $color-white + ); + content: ''; + display: block; + height: $icon-size; + margin-right: 5px; + min-width: $icon-size; + width: $icon-size; + } + } + } + + &__search-button { + height: 24px; + width: 24px; + min-width: 24px; + border: none; + opacity: 0; + transition: opacity 250ms ease-out; + + &:disabled { + cursor: default; + } + + &--show { + opacity: 1; + } + + @include light-theme { + @include color-svg('../images/icons/v2/search-24.svg', $color-gray-75); + } + @include dark-theme { + @include color-svg('../images/icons/v2/search-24.svg', $color-gray-15); + } + } + + &__more-button { + height: 24px; + width: 24px; + min-width: 24px; + border: none; + opacity: 0; + transition: opacity 250ms ease-out; + + &:disabled { + cursor: default; + } + + &--show { + opacity: 1; + } + + @include light-theme { + @include color-svg( + '../images/icons/v2/chevron-down-24.svg', + $color-gray-75 + ); + } + @include dark-theme { + @include color-svg( + '../images/icons/v2/chevron-down-24.svg', + $color-gray-15 + ); + } + } +} diff --git a/stylesheets/manifest.scss b/stylesheets/manifest.scss index b4d853898626..674be45d1129 100644 --- a/stylesheets/manifest.scss +++ b/stylesheets/manifest.scss @@ -28,3 +28,4 @@ // New style: components @import './components/Button.scss'; +@import './components/ConversationHeader.scss'; diff --git a/ts/background.ts b/ts/background.ts index 59d95fe46514..91222b96dfdc 100644 --- a/ts/background.ts +++ b/ts/background.ts @@ -1070,7 +1070,7 @@ export async function startApp(): Promise { (key === 'l' || key === 'L') ) { const button = document.querySelector( - '.module-conversation-header__more-button' + '.module-ConversationHeader__more-button' ); if (!button) { return; diff --git a/ts/components/Tooltip.tsx b/ts/components/Tooltip.tsx index 6c3f67c5698d..f87e6566b24c 100644 --- a/ts/components/Tooltip.tsx +++ b/ts/components/Tooltip.tsx @@ -22,6 +22,14 @@ const TooltipEventWrapper = React.forwardRef< >(({ onHoverChanged, children }, ref) => { const wrapperRef = React.useRef(null); + const on = React.useCallback(() => { + onHoverChanged(true); + }, [onHoverChanged]); + + const off = React.useCallback(() => { + onHoverChanged(false); + }, [onHoverChanged]); + React.useEffect(() => { const wrapperEl = wrapperRef.current; @@ -29,28 +37,19 @@ const TooltipEventWrapper = React.forwardRef< return noop; } - const on = () => { - onHoverChanged(true); - }; - const off = () => { - onHoverChanged(false); - }; - - wrapperEl.addEventListener('focus', on); - wrapperEl.addEventListener('blur', off); wrapperEl.addEventListener('mouseenter', on); wrapperEl.addEventListener('mouseleave', off); return () => { - wrapperEl.removeEventListener('focus', on); - wrapperEl.removeEventListener('blur', off); wrapperEl.removeEventListener('mouseenter', on); wrapperEl.removeEventListener('mouseleave', off); }; - }, [onHoverChanged]); + }, [on, off]); return ( { wrapperRef.current = el; diff --git a/ts/components/conversation/ConversationHeader.stories.tsx b/ts/components/conversation/ConversationHeader.stories.tsx index 5f005ac1feee..c7d34124cc83 100644 --- a/ts/components/conversation/ConversationHeader.stories.tsx +++ b/ts/components/conversation/ConversationHeader.stories.tsx @@ -159,6 +159,20 @@ const stories: Array = [ acceptedMessageRequest: true, }, }, + { + title: 'Disappearing messages + verified', + props: { + ...commonProps, + color: 'indigo', + title: '(202) 555-0005', + phoneNumber: '(202) 555-0005', + type: 'direct', + id: '5', + expireTimer: 60, + acceptedMessageRequest: true, + isVerified: true, + }, + }, { title: 'Muting Conversation', props: { diff --git a/ts/components/conversation/ConversationHeader.tsx b/ts/components/conversation/ConversationHeader.tsx index c515539d0426..28323aca2334 100644 --- a/ts/components/conversation/ConversationHeader.tsx +++ b/ts/components/conversation/ConversationHeader.tsx @@ -1,7 +1,8 @@ -// Copyright 2018-2020 Signal Messenger, LLC +// Copyright 2018-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import React from 'react'; +import React, { ReactNode } from 'react'; +import Measure from 'react-measure'; import moment from 'moment'; import classNames from 'classnames'; import { @@ -92,27 +93,33 @@ export type PropsType = PropsDataType & PropsActionsType & PropsHousekeepingType; -export class ConversationHeader extends React.Component { - public showMenuBound: (event: React.MouseEvent) => void; +type StateType = { + isNarrow: boolean; +}; + +export class ConversationHeader extends React.Component { + private showMenuBound: (event: React.MouseEvent) => void; // Comes from a third-party dependency // eslint-disable-next-line @typescript-eslint/no-explicit-any - public menuTriggerRef: React.RefObject; + private menuTriggerRef: React.RefObject; public constructor(props: PropsType) { super(props); + this.state = { isNarrow: false }; + this.menuTriggerRef = React.createRef(); this.showMenuBound = this.showMenu.bind(this); } - public showMenu(event: React.MouseEvent): void { + private showMenu(event: React.MouseEvent): void { if (this.menuTriggerRef.current) { this.menuTriggerRef.current.handleContextClick(event); } } - public renderBackButton(): JSX.Element { + private renderBackButton(): ReactNode { const { i18n, onGoBack, showBackButton } = this.props; return ( @@ -120,8 +127,8 @@ export class ConversationHeader extends React.Component { type="button" onClick={onGoBack} className={classNames( - 'module-conversation-header__back-icon', - showBackButton ? 'module-conversation-header__back-icon--show' : null + 'module-ConversationHeader__back-icon', + showBackButton ? 'module-ConversationHeader__back-icon--show' : null )} disabled={!showBackButton} aria-label={i18n('goBack')} @@ -129,51 +136,49 @@ export class ConversationHeader extends React.Component { ); } - public renderTitle(): JSX.Element | null { - const { - name, - phoneNumber, - title, - type, - i18n, - isMe, - profileName, - isVerified, - } = this.props; + private renderHeaderInfoTitle(): ReactNode { + const { name, title, type, i18n, isMe } = this.props; if (isMe) { return ( -
+
{i18n('noteToSelf')}
); } const shouldShowIcon = Boolean(name && type === 'direct'); - const shouldShowNumber = Boolean(phoneNumber && (name || profileName)); return ( -
+
{shouldShowIcon ? ( - - {' '} - - - ) : null} - {shouldShowNumber ? ` · ${phoneNumber}` : null} - {isVerified ? ( - - {' · '} - - {i18n('verified')} - + ) : null}
); } - public renderAvatar(): JSX.Element { + private renderHeaderInfoSubtitle(): ReactNode { + const expirationNode = this.renderExpirationLength(); + const verifiedNode = this.renderVerifiedIcon(); + + if (expirationNode || verifiedNode) { + return ( +
+ {expirationNode} + {verifiedNode} +
+ ); + } + + return null; + } + + private renderAvatar(): ReactNode { const { avatarPath, color, @@ -187,7 +192,7 @@ export class ConversationHeader extends React.Component { } = this.props; return ( - + { ); } - public renderExpirationLength(): JSX.Element | null { - const { i18n, expireTimer, showBackButton } = this.props; + private renderExpirationLength(): ReactNode { + const { i18n, expireTimer } = this.props; const expirationSettingName = expireTimer - ? ExpirationTimerOptions.getName(i18n, expireTimer) + ? ExpirationTimerOptions.getAbbreviated(i18n, expireTimer) : undefined; if (!expirationSettingName) { return null; } return ( -
-
-
- {expirationSettingName} -
+
+ {expirationSettingName}
); } - public renderMoreButton(triggerId: string): JSX.Element { + private renderVerifiedIcon(): ReactNode { + const { i18n, isVerified } = this.props; + + if (!isVerified) { + return null; + } + + return ( +
+ {i18n('verified')} +
+ ); + } + + private renderMoreButton(triggerId: string): ReactNode { const { i18n, showBackButton } = this.props; return ( @@ -240,10 +249,10 @@ export class ConversationHeader extends React.Component { type="button" onClick={this.showMenuBound} className={classNames( - 'module-conversation-header__more-button', + 'module-ConversationHeader__more-button', showBackButton ? null - : 'module-conversation-header__more-button--show' + : 'module-ConversationHeader__more-button--show' )} disabled={showBackButton} aria-label={i18n('moreInfo')} @@ -252,7 +261,7 @@ export class ConversationHeader extends React.Component { ); } - public renderSearchButton(): JSX.Element { + private renderSearchButton(): ReactNode { const { i18n, onSearchInConversation, showBackButton } = this.props; return ( @@ -260,10 +269,10 @@ export class ConversationHeader extends React.Component { type="button" onClick={onSearchInConversation} className={classNames( - 'module-conversation-header__search-button', + 'module-ConversationHeader__search-button', showBackButton ? null - : 'module-conversation-header__search-button--show' + : 'module-ConversationHeader__search-button--show' )} disabled={showBackButton} aria-label={i18n('search')} @@ -271,7 +280,7 @@ export class ConversationHeader extends React.Component { ); } - private renderOutgoingCallButtons(): JSX.Element | null { + private renderOutgoingCallButtons(): ReactNode { const { i18n, onOutgoingAudioCallInConversation, @@ -279,17 +288,18 @@ export class ConversationHeader extends React.Component { outgoingCallButtonStyle, showBackButton, } = this.props; + const { isNarrow } = this.state; const videoButton = ( ); default: @@ -342,7 +353,7 @@ export class ConversationHeader extends React.Component { } } - public renderMenu(triggerId: string): JSX.Element { + private renderMenu(triggerId: string): ReactNode { const { i18n, acceptedMessageRequest, @@ -484,7 +495,7 @@ export class ConversationHeader extends React.Component { ); } - private renderHeader(): JSX.Element { + private renderHeader(): ReactNode { const { conversationTitle, groupVersion, @@ -497,92 +508,94 @@ export class ConversationHeader extends React.Component { if (conversationTitle !== undefined) { return ( -
-
- {conversationTitle} +
+
+
+ {conversationTitle} +
); } - const hasGV2AdminEnabled = - groupVersion === 2 && - window.Signal.RemoteConfig.isEnabled('desktop.gv2Admin'); - - if (type === 'group' && hasGV2AdminEnabled) { - const onHeaderClick = () => onShowConversationDetails(); - const onKeyDown = (e: React.KeyboardEvent): void => { - if (e.key === 'Enter' || e.key === ' ') { - e.stopPropagation(); - e.preventDefault(); - - onShowConversationDetails(); - } - }; - - return ( -
- {this.renderAvatar()} - {this.renderTitle()} -
- ); - } - - if (type === 'group' || isMe) { - return ( -
- {this.renderAvatar()} - {this.renderTitle()} -
- ); - } - - const onContactClick = () => onShowContactModal(id); - const onKeyDown = (e: React.KeyboardEvent): void => { - if (e.key === 'Enter' || e.key === ' ') { - e.stopPropagation(); - e.preventDefault(); - - onShowContactModal(id); + let onClick: undefined | (() => void); + switch (type) { + case 'direct': + onClick = isMe + ? undefined + : () => { + onShowContactModal(id); + }; + break; + case 'group': { + const hasGV2AdminEnabled = + groupVersion === 2 && + window.Signal.RemoteConfig.isEnabled('desktop.gv2Admin'); + onClick = hasGV2AdminEnabled + ? () => { + onShowConversationDetails(); + } + : undefined; + break; } - }; + default: + throw missingCaseError(type); + } - return ( -
+ const contents = ( + <> {this.renderAvatar()} - {this.renderTitle()} -
+
+ {this.renderHeaderInfoTitle()} + {this.renderHeaderInfoSubtitle()} +
+ ); + + if (onClick) { + return ( + + ); + } + + return
{contents}
; } - public render(): JSX.Element { + public render(): ReactNode { const { id } = this.props; + const { isNarrow } = this.state; const triggerId = `conversation-${id}`; return ( -
- {this.renderBackButton()} -
- {this.renderHeader()} -
- {this.renderExpirationLength()} - {this.renderOutgoingCallButtons()} - {this.renderSearchButton()} - {this.renderMoreButton(triggerId)} - {this.renderMenu(triggerId)} -
+ { + const width = (bounds && bounds.width) || 0; + this.setState({ isNarrow: width < 500 }); + }} + > + {({ measureRef }) => ( +
+ {this.renderBackButton()} + {this.renderHeader()} + {this.renderOutgoingCallButtons()} + {this.renderSearchButton()} + {this.renderMoreButton(triggerId)} + {this.renderMenu(triggerId)} +
+ )} +
); } } diff --git a/ts/util/lint/exceptions.json b/ts/util/lint/exceptions.json index 82953a14f88a..aaef610e4abb 100644 --- a/ts/util/lint/exceptions.json +++ b/ts/util/lint/exceptions.json @@ -14820,7 +14820,7 @@ "rule": "React-createRef", "path": "ts/components/conversation/ConversationHeader.js", "line": " this.menuTriggerRef = react_1.default.createRef();", - "lineNumber": 30, + "lineNumber": 32, "reasonCategory": "usageTrusted", "updated": "2020-08-28T16:12:19.904Z", "reasonDetail": "Used to reference popup menu" @@ -14829,7 +14829,7 @@ "rule": "React-createRef", "path": "ts/components/conversation/ConversationHeader.tsx", "line": " this.menuTriggerRef = React.createRef();", - "lineNumber": 105, + "lineNumber": 112, "reasonCategory": "usageTrusted", "updated": "2020-05-20T20:10:43.540Z", "reasonDetail": "Used to reference popup menu"