signal-desktop/ts/components/LeftPane.tsx

229 lines
5.8 KiB
TypeScript
Raw Normal View History

2019-01-14 21:49:58 +00:00
import React from 'react';
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;
}
// from https://github.com/bvaughn/react-virtualized/blob/fb3484ed5dcc41bffae8eab029126c0fb8f7abc0/source/List/types.js#L5
2019-03-12 00:20:16 +00:00
type RowRendererParamsType = {
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-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];
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,
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-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);
// 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}
<AutoSizer>
{({ height, width }) => (
<List
className="module-left-pane__virtual-list"
2019-03-12 00:20:16 +00:00
ref={this.listRef}
conversations={conversations}
height={height}
2019-03-12 00:20:16 +00:00
rowCount={length}
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>
);
}
}