add start of user search dropdown in compose view

pull/712/head
Audric Ackermann 5 years ago
parent 887ba53902
commit e4b36fe7f7

@ -1,3 +1,5 @@
$session-compose-margin: 20px;
.dark-theme {
.module-conversation-list-item {
@include session-dark-background-lighter;
@ -144,6 +146,7 @@
margin-left: -65px;
top: 50%;
margin-top: 6px;
border: none;
}
.white {
@ -161,7 +164,11 @@
}
.session-search-input {
margin: 10px 20px;
margin: 10px $session-compose-margin 0 $session-compose-margin;
width: -webkit-fill-available;
}
.module-search-results {
width: -webkit-fill-available;
}
@ -219,3 +226,23 @@
.app-loading-screen {
@include session-dark-background;
}
.contacts-dropdown {
width: -webkit-fill-available;
&-row {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
padding: 5px 20px;
margin: 0px $session-compose-margin;
background: $session-shade-4;
color: $session-color-light-grey;
&-selected,
&:hover {
font-weight: bold;
color: $session-color-white;
}
}
}

@ -0,0 +1,73 @@
import React from 'react';
import { PropsData as ConversationListItemPropsType } from './ConversationListItem';
import { LocalizerType } from '../types/Util';
import classNames from 'classnames';
export type PropsData = {
contacts: Array<ConversationListItemPropsType>;
regionCode: string;
searchTerm: string;
selectedContact: Number;
onContactSelected: any;
};
type PropsHousekeeping = {
i18n: LocalizerType;
};
type Props = PropsData & PropsHousekeeping;
export class UserSearchResults extends React.Component<Props> {
public constructor(props: Props) {
super(props);
}
public render() {
const { contacts, i18n, searchTerm } = this.props;
const haveContacts = contacts && contacts.length;
const noResults = !haveContacts;
return (
<div className="module-search-results">
{noResults ? (
<div className="module-search-results__no-results">
{i18n('noSearchResults', [searchTerm])}
</div>
) : null}
{haveContacts ? this.renderContacts(contacts) : null}
</div>
);
}
private renderContacts(items: Array<ConversationListItemPropsType>) {
return (
<div className="contacts-dropdown">
{items.map((contact, index) => this.renderContact(contact, index))}
</div>
);
}
private renderContact(contact: ConversationListItemPropsType, index: Number) {
const { profileName, phoneNumber } = contact;
const { selectedContact } = this.props;
const shortenedPubkey = window.shortenPubkey(phoneNumber);
const rowContent = `${profileName} ${shortenedPubkey}`;
return (
<div
className={classNames(
'contacts-dropdown-row',
selectedContact === index && 'contacts-dropdown-row-selected'
)}
key={contact.phoneNumber}
onClick={() => this.props.onContactSelected(contact.phoneNumber)}
role="button"
>
{rowContent}
</div>
);
}
}

@ -20,6 +20,7 @@ import { cleanSearchTerm } from '../../util/cleanSearchTerm';
import { SearchOptions } from '../../types/Search';
import { SessionIconButton, SessionIconSize, SessionIconType } from './icon';
import { SessionIdEditable } from './SessionIdEditable';
import { UserSearchDropdown } from './UserSearchDropdown';
export interface Props {
searchTerm: string;
@ -185,6 +186,8 @@ export class LeftPaneMessageSection extends React.Component<Props, any> {
}
public renderCompose(): JSX.Element {
const { searchResults } = this.props;
return (
<div className="module-left-pane-compose">
<div className="exit">
@ -210,10 +213,12 @@ export class LeftPaneMessageSection extends React.Component<Props, any> {
{window.i18n('usersCanShareTheir...')}
</div>
<h4>{window.i18n('or')}</h4>
<SessionSearchInput
searchString={this.props.searchTerm}
onChange={this.updateSearchBound}
<UserSearchDropdown
searchTerm={this.props.searchTerm}
updateSearch={this.updateSearchBound}
placeholder={window.i18n('searchByIDOrDisplayName')}
searchResults={searchResults}
/>
<SessionButton
buttonColor={SessionButtonColor.Green}
@ -287,6 +292,8 @@ export class LeftPaneMessageSection extends React.Component<Props, any> {
this.setState((state: any) => {
return { showComposeView: !state.showComposeView };
});
// empty our generalized searchedString (one for the whole app)
this.updateSearch('');
}
private handleOnPasteSessionID() {

@ -4,12 +4,14 @@ import { SessionIconButton, SessionIconSize, SessionIconType } from './icon';
interface Props {
searchString: string;
onChange: any;
handleNavigation?: any;
placeholder: string;
}
export class SessionSearchInput extends React.Component<Props> {
public constructor(props: Props) {
super(props);
this.handleKeyDown = this.handleKeyDown.bind(this);
}
public render() {
@ -24,9 +26,20 @@ export class SessionSearchInput extends React.Component<Props> {
<input
value={searchString}
onChange={e => this.props.onChange(e.target.value)}
onKeyDown={this.handleKeyDown}
placeholder={this.props.placeholder}
/>
</div>
);
}
public handleKeyDown(e: any) {
if (e.keyCode === 38 || e.keyCode === 40 || e.key === 'Enter') {
// Up or Bottom arrow pressed
if (this.props.handleNavigation) {
e.stopPropagation();
this.props.handleNavigation(e);
}
}
}
}

@ -0,0 +1,96 @@
import React from 'react';
import { UserSearchResults } from '../UserSearchResults';
import { SessionSearchInput } from './SessionSearchInput';
import { PropsData as SearchResultsProps } from '../SearchResults';
export interface Props {
searchTerm: string;
placeholder: string;
searchResults?: SearchResultsProps;
updateSearch: (searchTerm: string) => void;
}
interface State {
selectedContact: number;
}
export class UserSearchDropdown extends React.Component<Props, State> {
private readonly updateSearchBound: (searchedString: string) => void;
public constructor(props: Props) {
super(props);
this.updateSearchBound = this.updateSearch.bind(this);
this.handleNavigation = this.handleNavigation.bind(this);
this.handleContactSelected = this.handleContactSelected.bind(this);
this.state = {
selectedContact: 0,
};
}
public handleNavigation(e: any) {
const { selectedContact } = this.state;
const { searchResults } = this.props;
// arrow up/down button should select next/previous list element
if (
e.keyCode === 38 &&
selectedContact > 0 &&
searchResults &&
searchResults.contacts.length > 0
) {
this.setState(prevState => ({
selectedContact: +prevState.selectedContact - 1,
}));
} else if (
e.keyCode === 40 &&
searchResults &&
selectedContact < searchResults.contacts.length - 1
) {
this.setState(prevState => ({
selectedContact: +prevState.selectedContact + 1,
}));
} else if (
e.key === 'Enter' &&
searchResults &&
searchResults.contacts.length > 0
) {
this.handleContactSelected(
searchResults.contacts[selectedContact].phoneNumber
);
}
}
public render() {
const { searchResults, placeholder } = this.props;
const { selectedContact } = this.state;
return (
<div>
<SessionSearchInput
searchString={this.props.searchTerm}
onChange={this.updateSearchBound}
placeholder={placeholder}
handleNavigation={this.handleNavigation}
/>
{searchResults && (
<UserSearchResults
{...searchResults}
i18n={window.i18n}
selectedContact={selectedContact}
onContactSelected={this.handleContactSelected}
/>
)}
</div>
);
}
public updateSearch(data: string) {
this.setState({ selectedContact: 0 });
this.props.updateSearch(data);
}
public handleContactSelected(key: string) {
this.updateSearch(key);
}
}

@ -204,10 +204,9 @@ async function queryConversationsAndContacts(
} else {
conversations.push(primaryDevice);
}
} else if (
conversation.type === 'direct' &&
!Boolean(conversation.lastMessage)
) {
} else if (conversation.type === 'direct') {
contacts.push(conversation.id);
} else if (conversation.type !== 'group') {
contacts.push(conversation.id);
} else {
conversations.push(conversation.id);

Loading…
Cancel
Save