diff --git a/ts/components/conversation/ContactName.tsx b/ts/components/conversation/ContactName.tsx index 1a58b0666..0994336dc 100644 --- a/ts/components/conversation/ContactName.tsx +++ b/ts/components/conversation/ContactName.tsx @@ -27,23 +27,20 @@ export const ContactName = (props: Props) => { } : {}) as React.CSSProperties; const textProfile = profileName || name || convoName || window.i18n('anonymous'); - const profileElement = shouldShowProfile ? ( - - - - ) : null; - - const pubKeyElement = shouldShowPubkey ? ( - - - - ) : null; return ( - {profileElement} + {shouldShowProfile ? ( + + + + ) : null} {shouldShowProfile ? ' ' : null} - {pubKeyElement} + {shouldShowPubkey ? ( + + + + ) : null} ); }; diff --git a/ts/components/leftpane/conversation-list-item/ConversationListItem.tsx b/ts/components/leftpane/conversation-list-item/ConversationListItem.tsx index 791318bd2..4e679c902 100644 --- a/ts/components/leftpane/conversation-list-item/ConversationListItem.tsx +++ b/ts/components/leftpane/conversation-list-item/ConversationListItem.tsx @@ -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) => { // 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, diff --git a/ts/components/leftpane/conversation-list-item/HeaderItem.tsx b/ts/components/leftpane/conversation-list-item/HeaderItem.tsx index e675697ed..aeeb60749 100644 --- a/ts/components/leftpane/conversation-list-item/HeaderItem.tsx +++ b/ts/components/leftpane/conversation-list-item/HeaderItem.tsx @@ -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} -
0 ? 'module-conversation-list-item__header__date--has-unread' : null - )} - > - -
+ {!isSearchingMode && ( +
0 ? 'module-conversation-list-item__header__date--has-unread' : null + )} + > + +
+ )} ); }; diff --git a/ts/components/leftpane/conversation-list-item/MessageItem.tsx b/ts/components/leftpane/conversation-list-item/MessageItem.tsx index 45aa97e40..8df2afb7e 100644 --- a/ts/components/leftpane/conversation-list-item/MessageItem.tsx +++ b/ts/components/leftpane/conversation-list-item/MessageItem.tsx @@ -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 }) => { )} - {lastMessage && lastMessage.status && !props.isMessageRequest ? ( + {!isSearchingMode && lastMessage && lastMessage.status && !props.isMessageRequest ? ( ) : null} diff --git a/ts/components/leftpane/conversation-list-item/UserItem.tsx b/ts/components/leftpane/conversation-list-item/UserItem.tsx index 6acdf8e2e..a550ac534 100644 --- a/ts/components/leftpane/conversation-list-item/UserItem.tsx +++ b/ts/components/leftpane/conversation-list-item/UserItem.tsx @@ -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)) { diff --git a/ts/components/search/SearchResults.tsx b/ts/components/search/SearchResults.tsx index 37db60275..a0f609b26 100644 --- a/ts/components/search/SearchResults.tsx +++ b/ts/components/search/SearchResults.tsx @@ -55,6 +55,7 @@ export const SearchResults = (props: SearchResultsProps) => { ))} diff --git a/ts/hooks/useParamSelector.ts b/ts/hooks/useParamSelector.ts index 0d550fa7a..12a7d4254 100644 --- a/ts/hooks/useParamSelector.ts +++ b/ts/hooks/useParamSelector.ts @@ -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 */ diff --git a/ts/interactions/conversationInteractions.ts b/ts/interactions/conversationInteractions.ts index f4f328601..ec1120528 100644 --- a/ts/interactions/conversationInteractions.ts +++ b/ts/interactions/conversationInteractions.ts @@ -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) { diff --git a/ts/models/conversation.ts b/ts/models/conversation.ts index 1c46cb867..0ad2939d2 100644 --- a/ts/models/conversation.ts +++ b/ts/models/conversation.ts @@ -334,8 +334,8 @@ export class ConversationModel extends Backbone.Model { 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 { if (hasNickname) { toRet.hasNickname = hasNickname; } + if (isKickedFromGroup) { toRet.isKickedFromGroup = isKickedFromGroup; } @@ -1027,14 +1028,25 @@ export class ConversationModel extends Backbone.Model { } } - // 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 { 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 { } public getProfileName() { - if (this.isPrivate() && !this.get('name')) { + if (this.isPrivate()) { return this.get('profileName'); } return undefined; diff --git a/ts/session/group/open-group.ts b/ts/session/group/open-group.ts index baa6c2cd1..048a63ee4 100644 --- a/ts/session/group/open-group.ts +++ b/ts/session/group/open-group.ts @@ -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({ diff --git a/ts/state/ducks/conversations.ts b/ts/state/ducks/conversations.ts index 506ca7b0d..4186b6b4a 100644 --- a/ts/state/ducks/conversations.ts +++ b/ts/state/ducks/conversations.ts @@ -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; diff --git a/ts/state/ducks/search.ts b/ts/state/ducks/search.ts index ddca4e075..f3229ac1e 100644 --- a/ts/state/ducks/search.ts +++ b/ts/state/ducks/search.ts @@ -198,9 +198,13 @@ function getAdvancedSearchOptionsFromQuery(query: string): AdvancedSearchOptions async function queryMessages(query: string): Promise> { 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 []; } }