You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
session-desktop/ts/components/session/LeftPaneContactSection.tsx

350 lines
9.5 KiB
TypeScript

Session v1.0 changes (#802) * correct typo in readme * include log * decrypt file server response, remove debug, handle crypt before _sendToProxy, improve json parsing failure logging * support file uploads on file proxy, fix _sendToProxy calling * bump form-data to 3.0 * initial refactor of feaure flag detection statements in serverRequest() * fix send-message line-height with multiple lines * fix lint * fix position of delete account modal * Profile picture upload, fixes and copy * Various changes suggested by redesign overview * Scrolling button updated and animations to modals * Display subscriber count for open chats * Prevent illegal username and passwords * Delete channel / group merge * Solidification of minor changes w appview injections * hide description field in group panel for now * fix join publicgroups pulls * increase min height respecting ratio * allow space inside a display name but not at start or end * fix height of leftpane overlay view * add back typing indicator and read receipt setting under privacy * Auto-focus new open chat input box * Password lock screen and delete data screen * touchups * Resolving Bilb revisions * Disable link previews as default per Kee on signup * remove date, we have git * add missing semicolon * _sendToProxy pass headers/handle response refactor, lint * fix my yarn conflict/resolve * include IV in server response * Sealed sender support * Support sealed sender for friend requests * fix lint * Remove unused destinationRegistrationId; lint * Update messages.json * pull RSS through file proxy * fix unit tests: remove not used count in scrolldown view and assert svg present * Disable auto-joining default loki open groups * session-id-editable-textarea * fix the textscramble for sessionID on registration * speed up lint, add lint-full/format-full, make sure use lint-full * add skipToken to establishConnection options, smuggle out secureRpcPubKey * get latest version through snode proxy, remove clearfix from ExpiredAlertBanner * expose semver and LokiAppDotNetServerAPI because we can't get ourKey from storage early enough * update note * fix upgrade link, wrap expiredWarning in span for styling, use br to clear the float, trim trailing whitespace * designalify * designalify * designalify user agent * continue designalification * make expired banner legible * remove ugly TLS hack * disable unauthorization rejection when making https requests limited to lokiRpc * Update main.js Aspect ratio amendment * Constants rework * local commit * event listeners * address missing comma for lint * fix header sessions message section * fix profile image size conversation list with pending friend request * textarea centering * refresh files in group in group panel * Looking into keyboard navigation * Remove P2P * cache eslint on `lint` but not `ready` * Cleanup media view formatting * force locale to be EN until our files are updated and translated * Simplification of keyup * Updated all icon references * SASS fixup * fix disabled state of message input on sent friendrequest * trim pubkey when user can enter one to remove whitespaces * remove lZ in path which fixes errors on svg and does not alter rendering * fix text scramble animation on registration * reload app on ctrl-r or f5 from anywhere * add back file which should have not been deleted * fix lint and clean code * fix lint * add .loki to have a self-signed cert * Remove mixpanel * use local shortcut instead of global shortcut otherwise, ctrl+r is only caught bu the last loaded instance * open the conversation when accepting a friend request also, it does what is needed to show the new friend in the friend list * make sure token comms are done over fileProxy, other notes, logging adjustment * leftpane sections titles are Wasa bold * minor refactor * onboarding messageview * linter * fix padding buttons overlay * do not render session-id-editable border when textarea disabled * textarea sessionID SpaceMono font * various touchups * fix font of description to sfprodisplay * reduce triple dots conversation header icon size * reduce size of conversationHeader title font size * fix font for session-search-input * make conversationlistitem title font wasa * fix green and white border under title in leftpane * fix panel-text-divider font-size and family * disable completely borders for profile images * make profile image which where 48pixels big 36 noew, as no more border * Complete conditional message onboarding * cache file deletions * Link preview warning on setting toggle * Messages.json amendments * Join channel generalisation * Localise global vars * remove eslintcache * rm global launchcount * Remove source field from envelope * Session public chat icon * CLosed groups ui initial listprops * Desktop: enable useSnodeProxy feature flag * file proxy needs to be able to talk to snode - disable TLS check for fileProxy - lokiHttpsAgent => snodeHttpsAgent (since we use for two different things now) * enable useSealedSender too per Maxim * lint * lint * window.extension.expiredPromise version * better error checking * use promise version to see if we're expired * fix typo * lint * put back seemingly now required process.env.NODE_TLS_REJECT_UNAUTHORIZED * fix querystring in file-proxy * lint * fix typo * Remove more references to signal.org * make sure TLS is forced on open groups, improve serverRequest error message * Closed groups UI * function params changes * turn off snode proxy logging * include useful info on error * actually validate URL before starting up a bunch of timers * Closed groups overlay integration * move comments from connecting_to_server_dialog_view * use attempt from window object to reduce code duplication * refactor out validServer() * lint * lint caught typo * Rename BACKGROUND_FRIEND_REQUEST to SESSION_REQUEST. Don't trigger friend request logic if a message is aimed at a group. * Linting * Closed group joining completed w/o backend * Fix friend request messages being sent to users you don't have a session in closed groups. Disable typing messages and read receipts in groups. Send out session request messages if you don't have a session with a member in the group. * Remove unneeded boolean condition. * Closed group update message stylgin * constants renaming * Message deletion fix * gruntify * fix grunt error * expose isRss, don't close uncloseable Rss conversation on deleteMessages * remove copyId and block user on RSS feeds * remove options from RSS feed that don't make any sense and don't work * fix grunt error * squelch RSS duplicate messages * extension.expiredStatus(), adjustable timers, improve guards * allowing sending of messages if we're still waiting to hear back * markRandomNodeUnreachable() refactor, notes/logging * improve logging * improve logging * no need to validate empty token, support lokinet/getession file domains, mark broken snodes as bad, improve logging * try to address travis-osx lint complaints * not designed to have a period at the end of titleIsNow * put period back at the end * Catch a stray loki messenger * fix stray loki messenger * loki messenger isnt a thing * lint * Fix open group joining. * guards incase there are no members yet, fixes dialog not showing up * fixed file server holding up message sender init. fix joining closed groups. * Clean * Don't wait for file server to return tokens when establishing home connection. * Disable join public chat prompt Co-authored-by: Audric Ackermann <audric.bilb@gmail.com> Co-authored-by: Ryan Tharp <neuro@interx.net> Co-authored-by: Vince <vincent@loki.network> Co-authored-by: Maxim Shishmarev <msgmaxim@gmail.com>
5 years ago
import React from 'react';
import {
ConversationListItem,
PropsData as ConversationListItemPropsType,
} from '../ConversationListItem';
import { PropsData as SearchResultsProps } from '../SearchResults';
import { debounce } from 'lodash';
import { cleanSearchTerm } from '../../util/cleanSearchTerm';
import { SearchOptions } from '../../types/Search';
import { LeftPane, RowRendererParamsType } from '../LeftPane';
import {
SessionButton,
SessionButtonColor,
SessionButtonType,
} from './SessionButton';
import { AutoSizer, List } from 'react-virtualized';
import { validateNumber } from '../../types/PhoneNumber';
import { ConversationType } from '../../state/ducks/conversations';
import { SessionClosableOverlay } from './SessionClosableOverlay';
import { MainViewController } from '../MainViewController';
export interface Props {
searchTerm: string;
isSecondaryDevice: boolean;
conversations: Array<ConversationListItemPropsType>;
friends: Array<ConversationType>;
receivedFriendsRequest: Array<ConversationListItemPropsType>;
receivedFriendRequestCount: number;
sentFriendsRequest: Array<ConversationListItemPropsType>;
searchResults?: SearchResultsProps;
updateSearchTerm: (searchTerm: string) => void;
search: (query: string, options: SearchOptions) => void;
openConversationInternal: (id: string, messageId?: string) => void;
clearSearch: () => void;
}
interface State {
showAddContactView: boolean;
selectedTab: number;
addContactRecipientID: string;
showFriendRequestsPopup: boolean;
pubKeyPasted: string;
}
export class LeftPaneContactSection extends React.Component<Props, State> {
private readonly debouncedSearch: (searchTerm: string) => void;
public constructor(props: Props) {
super(props);
this.state = {
showAddContactView: false,
selectedTab: 0,
addContactRecipientID: '',
pubKeyPasted: '',
showFriendRequestsPopup: false,
};
this.debouncedSearch = debounce(this.search.bind(this), 20);
this.handleTabSelected = this.handleTabSelected.bind(this);
this.handleToggleOverlay = this.handleToggleOverlay.bind(this);
this.handleOnAddContact = this.handleOnAddContact.bind(this);
this.handleRecipientSessionIDChanged = this.handleRecipientSessionIDChanged.bind(
this
);
this.handleToggleFriendRequestPopup = this.handleToggleFriendRequestPopup.bind(
this
);
}
public componentWillUnmount() {
this.updateSearch('');
this.setState({ addContactRecipientID: '' });
}
public handleTabSelected(tabType: number) {
this.setState({ selectedTab: tabType, showAddContactView: false });
}
public renderHeader(): JSX.Element | undefined {
const { receivedFriendRequestCount } = this.props;
// The feature "organize your friends as custom list" is not included in the first release
const labels = [window.i18n('contactsHeader') /*, window.i18n('lists')*/];
return LeftPane.RENDER_HEADER(
labels,
this.handleTabSelected,
undefined,
this.handleToggleFriendRequestPopup,
receivedFriendRequestCount
);
}
public componentDidMount() {
MainViewController.renderMessageView();
}
public componentDidUpdate() {
MainViewController.renderMessageView();
}
public render(): JSX.Element {
return (
<div className="left-pane-contact-section">
{this.renderHeader()}
{this.state.showAddContactView
? this.renderClosableOverlay()
: this.renderContacts()}
</div>
);
}
public renderRowFriendRequest = ({
index,
key,
style,
}: RowRendererParamsType): JSX.Element | undefined => {
const receivedFriendsRequest = this.props.receivedFriendsRequest;
const item = receivedFriendsRequest[index];
const onClick = this.props.openConversationInternal;
return (
<ConversationListItem
key={key}
style={style}
{...item}
i18n={window.i18n}
onClick={onClick}
/>
);
};
public renderRow = ({
index,
key,
style,
}: RowRendererParamsType): JSX.Element | undefined => {
const { sentFriendsRequest } = this.props;
const friends = window.getFriendsFromContacts(this.props.friends);
const combined = [...sentFriendsRequest, ...friends];
const item = combined[index];
return (
<ConversationListItem
key={key}
style={style}
{...item}
i18n={window.i18n}
onClick={this.props.openConversationInternal}
/>
);
};
public updateSearch(searchTerm: string) {
const { updateSearchTerm, clearSearch } = this.props;
if (!searchTerm) {
clearSearch();
return;
}
this.setState({ pubKeyPasted: '' });
if (updateSearchTerm) {
updateSearchTerm(searchTerm);
}
if (searchTerm.length < 2) {
return;
}
const cleanedTerm = cleanSearchTerm(searchTerm);
if (!cleanedTerm) {
return;
}
this.debouncedSearch(cleanedTerm);
}
public clearSearch() {
this.props.clearSearch();
}
public search() {
const { search } = this.props;
const { searchTerm, isSecondaryDevice } = this.props;
if (search) {
search(searchTerm, {
noteToSelf: window.i18n('noteToSelf').toLowerCase(),
ourNumber: window.textsecure.storage.user.getNumber(),
regionCode: '',
isSecondaryDevice,
});
}
}
private renderClosableOverlay() {
return (
<SessionClosableOverlay
overlayMode="contact"
onChangeSessionID={this.handleRecipientSessionIDChanged}
onCloseClick={this.handleToggleOverlay}
onButtonClick={this.handleOnAddContact}
/>
);
}
private handleToggleOverlay() {
this.setState((prevState: { showAddContactView: boolean }) => ({
showAddContactView: !prevState.showAddContactView,
}));
}
private handleToggleFriendRequestPopup() {
this.setState((prevState: { showFriendRequestsPopup: boolean }) => ({
showFriendRequestsPopup: !prevState.showFriendRequestsPopup,
}));
}
private handleOnAddContact() {
const sessionID = this.state.addContactRecipientID.trim();
const error = validateNumber(sessionID, window.i18n);
if (error) {
window.pushToast({
title: error,
type: 'error',
id: 'addContact',
});
} else {
window.Whisper.events.trigger('showConversation', sessionID);
}
}
private handleRecipientSessionIDChanged(value: string) {
this.setState({ addContactRecipientID: value });
}
private renderContacts() {
const { showFriendRequestsPopup } = this.state;
const hasReceivedFriendRequest =
this.props.receivedFriendsRequest.length > 0;
return (
<div className="left-pane-contact-content">
{this.renderList()}
{showFriendRequestsPopup &&
hasReceivedFriendRequest &&
this.renderFriendRequestPopup()}
{this.renderBottomButtons()}
</div>
);
}
private renderBottomButtons(): JSX.Element {
const { selectedTab } = this.state;
const edit = window.i18n('edit');
const addContact = window.i18n('addContact');
const createGroup = window.i18n('createGroup');
const showEditButton = false;
return (
<div className="left-pane-contact-bottom-buttons">
{showEditButton && (
<SessionButton
text={edit}
buttonType={SessionButtonType.SquareOutline}
buttonColor={SessionButtonColor.White}
/>
)}
{selectedTab === 0 ? (
<SessionButton
text={addContact}
buttonType={SessionButtonType.SquareOutline}
buttonColor={SessionButtonColor.Green}
onClick={this.handleToggleOverlay}
/>
) : (
<SessionButton
text={createGroup}
buttonType={SessionButtonType.SquareOutline}
buttonColor={SessionButtonColor.Green}
onClick={this.handleToggleOverlay}
/>
)}
</div>
);
}
private renderFriendRequestPopup() {
const frTitle = window.i18n('youHaveFriendRequestFrom');
const length = this.props.receivedFriendsRequest.length;
return (
<div className="module-left-pane__list-popup">
<div className="friend-request-title">{frTitle}</div>
<div className="module-left-pane__list-popup" key={0}>
<AutoSizer>
{({ height, width }) => (
<List
className="module-left-pane__virtual-list"
height={height}
rowCount={length}
rowHeight={64}
rowRenderer={this.renderRowFriendRequest}
width={width}
autoHeight={true}
/>
)}
</AutoSizer>
</div>
</div>
);
}
private renderList() {
const { sentFriendsRequest } = this.props;
const friends = window.getFriendsFromContacts(this.props.friends);
const length = Number(sentFriendsRequest.length) + Number(friends.length);
const combined = [...sentFriendsRequest, ...friends];
const list = (
<div className="module-left-pane__list" key={0}>
<AutoSizer>
{({ height, width }) => (
<List
className="module-left-pane__virtual-list"
height={height}
rowCount={length}
combined={combined}
rowHeight={64}
rowRenderer={this.renderRow}
width={width}
autoHeight={true}
/>
)}
</AutoSizer>
</div>
);
return [list];
}
}