Skip to content

Commit

Permalink
Add action buttons component to ChannelPreview
Browse files Browse the repository at this point in the history
  • Loading branch information
arnautov-anton committed Dec 5, 2024
1 parent b38d838 commit 3642fbb
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 40 deletions.
55 changes: 55 additions & 0 deletions src/components/ChannelPreview/ChannelPreviewActionButtons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import React from 'react';
import clsx from 'clsx';
import type { Channel, ExtendableGenerics } from 'stream-chat';

import { useChannelMembershipState } from '../ChannelList';
import { Icon } from './icons';

export type ChannelPreviewActionButtonsProps<SCG extends ExtendableGenerics> = {
channel: Channel<SCG>;
};

export function ChannelPreviewActionButtons<SCG extends ExtendableGenerics>({
channel,
}: ChannelPreviewActionButtonsProps<SCG>) {
const membership = useChannelMembershipState(channel);

return (
<div className='str-chat__channel-preview__action-buttons'>
<button
className={clsx(
'str-chat__channel-preview__action-button',
'str-chat__channel-preview__action-button--pin',
membership.pinned_at && 'str-chat__channel-preview__action-button--active',
)}
onClick={(e) => {
e.stopPropagation();
if (membership.pinned_at) {
channel.unpin();
} else {
channel.pin();
}
}}
>
<Icon.Pin />
</button>
<button
className={clsx(
'str-chat__channel-preview__action-button',
'str-chat__channel-preview__action-button--archive',
membership.archived_at && 'str-chat__channel-preview__action-button--active',
)}
onClick={(e) => {
e.stopPropagation();
if (membership.archived_at) {
channel.unarchive();
} else {
channel.archive();
}
}}
>
<Icon.ArchiveBox />
</button>
</div>
);
}
89 changes: 49 additions & 40 deletions src/components/ChannelPreview/ChannelPreviewMessenger.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import React, { useRef } from 'react';
import clsx from 'clsx';
import { Avatar as DefaultAvatar } from '../Avatar';

import type { ChannelPreviewUIComponentProps } from './ChannelPreview';
import { ChannelPreviewActionButtons as DefaultChannelPreviewActionButtons } from './ChannelPreviewActionButtons';
import { Avatar as DefaultAvatar } from '../Avatar';
import { useComponentContext } from '../../context';
import type { DefaultStreamChatGenerics } from '../../types/types';
import type { ChannelPreviewUIComponentProps } from './ChannelPreview';

const UnMemoizedChannelPreviewMessenger = <
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics
SCG extends DefaultStreamChatGenerics = DefaultStreamChatGenerics
>(
props: ChannelPreviewUIComponentProps<StreamChatGenerics>,
props: ChannelPreviewUIComponentProps<SCG>,
) => {
const {
active,
Expand All @@ -25,6 +27,10 @@ const UnMemoizedChannelPreviewMessenger = <
watchers,
} = props;

const {
ChannelPreviewActionButtons = DefaultChannelPreviewActionButtons,
} = useComponentContext<SCG>();

const channelPreviewButton = useRef<HTMLButtonElement | null>(null);

const avatarName =
Expand All @@ -42,44 +48,47 @@ const UnMemoizedChannelPreviewMessenger = <
};

return (
<button
aria-label={`Select Channel: ${displayTitle || ''}`}
aria-selected={active}
className={clsx(
`str-chat__channel-preview-messenger str-chat__channel-preview`,
active && 'str-chat__channel-preview-messenger--active',
unread && unread >= 1 && 'str-chat__channel-preview-messenger--unread',
customClassName,
)}
data-testid='channel-preview-button'
onClick={onSelectChannel}
ref={channelPreviewButton}
role='option'
>
<div className='str-chat__channel-preview-messenger--left'>
<Avatar
className='str-chat__avatar--channel-preview'
groupChannelDisplayInfo={groupChannelDisplayInfo}
image={displayImage}
name={avatarName}
/>
</div>
<div className='str-chat__channel-preview-end'>
<div className='str-chat__channel-preview-end-first-row'>
<div className='str-chat__channel-preview-messenger--name'>
<span>{displayTitle}</span>
</div>
{!!unread && (
<div className='str-chat__channel-preview-unread-badge' data-testid='unread-badge'>
{unread}
</div>
)}
<div className='str-chat__channel-preview-container'>
<ChannelPreviewActionButtons channel={channel} />
<button
aria-label={`Select Channel: ${displayTitle || ''}`}
aria-selected={active}
className={clsx(
`str-chat__channel-preview-messenger str-chat__channel-preview`,
active && 'str-chat__channel-preview-messenger--active',
unread && unread >= 1 && 'str-chat__channel-preview-messenger--unread',
customClassName,
)}
data-testid='channel-preview-button'
onClick={onSelectChannel}
ref={channelPreviewButton}
role='option'
>
<div className='str-chat__channel-preview-messenger--left'>
<Avatar
className='str-chat__avatar--channel-preview'
groupChannelDisplayInfo={groupChannelDisplayInfo}
image={displayImage}
name={avatarName}
/>
</div>
<div className='str-chat__channel-preview-messenger--last-message'>
{latestMessagePreview}
<div className='str-chat__channel-preview-end'>
<div className='str-chat__channel-preview-end-first-row'>
<div className='str-chat__channel-preview-messenger--name'>
<span>{displayTitle}</span>
</div>
{!!unread && (
<div className='str-chat__channel-preview-unread-badge' data-testid='unread-badge'>
{unread}
</div>
)}
</div>
<div className='str-chat__channel-preview-messenger--last-message'>
{latestMessagePreview}
</div>
</div>
</div>
</button>
</button>
</div>
);
};

Expand Down
28 changes: 28 additions & 0 deletions src/components/ChannelPreview/icons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* eslint-disable react/display-name */
import React from 'react';
import { ComponentPropsWithoutRef } from 'react';

export const Icon = {
ArchiveBox: (props: ComponentPropsWithoutRef<'svg'>) => (
<svg
className='str-chat__icon str-chat__icon--archive-box'
fill='currentColor'
viewBox='0 0 512 512'
xmlns='http://www.w3.org/2000/svg'
{...props}
>
<path d='M32 32l448 0c17.7 0 32 14.3 32 32l0 32c0 17.7-14.3 32-32 32L32 128C14.3 128 0 113.7 0 96L0 64C0 46.3 14.3 32 32 32zm0 128l448 0 0 256c0 35.3-28.7 64-64 64L96 480c-35.3 0-64-28.7-64-64l0-256zm128 80c0 8.8 7.2 16 16 16l160 0c8.8 0 16-7.2 16-16s-7.2-16-16-16l-160 0c-8.8 0-16 7.2-16 16z' />
</svg>
),
Pin: (props: ComponentPropsWithoutRef<'svg'>) => (
<svg
className='str-chat__icon str-chat__icon--pin'
fill='currentColor'
viewBox='0 0 384 512'
xmlns='http://www.w3.org/2000/svg'
{...props}
>
<path d='M32 32C32 14.3 46.3 0 64 0L320 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-29.5 0 11.4 148.2c36.7 19.9 65.7 53.2 79.5 94.7l1 3c3.3 9.8 1.6 20.5-4.4 28.8s-15.7 13.3-26 13.3L32 352c-10.3 0-19.9-4.9-26-13.3s-7.7-19.1-4.4-28.8l1-3c13.8-41.5 42.8-74.8 79.5-94.7L93.5 64 64 64C46.3 64 32 49.7 32 32zM160 384l64 0 0 96c0 17.7-14.3 32-32 32s-32-14.3-32-32l0-96z' />
</svg>
),
};
1 change: 1 addition & 0 deletions src/components/ChannelPreview/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './ChannelPreview';
export * from './ChannelPreviewMessenger';
export * from './ChannelPreviewActionButtons';
export * from './hooks';
export * from './utils';
5 changes: 5 additions & 0 deletions src/context/ComponentContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
AttachmentProps,
AvatarProps,
BaseImageProps,
ChannelPreviewActionButtonsProps,
CooldownTimerProps,
CustomMessageActionsListProps,
DateSeparatorProps,
Expand Down Expand Up @@ -79,6 +80,10 @@ export type ComponentContextValue<
Avatar?: React.ComponentType<AvatarProps<StreamChatGenerics>>;
/** Custom UI component to display <img/> elements resp. a fallback in case of load error, defaults to and accepts same props as: [BaseImage](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Gallery/BaseImage.tsx) */
BaseImage?: React.ComponentType<BaseImageProps>;
/** Custom UI component to display set of action buttons within `ChannelPreviewMessenger` component, accepts same props as: [ChannelPreviewActionButtons](https://github.com/GetStream/stream-chat-react/blob/master/src/components/ChannelList/ChannelPreviewActionButtons.tsx) */
ChannelPreviewActionButtons?: React.ComponentType<
ChannelPreviewActionButtonsProps<StreamChatGenerics>
>;
/** Custom UI component to display the slow mode cooldown timer, defaults to and accepts same props as: [CooldownTimer](https://github.com/GetStream/stream-chat-react/blob/master/src/components/MessageInput/CooldownTimer.tsx) */
CooldownTimer?: React.ComponentType<CooldownTimerProps>;
/** Custom UI component to render set of buttons to be displayed in the MessageActionsBox, defaults to and accepts same props as: [CustomMessageActionsList](https://github.com/GetStream/stream-chat-react/blob/master/src/components/MessageActions/CustomMessageActionsList.tsx) */
Expand Down

0 comments on commit 3642fbb

Please sign in to comment.