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;
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 (
<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}
{pubKeyElement}
{shouldShowPubkey ? (
<span className={`${prefix}__profile-number`}>
<Emojify text={pubkey} />
</span>
) : null}
</span>
);
};

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

@ -4,6 +4,7 @@ import { useSelector } from 'react-redux';
import styled from 'styled-components';
import { useConversationPropsById, useIsPinned } from '../../../hooks/useParamSelector';
import { SectionType } from '../../../state/ducks/section';
import { isSearching } from '../../../state/selectors/search';
import { getFocusedSection } from '../../../state/selectors/section';
import { Timestamp } from '../../conversation/Timestamp';
import { SessionIcon } from '../../icon';
@ -76,6 +77,8 @@ const ListItemIcons = () => {
export const ConversationListItemHeaderItem = () => {
const conversationId = useContext(ContextConversationId);
const isSearchingMode = useSelector(isSearching);
const convoProps = useHeaderItemProps(conversationId);
if (!convoProps) {
return null;
@ -104,14 +107,16 @@ export const ConversationListItemHeaderItem = () => {
{unreadCountDiv}
{atSymbol}
<div
className={classNames(
'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>
{!isSearchingMode && (
<div
className={classNames(
'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>
)}
</div>
);
};

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

@ -1,18 +1,34 @@
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 { isSearching } from '../../../state/selectors/search';
import { ContactName } from '../../conversation/ContactName';
import { ContextConversationId } from './ConversationListItem';
export const UserItem = () => {
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 isMe = useIsMe(conversationId);
const username = useConversationUsername(conversationId);
const realName = useConversationRealName(conversationId);
const hasNickname = useHasNickname(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;
if ((!username || username.length === 0) && (!displayName || displayName.length === 0)) {

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

@ -31,6 +31,15 @@ export function useConversationUsernameOrShorten(convoId?: string) {
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
*/

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

@ -334,8 +334,8 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
const expireTimer = this.get('expireTimer');
const currentNotificationSetting = this.get('triggerNotificationsFor');
// 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
// 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
const toRet: ReduxConversationType = {
id: this.id as string,
activeAt: this.get('active_at'),
@ -394,6 +394,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
if (hasNickname) {
toRet.hasNickname = hasNickname;
}
if (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();
if (this.get('nickname') === trimmed) {
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.updateProfileName();
@ -1060,8 +1072,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
public async updateProfileName() {
// Prioritise nickname over the profile display name
const nickname = this.getNickname();
const profile = this.getLokiProfile();
const displayName = profile && profile.displayName;
const displayName = this.getLokiProfile()?.displayName;
const profileName = nickname || displayName || null;
await this.setProfileName(profileName);
@ -1270,7 +1281,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
}
public getProfileName() {
if (this.isPrivate() && !this.get('name')) {
if (this.isPrivate()) {
return this.get('profileName');
}
return undefined;

@ -57,7 +57,6 @@ export async function initiateOpenGroupUpdate(
data: downloaded.buffer,
isRaw: true,
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));
await convo.setLokiProfile({

@ -222,7 +222,13 @@ export type LastMessageType = {
export interface ReduxConversationType {
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;
/**
* 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;
hasNickname?: boolean;

@ -198,9 +198,13 @@ function getAdvancedSearchOptionsFromQuery(query: string): AdvancedSearchOptions
async function queryMessages(query: string): Promise<Array<MessageResultProps>> {
try {
const normalized = cleanSearchTerm(query);
return searchMessages(normalized, 150);
const trimmedQuery = query.trim();
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) {
window.log.warn('queryMessages failed with', e.message);
return [];
}
}

Loading…
Cancel
Save