2019-01-14 21:49:58 +00:00
|
|
|
import React from 'react';
|
2019-03-09 02:25:28 +00:00
|
|
|
import { AutoSizer, List } from 'react-virtualized';
|
2019-01-14 21:49:58 +00:00
|
|
|
|
|
|
|
import {
|
|
|
|
ConversationListItem,
|
|
|
|
PropsData as ConversationListItemPropsType,
|
|
|
|
} from './ConversationListItem';
|
|
|
|
import {
|
|
|
|
PropsData as SearchResultsProps,
|
|
|
|
SearchResults,
|
|
|
|
} from './SearchResults';
|
|
|
|
import { LocalizerType } from '../types/Util';
|
|
|
|
|
|
|
|
export interface Props {
|
|
|
|
conversations?: Array<ConversationListItemPropsType>;
|
2019-03-12 00:20:16 +00:00
|
|
|
archivedConversations?: Array<ConversationListItemPropsType>;
|
2019-01-14 21:49:58 +00:00
|
|
|
searchResults?: SearchResultsProps;
|
2019-03-12 00:20:16 +00:00
|
|
|
showArchived?: boolean;
|
|
|
|
|
2019-01-14 21:49:58 +00:00
|
|
|
i18n: LocalizerType;
|
|
|
|
|
|
|
|
// Action Creators
|
2019-03-12 00:20:16 +00:00
|
|
|
startNewConversation: (
|
|
|
|
query: string,
|
|
|
|
options: { regionCode: string }
|
|
|
|
) => void;
|
2019-01-14 21:49:58 +00:00
|
|
|
openConversationInternal: (id: string, messageId?: string) => void;
|
2019-03-12 00:20:16 +00:00
|
|
|
showArchivedConversations: () => void;
|
|
|
|
showInbox: () => void;
|
2019-01-14 21:49:58 +00:00
|
|
|
|
|
|
|
// Render Props
|
|
|
|
renderMainHeader: () => JSX.Element;
|
|
|
|
}
|
|
|
|
|
2019-03-09 02:25:28 +00:00
|
|
|
// from https://github.com/bvaughn/react-virtualized/blob/fb3484ed5dcc41bffae8eab029126c0fb8f7abc0/source/List/types.js#L5
|
2019-03-12 00:20:16 +00:00
|
|
|
type RowRendererParamsType = {
|
2019-03-09 02:25:28 +00:00
|
|
|
index: number;
|
|
|
|
isScrolling: boolean;
|
|
|
|
isVisible: boolean;
|
|
|
|
key: string;
|
|
|
|
parent: Object;
|
|
|
|
style: Object;
|
|
|
|
};
|
|
|
|
|
2019-01-14 21:49:58 +00:00
|
|
|
export class LeftPane extends React.Component<Props> {
|
2019-03-12 00:20:16 +00:00
|
|
|
public listRef: React.RefObject<any> = React.createRef();
|
|
|
|
|
|
|
|
public scrollToTop() {
|
|
|
|
if (this.listRef && this.listRef.current) {
|
|
|
|
const { current } = this.listRef;
|
|
|
|
current.scrollToRow(0);
|
2019-03-09 02:25:28 +00:00
|
|
|
}
|
2019-03-12 00:20:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public componentDidUpdate(prevProps: Props) {
|
|
|
|
const { showArchived, searchResults } = this.props;
|
|
|
|
|
|
|
|
const isNotShowingSearchResults = !searchResults;
|
|
|
|
const hasArchiveViewChanged = showArchived !== prevProps.showArchived;
|
|
|
|
|
|
|
|
if (isNotShowingSearchResults && hasArchiveViewChanged) {
|
|
|
|
this.scrollToTop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public renderRow = ({
|
|
|
|
index,
|
|
|
|
key,
|
|
|
|
style,
|
|
|
|
}: RowRendererParamsType): JSX.Element => {
|
|
|
|
const {
|
|
|
|
archivedConversations,
|
|
|
|
conversations,
|
|
|
|
i18n,
|
|
|
|
openConversationInternal,
|
|
|
|
showArchived,
|
|
|
|
} = this.props;
|
|
|
|
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];
|
2019-03-09 02:25:28 +00:00
|
|
|
|
|
|
|
return (
|
|
|
|
<ConversationListItem
|
|
|
|
key={key}
|
|
|
|
style={style}
|
|
|
|
{...conversation}
|
|
|
|
onClick={openConversationInternal}
|
|
|
|
i18n={i18n}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2019-03-12 00:20:16 +00:00
|
|
|
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 (
|
|
|
|
<div
|
|
|
|
key={key}
|
|
|
|
className="module-left-pane__archived-button"
|
|
|
|
style={style}
|
|
|
|
role="button"
|
|
|
|
onClick={showArchivedConversations}
|
|
|
|
>
|
|
|
|
{i18n('archivedConversations')}{' '}
|
|
|
|
<span className="module-left-pane__archived-button__archived-count">
|
|
|
|
{archivedConversations.length}
|
|
|
|
</span>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
public renderList(): JSX.Element {
|
2019-01-14 21:49:58 +00:00
|
|
|
const {
|
2019-03-12 00:20:16 +00:00
|
|
|
archivedConversations,
|
2019-01-14 21:49:58 +00:00
|
|
|
i18n,
|
2019-03-09 02:25:28 +00:00
|
|
|
conversations,
|
2019-01-14 21:49:58 +00:00
|
|
|
openConversationInternal,
|
|
|
|
startNewConversation,
|
|
|
|
searchResults,
|
2019-03-12 00:20:16 +00:00
|
|
|
showArchived,
|
2019-01-14 21:49:58 +00:00
|
|
|
} = this.props;
|
|
|
|
|
|
|
|
if (searchResults) {
|
|
|
|
return (
|
|
|
|
<SearchResults
|
|
|
|
{...searchResults}
|
|
|
|
openConversation={openConversationInternal}
|
|
|
|
startNewConversation={startNewConversation}
|
|
|
|
i18n={i18n}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-03-12 00:20:16 +00:00
|
|
|
if (!conversations || !archivedConversations) {
|
|
|
|
throw new Error(
|
|
|
|
'render: must provided conversations and archivedConverstions if no search results are provided'
|
|
|
|
);
|
2019-03-09 02:25:28 +00:00
|
|
|
}
|
|
|
|
|
2019-03-12 00:20:16 +00:00
|
|
|
// That extra 1 element added to the list is the 'archived converastions' button
|
|
|
|
const length = showArchived
|
|
|
|
? archivedConversations.length
|
|
|
|
: conversations.length + (archivedConversations.length ? 1 : 0);
|
|
|
|
|
2019-03-09 02:25:28 +00:00
|
|
|
// 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.
|
2019-01-14 21:49:58 +00:00
|
|
|
return (
|
|
|
|
<div className="module-left-pane__list">
|
2019-03-12 00:20:16 +00:00
|
|
|
{showArchived ? (
|
|
|
|
<div className="module-left-pane__archive-helper-text">
|
|
|
|
{i18n('archiveHelperText')}
|
|
|
|
</div>
|
|
|
|
) : null}
|
2019-03-09 02:25:28 +00:00
|
|
|
<AutoSizer>
|
|
|
|
{({ height, width }) => (
|
|
|
|
<List
|
|
|
|
className="module-left-pane__virtual-list"
|
2019-03-12 00:20:16 +00:00
|
|
|
ref={this.listRef}
|
2019-03-09 02:25:28 +00:00
|
|
|
conversations={conversations}
|
|
|
|
height={height}
|
2019-03-12 00:20:16 +00:00
|
|
|
rowCount={length}
|
2019-03-09 02:25:28 +00:00
|
|
|
rowHeight={64}
|
|
|
|
rowRenderer={this.renderRow}
|
|
|
|
width={width}
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
</AutoSizer>
|
2019-01-14 21:49:58 +00:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-03-12 00:20:16 +00:00
|
|
|
public renderArchivedHeader(): JSX.Element {
|
|
|
|
const { i18n, showInbox } = this.props;
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div className="module-left-pane__archive-header">
|
|
|
|
<div
|
|
|
|
role="button"
|
|
|
|
onClick={showInbox}
|
|
|
|
className="module-left-pane__to-inbox-button"
|
|
|
|
/>
|
|
|
|
<div className="module-left-pane__archive-header-text">
|
|
|
|
{i18n('archivedConversations')}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
public render(): JSX.Element {
|
|
|
|
const { renderMainHeader, showArchived } = this.props;
|
2019-01-14 21:49:58 +00:00
|
|
|
|
|
|
|
return (
|
|
|
|
<div className="module-left-pane">
|
2019-03-12 00:20:16 +00:00
|
|
|
<div className="module-left-pane__header">
|
|
|
|
{showArchived ? this.renderArchivedHeader() : renderMainHeader()}
|
|
|
|
</div>
|
2019-01-14 21:49:58 +00:00
|
|
|
{this.renderList()}
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|