Archive Conversation
This commit is contained in:
parent
d72f89d776
commit
6ffbc0ac06
20 changed files with 568 additions and 109 deletions
|
@ -129,8 +129,14 @@ window.searchResults.messages = [
|
|||
<util.LeftPaneContext theme={util.theme} style={{ height: '200px' }}>
|
||||
<LeftPane
|
||||
searchResults={window.searchResults}
|
||||
openConversation={result => console.log('openConversation', result)}
|
||||
openMessage={result => console.log('onClickMessage', result)}
|
||||
startNewConversation={(query, options) =>
|
||||
console.log('startNewConversation', query, options)
|
||||
}
|
||||
openConversationInternal={(id, messageId) =>
|
||||
console.log('openConversation', id, messageId)
|
||||
}
|
||||
showArchivedConversations={() => console.log('showArchivedConversations')}
|
||||
showInbox={() => console.log('showInbox')}
|
||||
renderMainHeader={() => (
|
||||
<MainHeader
|
||||
searchTerm="Hi there!"
|
||||
|
@ -151,8 +157,74 @@ window.searchResults.messages = [
|
|||
<util.LeftPaneContext theme={util.theme} style={{ height: '200px' }}>
|
||||
<LeftPane
|
||||
conversations={window.searchResults.conversations}
|
||||
openConversation={result => console.log('openConversation', result)}
|
||||
openMessage={result => console.log('onClickMessage', result)}
|
||||
archivedConversations={[]}
|
||||
startNewConversation={(query, options) =>
|
||||
console.log('startNewConversation', query, options)
|
||||
}
|
||||
openConversationInternal={(id, messageId) =>
|
||||
console.log('openConversation', id, messageId)
|
||||
}
|
||||
showArchivedConversations={() => console.log('showArchivedConversations')}
|
||||
showInbox={() => console.log('showInbox')}
|
||||
renderMainHeader={() => (
|
||||
<MainHeader
|
||||
searchTerm="Hi there!"
|
||||
search={result => console.log('search', result)}
|
||||
updateSearch={result => console.log('updateSearch', result)}
|
||||
clearSearch={result => console.log('clearSearch', result)}
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
)}
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
</util.LeftPaneContext>
|
||||
```
|
||||
|
||||
#### Showing inbox, with some archived
|
||||
|
||||
```jsx
|
||||
<util.LeftPaneContext theme={util.theme} style={{ height: '200px' }}>
|
||||
<LeftPane
|
||||
conversations={window.searchResults.conversations.slice(0, 2)}
|
||||
archivedConversations={window.searchResults.conversations.slice(2)}
|
||||
startNewConversation={(query, options) =>
|
||||
console.log('startNewConversation', query, options)
|
||||
}
|
||||
openConversationInternal={(id, messageId) =>
|
||||
console.log('openConversation', id, messageId)
|
||||
}
|
||||
showArchivedConversations={() => console.log('showArchivedConversations')}
|
||||
showInbox={() => console.log('showInbox')}
|
||||
renderMainHeader={() => (
|
||||
<MainHeader
|
||||
searchTerm="Hi there!"
|
||||
search={result => console.log('search', result)}
|
||||
updateSearch={result => console.log('updateSearch', result)}
|
||||
clearSearch={result => console.log('clearSearch', result)}
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
)}
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
</util.LeftPaneContext>
|
||||
```
|
||||
|
||||
#### Showing archived conversations
|
||||
|
||||
```jsx
|
||||
<util.LeftPaneContext theme={util.theme} style={{ height: '200px' }}>
|
||||
<LeftPane
|
||||
conversations={window.searchResults.conversations.slice(0, 2)}
|
||||
archivedConversations={window.searchResults.conversations.slice(2)}
|
||||
showArchived={true}
|
||||
startNewConversation={(query, options) =>
|
||||
console.log('startNewConversation', query, options)
|
||||
}
|
||||
openConversationInternal={(id, messageId) =>
|
||||
console.log('openConversation', id, messageId)
|
||||
}
|
||||
showArchivedConversations={() => console.log('showArchivedConversations')}
|
||||
showInbox={() => console.log('showInbox')}
|
||||
renderMainHeader={() => (
|
||||
<MainHeader
|
||||
searchTerm="Hi there!"
|
||||
|
|
|
@ -13,19 +13,27 @@ import { LocalizerType } from '../types/Util';
|
|||
|
||||
export interface Props {
|
||||
conversations?: Array<ConversationListItemPropsType>;
|
||||
archivedConversations?: Array<ConversationListItemPropsType>;
|
||||
searchResults?: SearchResultsProps;
|
||||
showArchived?: boolean;
|
||||
|
||||
i18n: LocalizerType;
|
||||
|
||||
// Action Creators
|
||||
startNewConversation: () => void;
|
||||
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 RowRendererParams = {
|
||||
type RowRendererParamsType = {
|
||||
index: number;
|
||||
isScrolling: boolean;
|
||||
isVisible: boolean;
|
||||
|
@ -35,12 +43,51 @@ type RowRendererParams = {
|
|||
};
|
||||
|
||||
export class LeftPane extends React.Component<Props> {
|
||||
public renderRow = ({ index, key, style }: RowRendererParams) => {
|
||||
const { conversations, i18n, openConversationInternal } = this.props;
|
||||
if (!conversations) {
|
||||
return null;
|
||||
public listRef: React.RefObject<any> = React.createRef();
|
||||
|
||||
public scrollToTop() {
|
||||
if (this.listRef && this.listRef.current) {
|
||||
const { current } = this.listRef;
|
||||
current.scrollToRow(0);
|
||||
}
|
||||
const conversation = conversations[index];
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -53,13 +100,50 @@ export class LeftPane extends React.Component<Props> {
|
|||
);
|
||||
};
|
||||
|
||||
public renderList() {
|
||||
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 {
|
||||
const {
|
||||
archivedConversations,
|
||||
i18n,
|
||||
conversations,
|
||||
openConversationInternal,
|
||||
startNewConversation,
|
||||
searchResults,
|
||||
showArchived,
|
||||
} = this.props;
|
||||
|
||||
if (searchResults) {
|
||||
|
@ -73,22 +157,35 @@ export class LeftPane extends React.Component<Props> {
|
|||
);
|
||||
}
|
||||
|
||||
if (!conversations || !conversations.length) {
|
||||
return null;
|
||||
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);
|
||||
|
||||
// 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.
|
||||
return (
|
||||
<div className="module-left-pane__list">
|
||||
{showArchived ? (
|
||||
<div className="module-left-pane__archive-helper-text">
|
||||
{i18n('archiveHelperText')}
|
||||
</div>
|
||||
) : null}
|
||||
<AutoSizer>
|
||||
{({ height, width }) => (
|
||||
<List
|
||||
className="module-left-pane__virtual-list"
|
||||
ref={this.listRef}
|
||||
conversations={conversations}
|
||||
height={height}
|
||||
rowCount={conversations.length}
|
||||
rowCount={length}
|
||||
rowHeight={64}
|
||||
rowRenderer={this.renderRow}
|
||||
width={width}
|
||||
|
@ -99,12 +196,31 @@ export class LeftPane extends React.Component<Props> {
|
|||
);
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { renderMainHeader } = this.props;
|
||||
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;
|
||||
|
||||
return (
|
||||
<div className="module-left-pane">
|
||||
<div className="module-left-pane__header">{renderMainHeader()}</div>
|
||||
<div className="module-left-pane__header">
|
||||
{showArchived ? this.renderArchivedHeader() : renderMainHeader()}
|
||||
</div>
|
||||
{this.renderList()}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -113,7 +113,9 @@ window.searchResults.messages = [
|
|||
i18n={util.i18n}
|
||||
onClickMessage={id => console.log('onClickMessage', id)}
|
||||
onClickConversation={id => console.log('onClickConversation', id)}
|
||||
onStartNewConversation={() => console.log('onStartNewConversation')}
|
||||
onStartNewConversation={(query, options) =>
|
||||
console.log('onStartNewConversation', query, options)
|
||||
}
|
||||
/>
|
||||
</util.LeftPaneContext>;
|
||||
```
|
||||
|
@ -131,7 +133,9 @@ window.searchResults.messages = [
|
|||
i18n={util.i18n}
|
||||
onClickMessage={id => console.log('onClickMessage', id)}
|
||||
onClickConversation={id => console.log('onClickConversation', id)}
|
||||
onStartNewConversation={() => console.log('onStartNewConversation')}
|
||||
onStartNewConversation={(query, options) =>
|
||||
console.log('onStartNewConversation', query, options)
|
||||
}
|
||||
/>
|
||||
</util.LeftPaneContext>
|
||||
```
|
||||
|
@ -147,7 +151,9 @@ window.searchResults.messages = [
|
|||
i18n={util.i18n}
|
||||
onClickMessage={id => console.log('onClickMessage', id)}
|
||||
onClickConversation={id => console.log('onClickConversation', id)}
|
||||
onStartNewConversation={() => console.log('onStartNewConversation')}
|
||||
onStartNewConversation={(query, options) =>
|
||||
console.log('onStartNewConversation', query, options)
|
||||
}
|
||||
/>
|
||||
</util.LeftPaneContext>
|
||||
```
|
||||
|
@ -163,7 +169,9 @@ window.searchResults.messages = [
|
|||
i18n={util.i18n}
|
||||
onClickMessage={id => console.log('onClickMessage', id)}
|
||||
onClickConversation={id => console.log('onClickConversation', id)}
|
||||
onStartNewConversation={() => console.log('onStartNewConversation')}
|
||||
onStartNewConversation={(query, options) =>
|
||||
console.log('onStartNewConversation', query, options)
|
||||
}
|
||||
/>
|
||||
</util.LeftPaneContext>
|
||||
```
|
||||
|
|
|
@ -16,6 +16,7 @@ export type PropsData = {
|
|||
conversations: Array<ConversationListItemPropsType>;
|
||||
hideMessagesHeader: boolean;
|
||||
messages: Array<MessageSearchResultPropsType>;
|
||||
regionCode: string;
|
||||
searchTerm: string;
|
||||
showStartNewConversation: boolean;
|
||||
};
|
||||
|
@ -23,12 +24,21 @@ export type PropsData = {
|
|||
type PropsHousekeeping = {
|
||||
i18n: LocalizerType;
|
||||
openConversation: (id: string, messageId?: string) => void;
|
||||
startNewConversation: (id: string) => void;
|
||||
startNewConversation: (
|
||||
query: string,
|
||||
options: { regionCode: string }
|
||||
) => void;
|
||||
};
|
||||
|
||||
type Props = PropsData & PropsHousekeeping;
|
||||
|
||||
export class SearchResults extends React.Component<Props> {
|
||||
public handleStartNewConversation = () => {
|
||||
const { regionCode, searchTerm, startNewConversation } = this.props;
|
||||
|
||||
startNewConversation(searchTerm, { regionCode });
|
||||
};
|
||||
|
||||
public render() {
|
||||
const {
|
||||
conversations,
|
||||
|
@ -37,7 +47,6 @@ export class SearchResults extends React.Component<Props> {
|
|||
i18n,
|
||||
messages,
|
||||
openConversation,
|
||||
startNewConversation,
|
||||
searchTerm,
|
||||
showStartNewConversation,
|
||||
} = this.props;
|
||||
|
@ -62,7 +71,7 @@ export class SearchResults extends React.Component<Props> {
|
|||
<StartNewConversation
|
||||
phoneNumber={searchTerm}
|
||||
i18n={i18n}
|
||||
onClick={startNewConversation}
|
||||
onClick={this.handleStartNewConversation}
|
||||
/>
|
||||
) : null}
|
||||
{haveConversations ? (
|
||||
|
|
|
@ -7,7 +7,7 @@ import { LocalizerType } from '../types/Util';
|
|||
export interface Props {
|
||||
phoneNumber: string;
|
||||
i18n: LocalizerType;
|
||||
onClick: (id: string) => void;
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
export class StartNewConversation extends React.PureComponent<Props> {
|
||||
|
@ -18,9 +18,7 @@ export class StartNewConversation extends React.PureComponent<Props> {
|
|||
<div
|
||||
role="button"
|
||||
className="module-start-new-conversation"
|
||||
onClick={() => {
|
||||
onClick(phoneNumber);
|
||||
}}
|
||||
onClick={onClick}
|
||||
>
|
||||
<Avatar
|
||||
color="grey"
|
||||
|
|
|
@ -16,17 +16,19 @@ interface TimerOption {
|
|||
}
|
||||
|
||||
interface Props {
|
||||
i18n: LocalizerType;
|
||||
isVerified: boolean;
|
||||
name?: string;
|
||||
id: string;
|
||||
name?: string;
|
||||
|
||||
phoneNumber: string;
|
||||
profileName?: string;
|
||||
color: string;
|
||||
|
||||
avatarPath?: string;
|
||||
|
||||
isVerified: boolean;
|
||||
isMe: boolean;
|
||||
isGroup: boolean;
|
||||
isArchived: boolean;
|
||||
|
||||
expirationSettingName?: string;
|
||||
showBackButton: boolean;
|
||||
timerOptions: Array<TimerOption>;
|
||||
|
@ -39,6 +41,11 @@ interface Props {
|
|||
onShowAllMedia: () => void;
|
||||
onShowGroupMembers: () => void;
|
||||
onGoBack: () => void;
|
||||
|
||||
onArchive: () => void;
|
||||
onMoveToInbox: () => void;
|
||||
|
||||
i18n: LocalizerType;
|
||||
}
|
||||
|
||||
export class ConversationHeader extends React.Component<Props> {
|
||||
|
@ -184,12 +191,15 @@ export class ConversationHeader extends React.Component<Props> {
|
|||
i18n,
|
||||
isMe,
|
||||
isGroup,
|
||||
isArchived,
|
||||
onDeleteMessages,
|
||||
onResetSession,
|
||||
onSetDisappearingMessages,
|
||||
onShowAllMedia,
|
||||
onShowGroupMembers,
|
||||
onShowSafetyNumber,
|
||||
onArchive,
|
||||
onMoveToInbox,
|
||||
timerOptions,
|
||||
} = this.props;
|
||||
|
||||
|
@ -223,6 +233,13 @@ export class ConversationHeader extends React.Component<Props> {
|
|||
{!isGroup ? (
|
||||
<MenuItem onClick={onResetSession}>{i18n('resetSession')}</MenuItem>
|
||||
) : null}
|
||||
{isArchived ? (
|
||||
<MenuItem onClick={onMoveToInbox}>
|
||||
{i18n('moveConversationToInbox')}
|
||||
</MenuItem>
|
||||
) : (
|
||||
<MenuItem onClick={onArchive}>{i18n('archiveConversation')}</MenuItem>
|
||||
)}
|
||||
<MenuItem onClick={onDeleteMessages}>{i18n('deleteMessages')}</MenuItem>
|
||||
</ContextMenu>
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue