import React from 'react'; import classNames from 'classnames'; import { AutoSizer, List } from 'react-virtualized'; import { ConversationListItem, PropsData as ConversationListItemPropsType, } from './ConversationListItem'; import { PropsData as SearchResultsProps, SearchResults, } from './SearchResults'; import { LocalizerType } from '../types/Util'; import { LeftPaneSections } from './LeftPaneSections'; export interface Props { conversations?: Array; friends?: Array; archivedConversations?: Array; searchResults?: SearchResultsProps; showArchived?: boolean; i18n: LocalizerType; // Action Creators startNewConversation: ( query: string, options: { regionCode: string } ) => void; openConversationInternal: (id: string, messageId?: string) => void; showArchivedConversations: () => void; showInbox: () => void; // Render Props renderMainHeader: () => JSX.Element; } // from https://github.com/bvaughn/react-virtualized/blob/fb3484ed5dcc41bffae8eab029126c0fb8f7abc0/source/List/types.js#L5 type RowRendererParamsType = { index: number; isScrolling: boolean; isVisible: boolean; key: string; parent: Object; style: Object; }; export class LeftPane extends React.Component { public state = { currentTab: 'conversations', }; public getCurrentConversations(): | Array | undefined { const { conversations, friends } = this.props; const { currentTab } = this.state; let conversationList = currentTab === 'conversations' ? conversations : friends; if (conversationList !== undefined) { conversationList = conversationList.filter( conversation => !conversation.isSecondary ); } return conversationList; } public renderTabs(): JSX.Element { const { i18n } = this.props; const { currentTab } = this.state; const tabs = [ { id: 'conversations', name: i18n('conversationsTab'), }, { id: 'friends', name: i18n('friendsTab'), }, ]; return (
{tabs.map(tab => (
{ this.setState({ currentTab: tab.id }); }} > {tab.name}
))}
); } public renderRow = ({ index, key, style, }: RowRendererParamsType): JSX.Element => { const { archivedConversations, i18n, openConversationInternal, showArchived, } = this.props; const { currentTab } = this.state; const conversations = this.getCurrentConversations(); if (!conversations || !archivedConversations) { throw new Error( 'renderRow: Tried to render without conversations or archivedConversations' ); } if (!showArchived && index === conversations.length) { return this.renderArchivedButton({ key, style }); } const conversation = showArchived ? archivedConversations[index] : conversations[index]; return ( ); }; public renderArchivedButton({ key, style, }: { key: string; style: Object; }): JSX.Element { const { archivedConversations, i18n, showArchivedConversations, } = this.props; if (!archivedConversations || !archivedConversations.length) { throw new Error( 'renderArchivedButton: Tried to render without archivedConversations' ); } return (
{i18n('archivedConversations')}{' '} {archivedConversations.length}
); } public renderList(): JSX.Element | Array { const { archivedConversations, i18n, openConversationInternal, startNewConversation, searchResults, showArchived, } = this.props; if (searchResults) { return ( ); } const conversations = this.getCurrentConversations(); if (!conversations || !archivedConversations) { throw new Error( 'render: must provided conversations and archivedConverstions if no search results are provided' ); } // That extra 1 element added to the list is the 'archived converastions' button const length = showArchived ? archivedConversations.length : conversations.length + (archivedConversations.length ? 1 : 0); const archived = showArchived ? (
{i18n('archiveHelperText')}
) : null; // We ensure that the listKey differs between inbox and archive views, which ensures // that AutoSizer properly detects the new size of its slot in the flexbox. The // archive explainer text at the top of the archive view causes problems otherwise. // It also ensures that we scroll to the top when switching views. const listKey = showArchived ? 1 : 0; // Note: conversations is not a known prop for List, but it is required to ensure that // it re-renders when our conversation data changes. Otherwise it would just render // on startup and scroll. const list = (
{({ height, width }) => ( )}
); return [this.renderTabs(), archived, list]; } public renderArchivedHeader(): JSX.Element { const { i18n, showInbox } = this.props; return (
{i18n('archivedConversations')}
); } public render(): JSX.Element { const { renderMainHeader, showArchived } = this.props; return (
{showArchived ? this.renderArchivedHeader() : renderMainHeader()}
{this.renderList()}
); } }