/* * Wire * Copyright (C) 2018 Wire Swiss GmbH * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/. * */ import React, { useEffect, useMemo, useRef, useState, MouseEvent as ReactMouseEvent, KeyboardEvent as ReactKeyBoardEvent, } from 'react'; import {TabIndex} from '@wireapp/react-ui-kit/lib/types/enums'; import cx from 'classnames'; import {Avatar, AVATAR_SIZE, GroupAvatar} from 'Components/Avatar'; import {UserBlockedBadge} from 'Components/Badge'; import {UserInfo} from 'Components/UserInfo'; import {useKoSubscribableChildren} from 'Util/ComponentUtil'; import {isKey, isOneOfKeys, KEY} from 'Util/KeyboardUtil'; import {t} from 'Util/LocalizerUtil'; import {noop, setContextMenuPosition} from 'Util/util'; import {StatusIcon} from './components/StatusIcon'; import {generateCellState} from '../../conversation/ConversationCellState'; import type {Conversation} from '../../entity/Conversation'; import {MediaType} from '../../media/MediaType'; export interface ConversationListCellProps { conversation: Conversation; dataUieName: string; isSelected?: (conversation: Conversation) => boolean; onClick: (event: ReactMouseEvent | ReactKeyBoardEvent) => void; onJoinCall: (conversation: Conversation, mediaType: MediaType) => void; rightClick: (conversation: Conversation, event: MouseEvent | React.MouseEvent) => void; showJoinButton: boolean; handleArrowKeyDown: (e: React.KeyboardEvent) => void; isFocused?: boolean; // This method resetting the current focused conversation to first conversation on click outside or click tab or shift + tab resetConversationFocus: () => void; } export const ConversationListCell = ({ showJoinButton, conversation, onJoinCall, onClick = noop, isSelected = () => false, rightClick = noop, dataUieName, handleArrowKeyDown, isFocused = false, resetConversationFocus, }: ConversationListCellProps) => { const { isGroup, is1to1, participating_user_ets: users, display_name: displayName, isSelfUserRemoved, unreadState, mutedState, isRequest, isConversationWithBlockedUser, } = useKoSubscribableChildren(conversation, [ 'isGroup', 'is1to1', 'participating_user_ets', 'display_name', 'isSelfUserRemoved', 'unreadState', 'mutedState', 'isRequest', 'isConversationWithBlockedUser', ]); const isActive = isSelected(conversation); const conversationRef = useRef(null); const contextMenuRef = useRef(null); const [focusContextMenu, setContextMenuFocus] = useState(false); const [isContextMenuOpen, setContextMenuOpen] = useState(false); const contextMenuKeyboardShortcut = `keyboard-shortcut-${conversation.id}`; const openContextMenu = (event: MouseEvent | React.MouseEvent) => { event.stopPropagation(); event.preventDefault(); rightClick(conversation, event); }; const cellState = useMemo(() => generateCellState(conversation), [unreadState, mutedState, isRequest]); const onClickJoinCall = (event: React.MouseEvent) => { event.preventDefault(); onJoinCall(conversation, MediaType.AUDIO); }; const handleDivKeyDown = (event: React.KeyboardEvent) => { if (event.key === KEY.SPACE || event.key === KEY.ENTER) { onClick(event); } else if (isKey(event, KEY.ARROW_RIGHT)) { setContextMenuFocus(true); contextMenuRef.current?.focus(); } else { setContextMenuFocus(false); } handleArrowKeyDown(event); }; const handleContextKeyDown = (event: React.KeyboardEvent) => { if (event.key === KEY.SPACE || event.key === KEY.ENTER) { const newEvent = setContextMenuPosition(event); rightClick(conversation, newEvent); setContextMenuOpen(true); return; } if (event.key === KEY.TAB || (event.shiftKey && event.key === KEY.TAB)) { resetConversationFocus(); } setContextMenuFocus(false); setContextMenuOpen(false); // when focused on the context menu and the menu is closed pressing up/down arrow keys will // get the focus back to the conversation list items if (isOneOfKeys(event, [KEY.ARROW_UP, KEY.ARROW_DOWN]) && !isContextMenuOpen) { handleArrowKeyDown(event); } }; // always focus on the selected conversation when the folder tab loaded useEffect(() => { if (isFocused) { conversationRef.current?.focus(); } }, [isFocused]); return (
  • { event.stopPropagation(); event.preventDefault(); onClick(event); }} onKeyDown={handleDivKeyDown} data-uie-name="go-open-conversation" tabIndex={isFocused ? TabIndex.FOCUSABLE : TabIndex.UNFOCUSABLE} aria-label={t('accessibility.openConversation', displayName)} aria-describedby={contextMenuKeyboardShortcut} >
    {isGroup && } {!isGroup && !!users.length && }
    {is1to1 ? ( {isConversationWithBlockedUser && } ) : ( {displayName} )} {cellState.description && ( {cellState.description} )}
    )}
  • ); };