MM-56324 Channel Header UI Update (#26383)

Этот коммит содержится в:
Matthew Birtch
2024-04-26 09:21:28 -04:00
коммит произвёл GitHub
родитель 54d7011aba
Коммит 4ba736f018
17 изменённых файлов: 2600 добавлений и 2482 удалений

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@@ -21,8 +21,6 @@ import Markdown from 'components/markdown';
import OverlayTrigger from 'components/overlay_trigger';
import type {BaseOverlayTrigger} from 'components/overlay_trigger';
import ChannelPermissionGate from 'components/permissions_gates/channel_permission_gate';
import {statusDropdownMessages} from 'components/status_dropdown/status_dropdown';
import StatusIcon from 'components/status_icon';
import Timestamp from 'components/timestamp';
import Tooltip from 'components/tooltip';
import Popover from 'components/widgets/popover';
@@ -241,6 +239,7 @@ class ChannelHeader extends React.PureComponent<Props, State> {
/>
<CustomStatusText
text={customStatus?.text}
className='custom-emoji__text'
/>
</div>
);
@@ -270,7 +269,7 @@ class ChannelHeader extends React.PureComponent<Props, State> {
<span tabIndex={0}>
<FormattedMessage
id='channel_header.channelHasGuests'
defaultMessage='This channel has guests'
defaultMessage='Channel has guests'
/>
</span>
</span>
@@ -308,14 +307,10 @@ class ChannelHeader extends React.PureComponent<Props, State> {
}
}
let dmHeaderIconStatus: ReactNode;
let dmHeaderTextStatus: ReactNode;
if (isDirect && !dmUser?.delete_at && !dmUser?.is_bot) {
dmHeaderIconStatus = (<StatusIcon status={channel.status}/>);
dmHeaderTextStatus = (
<span className='header-status__text'>
<FormattedMessage {...statusDropdownMessages[channel.status!].name}/>
{this.renderCustomStatus()}
</span>
);
@@ -345,11 +340,11 @@ class ChannelHeader extends React.PureComponent<Props, State> {
}
}
const channelFilesIconClass = classNames('channel-header__icon channel-header__icon--wide channel-header__icon--left', {
const channelFilesIconClass = classNames('channel-header__icon channel-header__icon--left btn btn-icon btn-xs ', {
'channel-header__icon--active': rhsState === RHSStates.CHANNEL_FILES,
});
const channelFilesIcon = <i className='icon icon-file-text-outline'/>;
const pinnedIconClass = classNames('channel-header__icon channel-header__icon--wide channel-header__icon--left', {
const pinnedIconClass = classNames('channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs', {
'channel-header__icon--active': rhsState === RHSStates.PIN,
});
const pinnedIcon = this.props.pinnedPostsCount ? (
@@ -374,7 +369,7 @@ class ChannelHeader extends React.PureComponent<Props, State> {
let memberListButton = null;
if (!isDirect) {
const membersIconClass = classNames('member-rhs__trigger channel-header__icon channel-header__icon--left channel-header__icon--wide', {
const membersIconClass = classNames('member-rhs__trigger channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs', {
'channel-header__icon--active': rhsState === RHSStates.CHANNEL_MEMBERS,
});
const membersIcon = this.props.memberCount ? (
@@ -428,7 +423,7 @@ class ChannelHeader extends React.PureComponent<Props, State> {
id='header-popover'
popoverStyle='info'
popoverSize='lg'
style={{transform: `translate(${this.state.leftOffset}px, ${this.state.topOffset}px)`, maxWidth: this.state.channelHeaderPoverWidth}}
style={{transform: `translate(${this.state.leftOffset}px, ${this.state.topOffset}px)`, maxWidth: this.state.channelHeaderPoverWidth + 16}}
placement='bottom'
className={classNames('channel-header__popover', {'chanel-header__popover--lhs_offset': this.props.hasMoreThanOneTeam})}
>
@@ -450,28 +445,7 @@ class ChannelHeader extends React.PureComponent<Props, State> {
className='channel-header__description'
dir='auto'
>
{dmHeaderIconStatus}
{dmHeaderTextStatus}
{memberListButton}
<HeaderIconWrapper
iconComponent={pinnedIcon}
ariaLabel={true}
buttonClass={pinnedIconClass}
buttonId={'channelHeaderPinButton'}
onClick={this.showPinnedPosts}
tooltipKey={'pinnedPosts'}
/>
{this.props.isFileAttachmentsEnabled &&
<HeaderIconWrapper
iconComponent={channelFilesIcon}
ariaLabel={true}
buttonClass={channelFilesIconClass}
buttonId={'channelHeaderFilesButton'}
onClick={this.showChannelFiles}
tooltipKey={'channelFiles'}
/>
}
{hasGuestsText}
<div
className='header-popover-text-measurer'
@@ -557,30 +531,9 @@ class ChannelHeader extends React.PureComponent<Props, State> {
headerTextContainer = (
<div
id='channelHeaderDescription'
className='channel-header__description light'
className='channel-header__description'
>
{dmHeaderIconStatus}
{dmHeaderTextStatus}
{memberListButton}
<HeaderIconWrapper
iconComponent={pinnedIcon}
ariaLabel={true}
buttonClass={pinnedIconClass}
buttonId={'channelHeaderPinButton'}
onClick={this.showPinnedPosts}
tooltipKey={'pinnedPosts'}
/>
{this.props.isFileAttachmentsEnabled &&
<HeaderIconWrapper
iconComponent={channelFilesIcon}
ariaLabel={true}
buttonClass={channelFilesIconClass}
buttonId={'channelHeaderFilesButton'}
onClick={this.showChannelFiles}
tooltipKey={'channelFiles'}
/>
}
{hasGuestsText}
{editMessage}
</div>
@@ -607,7 +560,7 @@ class ChannelHeader extends React.PureComponent<Props, State> {
<button
id='toggleMute'
onClick={this.unmute}
className={'style--none color--link channel-header__mute inactive'}
className={'channel-header__mute inactive btn btn-icon btn-xs'}
aria-label={formatMessage({id: 'generic_icons.muted', defaultMessage: 'Muted Icon'})}
>
<i className={'icon icon-bell-off-outline'}/>
@@ -635,15 +588,36 @@ class ChannelHeader extends React.PureComponent<Props, State> {
<div
className='channel-header__title dropdown'
>
<div>
<ChannelHeaderTitle
dmUser={dmUser}
gmMembers={gmMembers}
<ChannelHeaderTitle
dmUser={dmUser}
gmMembers={gmMembers}
/>
<div
className='channel-header__icons'
>
{muteTrigger}
{memberListButton}
<HeaderIconWrapper
iconComponent={pinnedIcon}
ariaLabel={true}
buttonClass={pinnedIconClass}
buttonId={'channelHeaderPinButton'}
onClick={this.showPinnedPosts}
tooltipKey={'pinnedPosts'}
/>
{this.props.isFileAttachmentsEnabled &&
<HeaderIconWrapper
iconComponent={channelFilesIcon}
ariaLabel={true}
buttonClass={channelFilesIconClass}
buttonId={'channelHeaderFilesButton'}
onClick={this.showChannelFiles}
tooltipKey={'channelFiles'}
/>
}
</div>
{muteTrigger}
{headerTextContainer}
</div>
{headerTextContainer}
</div>
</div>
<ChannelHeaderPlug

Просмотреть файл

@@ -3,19 +3,24 @@
import classNames from 'classnames';
import type {ReactNode} from 'react';
import React, {memo, useState} from 'react';
import React, {memo, useState, useRef, useEffect} from 'react';
import {useIntl} from 'react-intl';
import {useSelector} from 'react-redux';
import type {UserProfile} from '@mattermost/types/users';
import {Client4} from 'mattermost-redux/client';
import {getCurrentChannel} from 'mattermost-redux/selectors/entities/channels';
import {getIsRhsOpen} from 'selectors/rhs';
import {ChannelHeaderDropdown} from 'components/channel_header_dropdown';
import ProfilePicture from 'components/profile_picture';
import SharedChannelIndicator from 'components/shared_channel_indicator';
import ArchiveIcon from 'components/widgets/icons/archive_icon';
import MenuWrapper from 'components/widgets/menu/menu_wrapper';
import BotTag from 'components/widgets/tag/bot_tag';
import WithTooltip from 'components/with_tooltip';
import {Constants} from 'utils/constants';
@@ -33,13 +38,33 @@ const ChannelHeaderTitle = ({
gmMembers,
}: Props) => {
const [titleMenuOpen, setTitleMenuOpen] = useState(false);
const [showTooltip, setShowTooltip] = useState(false);
const intl = useIntl();
const channel = useSelector(getCurrentChannel);
const headerItemRef = useRef<HTMLElement | null>(null);
const isRHSOpen = useSelector(getIsRhsOpen);
useEffect(() => {
enableToolTipIfNeeded();
// Re-check on window resize
const handleResize = () => enableToolTipIfNeeded();
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, [channel, gmMembers, dmUser, isRHSOpen]);
if (!channel) {
return null;
}
const enableToolTipIfNeeded = () => {
const element = headerItemRef.current;
const isTooltip = element && element.offsetWidth < element.scrollWidth;
setShowTooltip(isTooltip as boolean);
};
const isDirect = (channel.type === Constants.DM_CHANNEL);
const isGroup = (channel.type === Constants.GM_CHANNEL);
const channelIsArchived = channel.delete_at !== 0;
@@ -71,8 +96,13 @@ const ChannelHeaderTitle = ({
return (
<div
id='channelHeaderDropdownButton'
className='channel-header__top channel-header__bot'
className='channel-header__bot'
>
<ChannelHeaderTitleFavorite/>
<ProfilePicture
src={Client4.getProfilePictureUrl(dmUser.id, dmUser.last_picture_update)}
size='sm'
/>
<strong
role='heading'
aria-level={2}
@@ -85,33 +115,63 @@ const ChannelHeaderTitle = ({
</span>
</strong>
<BotTag/>
<ChannelHeaderTitleFavorite/>
</div>
);
}
return (
<React.Fragment>
<div className='channel-header__top'>
<ChannelHeaderTitleFavorite/>
{isDirect && dmUser && ( // Check if it's a DM and dmUser is provided
<ProfilePicture
src={Client4.getProfilePictureUrl(dmUser.id, dmUser.last_picture_update)}
size='sm'
status={channel.status}
/>
)}
<MenuWrapper onToggle={setTitleMenuOpen}>
<div
id='channelHeaderDropdownButton'
className='channel-header__top'
>
<button
className={classNames('channel-header__trigger style--none', {active: titleMenuOpen})}
aria-label={intl.formatMessage({id: 'channel_header.menuAriaLabel', defaultMessage: 'Channel Menu'}).toLowerCase()}
>
<strong
role='heading'
aria-level={2}
id='channelHeaderTitle'
className='heading'
>
<span>
{archivedIcon}
{channelTitle}
{sharedIcon}
</span>
</strong>
{showTooltip ? (
<WithTooltip
id='channelHeaderTooltip'
placement='bottom'
title={channelTitle as string}
>
<strong
role='heading'
aria-level={2}
id='channelHeaderTitle'
className='heading'
ref={headerItemRef}
>
<span>
{archivedIcon}
{channelTitle}
{sharedIcon}
</span>
</strong>
</WithTooltip>
) : (
<strong
role='heading'
aria-level={2}
id='channelHeaderTitle'
className='heading'
ref={headerItemRef}
>
<span>
{archivedIcon}
{channelTitle}
{sharedIcon}
</span>
</strong>
)}
<span
id='channelHeaderDropdownIcon'
className='icon icon-chevron-down header-dropdown-chevron-icon'
@@ -120,8 +180,7 @@ const ChannelHeaderTitle = ({
</div>
<ChannelHeaderDropdown/>
</MenuWrapper>
<ChannelHeaderTitleFavorite/>
</React.Fragment>
</div>
);
};

Просмотреть файл

@@ -73,7 +73,7 @@ const ChannelHeaderTitleFavorite = () => {
id='toggleFavorite'
ref={toggleFavoriteRef}
onClick={toggleFavoriteCallback}
className={classNames('style--none color--link channel-header__favorites', {active: isFavorite, inactive: !isFavorite})}
className={classNames('channel-header__favorites btn btn-icon btn-xs', {active: isFavorite, inactive: !isFavorite})}
aria-label={ariaLabel}
>
<i className={classNames('icon', {'icon-star': isFavorite, 'icon-star-outline': !isFavorite})}/>