Moves panel header into the panel component

This commit is contained in:
Josh Perez 2023-07-10 18:44:32 -04:00 committed by GitHub
parent 0b885d5feb
commit 7d0f94c654
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 158 additions and 87 deletions

View file

@ -36,24 +36,6 @@
background-color: $color-gray-95; background-color: $color-gray-95;
} }
.panel {
height: calc(100% - #{$header-height} - var(--title-bar-drag-area-height));
inset-inline-start: 0;
overflow-y: overlay;
position: absolute;
top: calc(#{$header-height} + var(--title-bar-drag-area-height));
width: 100%;
z-index: $z-index-base;
@include light-theme() {
background-color: $color-white;
}
@include dark-theme() {
background-color: $color-gray-95;
}
}
.panel { .panel {
&:not(.main) { &:not(.main) {
&:dir(ltr) { &:dir(ltr) {

View file

@ -197,7 +197,6 @@
height: $icon-size; height: $icon-size;
margin-inline-end: var(--button-spacing); margin-inline-end: var(--button-spacing);
min-width: $icon-size; min-width: $icon-size;
opacity: 0;
padding: 2px; padding: 2px;
transition: margin-inline-end 200ms ease-out, opacity 200ms ease-out, transition: margin-inline-end 200ms ease-out, opacity 200ms ease-out,
background 100ms ease-out; background 100ms ease-out;
@ -207,10 +206,6 @@
cursor: default; cursor: default;
} }
&--show {
opacity: 1;
}
&--show-disabled { &--show-disabled {
opacity: 0.5; opacity: 0.5;
} }

View file

@ -0,0 +1,99 @@
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
.ConversationPanel {
height: 100%;
inset-inline-start: 0;
overflow-y: overlay;
position: absolute;
top: 0;
width: 100%;
z-index: $z-index-base;
@include light-theme() {
background-color: $color-white;
}
@include dark-theme() {
background-color: $color-gray-95;
}
&__header {
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;
}
&__info {
display: flex;
flex-direction: column;
min-width: 0;
&__title {
@include font-body-1-bold;
display: flex;
align-items: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
user-select: none;
&__in-contacts-icon {
margin-inline-start: 4px;
}
}
}
&__back-button {
border: none;
display: inline-block;
height: 20px;
margin-inline: 24px 6px;
min-width: 20px;
opacity: 0;
opacity: 1;
vertical-align: text-bottom;
-webkit-app-region: no-drag;
width: 20px;
&:disabled {
cursor: default;
}
@include light-theme {
@include color-svg(
'../images/icons/v3/chevron/chevron-left.svg',
$color-gray-90
);
}
@include dark-theme {
@include color-svg(
'../images/icons/v3/chevron/chevron-left.svg',
$color-gray-02
);
}
@include keyboard-mode {
&:focus {
background-color: $color-ultramarine;
}
}
@include dark-keyboard-mode {
&:focus {
background-color: $color-ultramarine-light;
}
}
}
}
}

View file

@ -9,10 +9,21 @@
&__pane { &__pane {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: calc(100% - #{$header-height} - var(--title-bar-drag-area-height));
overflow: initial; inset-inline-start: 0;
overflow-y: overlay;
position: absolute; position: absolute;
top: calc(#{$header-height} + var(--title-bar-drag-area-height));
width: 100%; width: 100%;
z-index: $z-index-base;
@include light-theme() {
background-color: $color-white;
}
@include dark-theme() {
background-color: $color-gray-95;
}
} }
&__timeline { &__timeline {
@ -44,4 +55,7 @@
margin-inline: 16px; margin-inline: 16px;
} }
} }
&__header {
}
} }

View file

@ -67,6 +67,7 @@
@import './components/ConversationHeader.scss'; @import './components/ConversationHeader.scss';
@import './components/ConversationHero.scss'; @import './components/ConversationHero.scss';
@import './components/ConversationMergeNotification.scss'; @import './components/ConversationMergeNotification.scss';
@import './components/ConversationPanel.scss';
@import './components/ConversationView.scss'; @import './components/ConversationView.scss';
@import './components/CustomColorEditor.scss'; @import './components/CustomColorEditor.scss';
@import './components/CustomizingPreferredReactionsModal.scss'; @import './components/CustomizingPreferredReactionsModal.scss';

View file

@ -51,11 +51,10 @@ export enum OutgoingCallButtonStyle {
export type PropsDataType = { export type PropsDataType = {
badge?: BadgeType; badge?: BadgeType;
cannotLeaveBecauseYouAreLastAdmin: boolean; cannotLeaveBecauseYouAreLastAdmin: boolean;
conversationTitle?: string; hasPanelShowing?: boolean;
hasStories?: HasStories; hasStories?: HasStories;
isMissingMandatoryProfileSharing?: boolean; isMissingMandatoryProfileSharing?: boolean;
outgoingCallButtonStyle: OutgoingCallButtonStyle; outgoingCallButtonStyle: OutgoingCallButtonStyle;
showBackButton?: boolean;
isSMSOnly?: boolean; isSMSOnly?: boolean;
isSignalConversation?: boolean; isSignalConversation?: boolean;
theme: ThemeType; theme: ThemeType;
@ -161,23 +160,6 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
} }
} }
private renderBackButton(): ReactNode {
const { i18n, popPanelForConversation, showBackButton } = this.props;
return (
<button
type="button"
onClick={popPanelForConversation}
className={classNames(
'module-ConversationHeader__back-icon',
showBackButton ? 'module-ConversationHeader__back-icon--show' : null
)}
disabled={!showBackButton}
aria-label={i18n('icu:goBack')}
/>
);
}
private renderHeaderInfoTitle(): ReactNode { private renderHeaderInfoTitle(): ReactNode {
const { name, title, type, i18n, isMe } = this.props; const { name, title, type, i18n, isMe } = this.props;
@ -304,7 +286,7 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
} }
private renderMoreButton(triggerId: string): ReactNode { private renderMoreButton(triggerId: string): ReactNode {
const { i18n, showBackButton } = this.props; const { i18n } = this.props;
return ( return (
<ContextMenuTrigger id={triggerId} ref={this.menuTriggerRef}> <ContextMenuTrigger id={triggerId} ref={this.menuTriggerRef}>
@ -313,10 +295,8 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
onClick={this.showMenuBound} onClick={this.showMenuBound}
className={classNames( className={classNames(
'module-ConversationHeader__button', 'module-ConversationHeader__button',
'module-ConversationHeader__button--more', 'module-ConversationHeader__button--more'
showBackButton ? null : 'module-ConversationHeader__button--show'
)} )}
disabled={showBackButton}
aria-label={i18n('icu:moreInfo')} aria-label={i18n('icu:moreInfo')}
/> />
</ContextMenuTrigger> </ContextMenuTrigger>
@ -324,7 +304,7 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
} }
private renderSearchButton(): ReactNode { private renderSearchButton(): ReactNode {
const { i18n, id, searchInConversation, showBackButton } = this.props; const { i18n, id, searchInConversation } = this.props;
return ( return (
<button <button
@ -332,10 +312,8 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
onClick={() => searchInConversation(id)} onClick={() => searchInConversation(id)}
className={classNames( className={classNames(
'module-ConversationHeader__button', 'module-ConversationHeader__button',
'module-ConversationHeader__button--search', 'module-ConversationHeader__button--search'
showBackButton ? null : 'module-ConversationHeader__button--show'
)} )}
disabled={showBackButton}
aria-label={i18n('icu:search')} aria-label={i18n('icu:search')}
/> />
); );
@ -700,20 +678,7 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
} }
private renderHeader(): ReactNode { private renderHeader(): ReactNode {
const { conversationTitle, groupVersion, pushPanelForConversation, type } = const { groupVersion, pushPanelForConversation, type } = this.props;
this.props;
if (conversationTitle !== undefined) {
return (
<div className="module-ConversationHeader__header">
<div className="module-ConversationHeader__header__info">
<div className="module-ConversationHeader__header__info__title">
{conversationTitle}
</div>
</div>
</div>
);
}
let onClick: undefined | (() => void); let onClick: undefined | (() => void);
switch (type) { switch (type) {
@ -775,6 +740,7 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
announcementsOnly, announcementsOnly,
areWeAdmin, areWeAdmin,
expireTimer, expireTimer,
hasPanelShowing,
i18n, i18n,
id, id,
isSMSOnly, isSMSOnly,
@ -783,8 +749,12 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
onOutgoingVideoCallInConversation, onOutgoingVideoCallInConversation,
outgoingCallButtonStyle, outgoingCallButtonStyle,
setDisappearingMessages, setDisappearingMessages,
showBackButton,
} = this.props; } = this.props;
if (hasPanelShowing) {
return null;
}
const { isNarrow, modalState } = this.state; const { isNarrow, modalState } = this.state;
const triggerId = `conversation-${id}`; const triggerId = `conversation-${id}`;
@ -829,7 +799,6 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
})} })}
ref={measureRef} ref={measureRef}
> >
{this.renderBackButton()}
{this.renderHeader()} {this.renderHeader()}
{!isSMSOnly && !isSignalConversation && ( {!isSMSOnly && !isSignalConversation && (
<OutgoingCallButtons <OutgoingCallButtons
@ -845,7 +814,6 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
onOutgoingVideoCallInConversation onOutgoingVideoCallInConversation
} }
outgoingCallButtonStyle={outgoingCallButtonStyle} outgoingCallButtonStyle={outgoingCallButtonStyle}
showBackButton={showBackButton}
/> />
)} )}
{this.renderSearchButton()} {this.renderSearchButton()}
@ -868,7 +836,6 @@ function OutgoingCallButtons({
onOutgoingAudioCallInConversation, onOutgoingAudioCallInConversation,
onOutgoingVideoCallInConversation, onOutgoingVideoCallInConversation,
outgoingCallButtonStyle, outgoingCallButtonStyle,
showBackButton,
}: { isNarrow: boolean } & Pick< }: { isNarrow: boolean } & Pick<
PropsType, PropsType,
| 'announcementsOnly' | 'announcementsOnly'
@ -878,7 +845,6 @@ function OutgoingCallButtons({
| 'onOutgoingAudioCallInConversation' | 'onOutgoingAudioCallInConversation'
| 'onOutgoingVideoCallInConversation' | 'onOutgoingVideoCallInConversation'
| 'outgoingCallButtonStyle' | 'outgoingCallButtonStyle'
| 'showBackButton'
>): JSX.Element | null { >): JSX.Element | null {
const videoButton = ( const videoButton = (
<button <button
@ -886,12 +852,10 @@ function OutgoingCallButtons({
className={classNames( className={classNames(
'module-ConversationHeader__button', 'module-ConversationHeader__button',
'module-ConversationHeader__button--video', 'module-ConversationHeader__button--video',
showBackButton ? null : 'module-ConversationHeader__button--show', announcementsOnly && !areWeAdmin
!showBackButton && announcementsOnly && !areWeAdmin
? 'module-ConversationHeader__button--show-disabled' ? 'module-ConversationHeader__button--show-disabled'
: undefined : undefined
)} )}
disabled={showBackButton}
onClick={() => onOutgoingVideoCallInConversation(id)} onClick={() => onOutgoingVideoCallInConversation(id)}
type="button" type="button"
/> />
@ -917,10 +881,8 @@ function OutgoingCallButtons({
onClick={() => onOutgoingAudioCallInConversation(id)} onClick={() => onOutgoingAudioCallInConversation(id)}
className={classNames( className={classNames(
'module-ConversationHeader__button', 'module-ConversationHeader__button',
'module-ConversationHeader__button--audio', 'module-ConversationHeader__button--audio'
showBackButton ? null : 'module-ConversationHeader__button--show'
)} )}
disabled={showBackButton}
aria-label={i18n('icu:makeOutgoingCall')} aria-label={i18n('icu:makeOutgoingCall')}
/> />
</> </>
@ -932,9 +894,10 @@ function OutgoingCallButtons({
className={classNames( className={classNames(
'module-ConversationHeader__button', 'module-ConversationHeader__button',
'module-ConversationHeader__button--join-call', 'module-ConversationHeader__button--join-call',
showBackButton ? null : 'module-ConversationHeader__button--show' announcementsOnly && !areWeAdmin
? 'module-ConversationHeader__button--show-disabled'
: undefined
)} )}
disabled={showBackButton}
onClick={() => onOutgoingVideoCallInConversation(id)} onClick={() => onOutgoingVideoCallInConversation(id)}
type="button" type="button"
> >

View file

@ -14,7 +14,6 @@ import { getPreferredBadgeSelector } from '../selectors/badges';
import { import {
getConversationByUuidSelector, getConversationByUuidSelector,
getConversationSelector, getConversationSelector,
getConversationTitle,
isMissingRequiredProfileSharing, isMissingRequiredProfileSharing,
} from '../selectors/conversations'; } from '../selectors/conversations';
import { CallMode } from '../../types/Calling'; import { CallMode } from '../../types/Calling';
@ -88,9 +87,8 @@ export function SmartConversationHeader({ id }: OwnProps): JSX.Element {
const badgeSelector = useSelector(getPreferredBadgeSelector); const badgeSelector = useSelector(getPreferredBadgeSelector);
const badge = badgeSelector(conversation.badges); const badge = badgeSelector(conversation.badges);
const conversationTitle = useSelector(getConversationTitle);
const i18n = useSelector(getIntl); const i18n = useSelector(getIntl);
const showBackButton = useSelector<StateType, boolean>( const hasPanelShowing = useSelector<StateType, boolean>(
state => state.conversations.targetedConversationPanels.length > 0 state => state.conversations.targetedConversationPanels.length > 0
); );
const outgoingCallButtonStyle = useSelector< const outgoingCallButtonStyle = useSelector<
@ -155,8 +153,8 @@ export function SmartConversationHeader({ id }: OwnProps): JSX.Element {
])} ])}
badge={badge} badge={badge}
cannotLeaveBecauseYouAreLastAdmin={cannotLeaveBecauseYouAreLastAdmin} cannotLeaveBecauseYouAreLastAdmin={cannotLeaveBecauseYouAreLastAdmin}
conversationTitle={conversationTitle}
destroyMessages={destroyMessages} destroyMessages={destroyMessages}
hasPanelShowing={hasPanelShowing}
hasStories={hasStories} hasStories={hasStories}
i18n={i18n} i18n={i18n}
id={id} id={id}
@ -178,7 +176,6 @@ export function SmartConversationHeader({ id }: OwnProps): JSX.Element {
setDisappearingMessages={setDisappearingMessages} setDisappearingMessages={setDisappearingMessages}
setMuteExpiration={setMuteExpiration} setMuteExpiration={setMuteExpiration}
setPinned={setPinned} setPinned={setPinned}
showBackButton={showBackButton}
theme={theme} theme={theme}
toggleSelectMode={toggleSelectMode} toggleSelectMode={toggleSelectMode}
viewUserStories={viewUserStories} viewUserStories={viewUserStories}

View file

@ -20,7 +20,7 @@ import { SmartMessageDetail } from './MessageDetail';
import { SmartPendingInvites } from './PendingInvites'; import { SmartPendingInvites } from './PendingInvites';
import { SmartStickerManager } from './StickerManager'; import { SmartStickerManager } from './StickerManager';
import { getIntl } from '../selectors/user'; import { getIntl } from '../selectors/user';
import { getTopPanel } from '../selectors/conversations'; import { getConversationTitle, getTopPanel } from '../selectors/conversations';
import { useConversationsActions } from '../ducks/conversations'; import { useConversationsActions } from '../ducks/conversations';
import { focusableSelectors } from '../../util/focusableSelectors'; import { focusableSelectors } from '../../util/focusableSelectors';
@ -30,10 +30,12 @@ export function ConversationPanel({
conversationId: string; conversationId: string;
}): JSX.Element | null { }): JSX.Element | null {
const i18n = useSelector(getIntl); const i18n = useSelector(getIntl);
const { startConversation } = useConversationsActions(); const { popPanelForConversation, startConversation } =
useConversationsActions();
const topPanel = useSelector<StateType, PanelRenderType | undefined>( const topPanel = useSelector<StateType, PanelRenderType | undefined>(
getTopPanel getTopPanel
); );
const conversationTitle = useSelector(getConversationTitle);
const selectors = useMemo(() => focusableSelectors.join(','), []); const selectors = useMemo(() => focusableSelectors.join(','), []);
const panelRef = useRef<HTMLDivElement | null>(null); const panelRef = useRef<HTMLDivElement | null>(null);
@ -109,7 +111,25 @@ export function ConversationPanel({
} }
return ( return (
<div className={classNames('panel', panelClassName)} ref={panelRef}> <div
className={classNames('ConversationPanel', 'panel', panelClassName)}
ref={panelRef}
>
<div className="ConversationPanel__header">
<button
aria-label={i18n('icu:goBack')}
className="ConversationPanel__header__back-button"
onClick={popPanelForConversation}
type="button"
/>
{conversationTitle && (
<div className="ConversationPanel__header__info">
<div className="ConversationPanel__header__info__title">
{conversationTitle}
</div>
</div>
)}
</div>
{panelChild} {panelChild}
</div> </div>
); );