allow searching for nickname or name for contacts

pull/2142/head
Audric Ackermann 3 years ago
parent ceb5317160
commit d6a8f5e92b
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4

@ -27,23 +27,20 @@ export const ContactName = (props: Props) => {
} }
: {}) as React.CSSProperties; : {}) as React.CSSProperties;
const textProfile = profileName || name || convoName || window.i18n('anonymous'); const textProfile = profileName || name || convoName || window.i18n('anonymous');
const profileElement = shouldShowProfile ? (
<span style={styles as any} className={`${prefix}__profile-name`}>
<Emojify text={textProfile} />
</span>
) : null;
const pubKeyElement = shouldShowPubkey ? (
<span className={`${prefix}__profile-number`}>
<Emojify text={pubkey} />
</span>
) : null;
return ( return (
<span className={classNames(prefix, compact && 'compact')} dir="auto"> <span className={classNames(prefix, compact && 'compact')} dir="auto">
{profileElement} {shouldShowProfile ? (
<span style={styles as any} className={`${prefix}__profile-name`}>
<Emojify text={textProfile} />
</span>
) : null}
{shouldShowProfile ? ' ' : null} {shouldShowProfile ? ' ' : null}
{pubKeyElement} {shouldShowPubkey ? (
<span className={`${prefix}__profile-number`}>
<Emojify text={pubkey} />
</span>
) : null}
</span> </span>
); );
}; };

@ -85,9 +85,10 @@ const ConversationListItem = (props: Props) => {
mentionedUs, mentionedUs,
isMessageRequest, isMessageRequest,
} = props; } = props;
const triggerId = `conversation-item-${conversationId}-ctxmenu`;
const key = `conversation-item-${conversationId}`; const key = `conversation-item-${conversationId}`;
const triggerId = `${key}-ctxmenu`;
const openConvo = useCallback( const openConvo = useCallback(
async (e: React.MouseEvent<HTMLDivElement>) => { async (e: React.MouseEvent<HTMLDivElement>) => {
// mousedown is invoked sooner than onClick, but for both right and left click // mousedown is invoked sooner than onClick, but for both right and left click
@ -108,7 +109,7 @@ const ConversationListItem = (props: Props) => {
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
}} }}
onContextMenu={(e: any) => { onContextMenu={e => {
contextMenu.show({ contextMenu.show({
id: triggerId, id: triggerId,
event: e, event: e,

@ -4,6 +4,7 @@ import { useSelector } from 'react-redux';
import styled from 'styled-components'; import styled from 'styled-components';
import { useConversationPropsById, useIsPinned } from '../../../hooks/useParamSelector'; import { useConversationPropsById, useIsPinned } from '../../../hooks/useParamSelector';
import { SectionType } from '../../../state/ducks/section'; import { SectionType } from '../../../state/ducks/section';
import { isSearching } from '../../../state/selectors/search';
import { getFocusedSection } from '../../../state/selectors/section'; import { getFocusedSection } from '../../../state/selectors/section';
import { Timestamp } from '../../conversation/Timestamp'; import { Timestamp } from '../../conversation/Timestamp';
import { SessionIcon } from '../../icon'; import { SessionIcon } from '../../icon';
@ -76,6 +77,8 @@ const ListItemIcons = () => {
export const ConversationListItemHeaderItem = () => { export const ConversationListItemHeaderItem = () => {
const conversationId = useContext(ContextConversationId); const conversationId = useContext(ContextConversationId);
const isSearchingMode = useSelector(isSearching);
const convoProps = useHeaderItemProps(conversationId); const convoProps = useHeaderItemProps(conversationId);
if (!convoProps) { if (!convoProps) {
return null; return null;
@ -104,14 +107,16 @@ export const ConversationListItemHeaderItem = () => {
{unreadCountDiv} {unreadCountDiv}
{atSymbol} {atSymbol}
<div {!isSearchingMode && (
className={classNames( <div
'module-conversation-list-item__header__date', className={classNames(
unreadCount > 0 ? 'module-conversation-list-item__header__date--has-unread' : null 'module-conversation-list-item__header__date',
)} unreadCount > 0 ? 'module-conversation-list-item__header__date--has-unread' : null
> )}
<Timestamp timestamp={activeAt} isConversationListItem={true} momentFromNow={true} /> >
</div> <Timestamp timestamp={activeAt} isConversationListItem={true} momentFromNow={true} />
</div>
)}
</div> </div>
); );
}; };

@ -7,6 +7,8 @@ import { OutgoingMessageStatus } from '../../conversation/message/message-conten
import { TypingAnimation } from '../../conversation/TypingAnimation'; import { TypingAnimation } from '../../conversation/TypingAnimation';
import { ContextConversationId } from './ConversationListItem'; import { ContextConversationId } from './ConversationListItem';
import { MessageRequestButtons } from './MessageRequest'; import { MessageRequestButtons } from './MessageRequest';
import { useSelector } from 'react-redux';
import { isSearching } from '../../../state/selectors/search';
function useMessageItemProps(convoId: string) { function useMessageItemProps(convoId: string) {
const convoProps = useConversationPropsById(convoId); const convoProps = useConversationPropsById(convoId);
@ -23,6 +25,8 @@ function useMessageItemProps(convoId: string) {
export const MessageItem = (props: { isMessageRequest: boolean }) => { export const MessageItem = (props: { isMessageRequest: boolean }) => {
const conversationId = useContext(ContextConversationId); const conversationId = useContext(ContextConversationId);
const convoProps = useMessageItemProps(conversationId); const convoProps = useMessageItemProps(conversationId);
const isSearchingMode = useSelector(isSearching);
if (!convoProps) { if (!convoProps) {
return null; return null;
} }
@ -52,7 +56,7 @@ export const MessageItem = (props: { isMessageRequest: boolean }) => {
)} )}
</div> </div>
<MessageRequestButtons isMessageRequest={props.isMessageRequest} /> <MessageRequestButtons isMessageRequest={props.isMessageRequest} />
{lastMessage && lastMessage.status && !props.isMessageRequest ? ( {!isSearchingMode && lastMessage && lastMessage.status && !props.isMessageRequest ? (
<OutgoingMessageStatus status={lastMessage.status} /> <OutgoingMessageStatus status={lastMessage.status} />
) : null} ) : null}
</div> </div>

@ -1,18 +1,34 @@
import React, { useContext } from 'react'; import React, { useContext } from 'react';
import { useConversationUsername, useIsMe } from '../../../hooks/useParamSelector'; import { useSelector } from 'react-redux';
import {
useConversationRealName,
useConversationUsername,
useHasNickname,
useIsMe,
} from '../../../hooks/useParamSelector';
import { PubKey } from '../../../session/types'; import { PubKey } from '../../../session/types';
import { isSearching } from '../../../state/selectors/search';
import { ContactName } from '../../conversation/ContactName'; import { ContactName } from '../../conversation/ContactName';
import { ContextConversationId } from './ConversationListItem'; import { ContextConversationId } from './ConversationListItem';
export const UserItem = () => { export const UserItem = () => {
const conversationId = useContext(ContextConversationId); const conversationId = useContext(ContextConversationId);
// we want to show the nickname in brackets if a nickname is set for search results
const isSearchResultsMode = useSelector(isSearching);
const shortenedPubkey = PubKey.shorten(conversationId); const shortenedPubkey = PubKey.shorten(conversationId);
const isMe = useIsMe(conversationId); const isMe = useIsMe(conversationId);
const username = useConversationUsername(conversationId); const username = useConversationUsername(conversationId);
const realName = useConversationRealName(conversationId);
const hasNickname = useHasNickname(conversationId);
const displayedPubkey = username ? shortenedPubkey : conversationId; const displayedPubkey = username ? shortenedPubkey : conversationId;
const displayName = isMe ? window.i18n('noteToSelf') : username; const displayName = isMe
? window.i18n('noteToSelf')
: isSearchResultsMode && hasNickname
? `${realName} (${username})`
: username;
let shouldShowPubkey = false; let shouldShowPubkey = false;
if ((!username || username.length === 0) && (!displayName || displayName.length === 0)) { if ((!username || username.length === 0) && (!displayName || displayName.length === 0)) {

@ -55,6 +55,7 @@ export const SearchResults = (props: SearchResultsProps) => {
<MemoConversationListItemWithDetails <MemoConversationListItemWithDetails
{...contactOrGroup} {...contactOrGroup}
mentionedUs={false} mentionedUs={false}
isBlocked={false}
key={`search-result-convo-${contactOrGroup.id}`} key={`search-result-convo-${contactOrGroup.id}`}
/> />
))} ))}

@ -31,6 +31,15 @@ export function useConversationUsernameOrShorten(convoId?: string) {
return convoProps?.profileName || convoProps?.name || (convoId && PubKey.shorten(convoId)); return convoProps?.profileName || convoProps?.name || (convoId && PubKey.shorten(convoId));
} }
/**
* Returns either the nickname, profileName, or the shorten pubkey
*/
export function useConversationRealName(convoId?: string) {
const convoProps = useConversationPropsById(convoId);
return convoProps?.name;
}
/** /**
* Returns either the nickname, the profileName, in '"' or the full pubkeys given * Returns either the nickname, the profileName, in '"' or the full pubkeys given
*/ */

@ -251,7 +251,7 @@ export async function setNotificationForConvoId(
} }
export async function clearNickNameByConvoId(conversationId: string) { export async function clearNickNameByConvoId(conversationId: string) {
const conversation = getConversationController().get(conversationId); const conversation = getConversationController().get(conversationId);
await conversation.setNickname(''); await conversation.setNickname(undefined);
} }
export function showChangeNickNameByConvoId(conversationId: string) { export function showChangeNickNameByConvoId(conversationId: string) {

@ -334,8 +334,8 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
const expireTimer = this.get('expireTimer'); const expireTimer = this.get('expireTimer');
const currentNotificationSetting = this.get('triggerNotificationsFor'); const currentNotificationSetting = this.get('triggerNotificationsFor');
// to reduce the redux store size, only set fields which cannot be undefined // To reduce the redux store size, only set fields which cannot be undefined.
// for instance, a boolean can usually be not set if false, etc // For instance, a boolean can usually be not set if false, etc
const toRet: ReduxConversationType = { const toRet: ReduxConversationType = {
id: this.id as string, id: this.id as string,
activeAt: this.get('active_at'), activeAt: this.get('active_at'),
@ -394,6 +394,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
if (hasNickname) { if (hasNickname) {
toRet.hasNickname = hasNickname; toRet.hasNickname = hasNickname;
} }
if (isKickedFromGroup) { if (isKickedFromGroup) {
toRet.isKickedFromGroup = isKickedFromGroup; toRet.isKickedFromGroup = isKickedFromGroup;
} }
@ -1027,14 +1028,25 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
} }
} }
// LOKI PROFILES public async setNickname(nickname?: string) {
public async setNickname(nickname: string) { if (!this.isPrivate()) {
window.log.info('cannot setNickname to a non private conversation.');
return;
}
const trimmed = nickname && nickname.trim(); const trimmed = nickname && nickname.trim();
if (this.get('nickname') === trimmed) { if (this.get('nickname') === trimmed) {
return; return;
} }
// make sure to save the lokiDisplayName as name in the db. so a search of conversation returns it.
// (we look for matches in name too)
const realUserName = this.getLokiProfile()?.displayName;
if (!trimmed || !trimmed.length) {
this.set({ nickname: undefined, name: realUserName });
} else {
this.set({ nickname: trimmed, name: realUserName });
}
this.set({ nickname: trimmed });
await this.commit(); await this.commit();
await this.updateProfileName(); await this.updateProfileName();
@ -1060,8 +1072,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
public async updateProfileName() { public async updateProfileName() {
// Prioritise nickname over the profile display name // Prioritise nickname over the profile display name
const nickname = this.getNickname(); const nickname = this.getNickname();
const profile = this.getLokiProfile(); const displayName = this.getLokiProfile()?.displayName;
const displayName = profile && profile.displayName;
const profileName = nickname || displayName || null; const profileName = nickname || displayName || null;
await this.setProfileName(profileName); await this.setProfileName(profileName);
@ -1270,7 +1281,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
} }
public getProfileName() { public getProfileName() {
if (this.isPrivate() && !this.get('name')) { if (this.isPrivate()) {
return this.get('profileName'); return this.get('profileName');
} }
return undefined; return undefined;

@ -57,7 +57,6 @@ export async function initiateOpenGroupUpdate(
data: downloaded.buffer, data: downloaded.buffer,
isRaw: true, isRaw: true,
contentType: MIME.IMAGE_UNKNOWN, // contentType is mostly used to generate previews and screenshot. We do not care for those in this case. contentType: MIME.IMAGE_UNKNOWN, // contentType is mostly used to generate previews and screenshot. We do not care for those in this case.
// url: pathname,
}); });
const newHash = sha256(fromArrayBufferToBase64(downloaded.buffer)); const newHash = sha256(fromArrayBufferToBase64(downloaded.buffer));
await convo.setLokiProfile({ await convo.setLokiProfile({

@ -222,7 +222,13 @@ export type LastMessageType = {
export interface ReduxConversationType { export interface ReduxConversationType {
id: string; id: string;
/**
* For a group, this is the groupName. For a private convo, this is always the realName of that user as he defined it (and so not a custom nickname)
*/
name?: string; name?: string;
/**
* profileName is the bad duck. if a nickname is set, this holds the value of it. Otherwise, it holds the name of that user as he defined it
*/
profileName?: string; profileName?: string;
hasNickname?: boolean; hasNickname?: boolean;

@ -198,9 +198,13 @@ function getAdvancedSearchOptionsFromQuery(query: string): AdvancedSearchOptions
async function queryMessages(query: string): Promise<Array<MessageResultProps>> { async function queryMessages(query: string): Promise<Array<MessageResultProps>> {
try { try {
const normalized = cleanSearchTerm(query); const trimmedQuery = query.trim();
return searchMessages(normalized, 150); const normalized = cleanSearchTerm(trimmedQuery);
// 200 on a large database is already pretty slow
const limit = Math.min((trimmedQuery.length || 2) * 50, 200);
return searchMessages(normalized, limit);
} catch (e) { } catch (e) {
window.log.warn('queryMessages failed with', e.message);
return []; return [];
} }
} }

Loading…
Cancel
Save