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.
		
		
		
		
		
			
		
			
				
	
	
		
			252 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			TypeScript
		
	
			
		
		
	
	
			252 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			TypeScript
		
	
import styled, { CSSProperties } from 'styled-components';
 | 
						|
 | 
						|
import {
 | 
						|
  useIsPrivate,
 | 
						|
  useIsPublic,
 | 
						|
  useNicknameOrProfileNameOrShortenedPubkey,
 | 
						|
} from '../../hooks/useParamSelector';
 | 
						|
import { isUsAnySogsFromCache } from '../../session/apis/open_group_api/sogsv3/knownBlindedkeys';
 | 
						|
import { PubKey } from '../../session/types';
 | 
						|
import { UserUtils } from '../../session/utils';
 | 
						|
import { getOurPubKeyStrFromCache } from '../../session/utils/User';
 | 
						|
import { openConversationToSpecificMessage } from '../../state/ducks/conversations';
 | 
						|
import { MessageResultProps } from '../../types/message';
 | 
						|
import { Avatar, AvatarSize } from '../avatar/Avatar';
 | 
						|
import { MessageBodyHighlight } from '../basic/MessageBodyHighlight';
 | 
						|
import { ContactName } from '../conversation/ContactName';
 | 
						|
import { Timestamp } from '../conversation/Timestamp';
 | 
						|
import { leftPaneListWidth } from '../leftpane/LeftPane';
 | 
						|
 | 
						|
const StyledConversationTitleResults = styled.div`
 | 
						|
  flex-grow: 1;
 | 
						|
  flex-shrink: 1;
 | 
						|
  font-size: 14px;
 | 
						|
  line-height: 18px;
 | 
						|
  overflow-x: hidden;
 | 
						|
  white-space: nowrap;
 | 
						|
  text-overflow: ellipsis;
 | 
						|
  color: var(--conversation-tab-text-color);
 | 
						|
  /* We don't want this to overflow horizontally past the timestamp */
 | 
						|
  width: 90px;
 | 
						|
`;
 | 
						|
 | 
						|
const StyledConversationFromUserInGroup = styled(StyledConversationTitleResults)`
 | 
						|
  display: inline;
 | 
						|
  font-size: 12px;
 | 
						|
  line-height: 14px;
 | 
						|
  overflow-x: hidden;
 | 
						|
  font-weight: 700;
 | 
						|
  color: var(--conversation-tab-text-color);
 | 
						|
`;
 | 
						|
 | 
						|
const StyledSearchResults = styled.div`
 | 
						|
  padding: var(--margins-sm) var(--margins-md);
 | 
						|
  height: var(--contact-row-height);
 | 
						|
  max-width: ${leftPaneListWidth}px;
 | 
						|
 | 
						|
  display: flex;
 | 
						|
  flex-direction: row;
 | 
						|
  align-items: flex-start;
 | 
						|
 | 
						|
  cursor: pointer;
 | 
						|
 | 
						|
  &:hover {
 | 
						|
    background-color: var(--conversation-tab-background-hover-color);
 | 
						|
  }
 | 
						|
`;
 | 
						|
 | 
						|
const StyledResultText = styled.div`
 | 
						|
  flex-grow: 1;
 | 
						|
  margin-inline-start: 12px;
 | 
						|
  display: inline-flex;
 | 
						|
  flex-direction: column;
 | 
						|
  align-items: stretch;
 | 
						|
  min-width: 0;
 | 
						|
`;
 | 
						|
 | 
						|
const ResultsHeader = styled.div`
 | 
						|
  display: flex;
 | 
						|
  flex-direction: row;
 | 
						|
  align-items: center;
 | 
						|
`;
 | 
						|
 | 
						|
const StyledMessageResultsHeaderName = styled.span`
 | 
						|
  font-weight: 300;
 | 
						|
`;
 | 
						|
 | 
						|
const FromName = (props: { source: string; conversationId: string }) => {
 | 
						|
  const { conversationId, source } = props;
 | 
						|
 | 
						|
  const isNoteToSelf = conversationId === getOurPubKeyStrFromCache() && source === conversationId;
 | 
						|
 | 
						|
  if (isNoteToSelf) {
 | 
						|
    return (
 | 
						|
      <StyledMessageResultsHeaderName>{window.i18n('noteToSelf')}</StyledMessageResultsHeaderName>
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
  if (source === getOurPubKeyStrFromCache()) {
 | 
						|
    return <StyledMessageResultsHeaderName>{window.i18n('you')}</StyledMessageResultsHeaderName>;
 | 
						|
  }
 | 
						|
 | 
						|
  return (
 | 
						|
    <ContactName
 | 
						|
      pubkey={conversationId}
 | 
						|
      module="module-message-search-result__header__name"
 | 
						|
      shouldShowPubkey={false}
 | 
						|
    />
 | 
						|
  );
 | 
						|
};
 | 
						|
 | 
						|
const ConversationHeader = (props: { source: string; conversationId: string }) => {
 | 
						|
  const { source, conversationId } = props;
 | 
						|
 | 
						|
  const ourKey = getOurPubKeyStrFromCache();
 | 
						|
 | 
						|
  if (conversationId !== ourKey) {
 | 
						|
    return (
 | 
						|
      <StyledConversationTitleResults>
 | 
						|
        <StyledMessageResultsHeaderName>
 | 
						|
          <ContactName pubkey={conversationId} shouldShowPubkey={false} boldProfileName={false} />
 | 
						|
        </StyledMessageResultsHeaderName>
 | 
						|
      </StyledConversationTitleResults>
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
  return (
 | 
						|
    <StyledConversationTitleResults>
 | 
						|
      <FromName source={source} conversationId={conversationId} />
 | 
						|
    </StyledConversationTitleResults>
 | 
						|
  );
 | 
						|
};
 | 
						|
 | 
						|
const FromUserInGroup = (props: { authorPubkey: string; conversationId: string }) => {
 | 
						|
  const { authorPubkey, conversationId } = props;
 | 
						|
 | 
						|
  const ourKey = getOurPubKeyStrFromCache();
 | 
						|
  const isPublic = useIsPublic(conversationId);
 | 
						|
  const convoIsPrivate = useIsPrivate(conversationId);
 | 
						|
  const authorConvoName = useNicknameOrProfileNameOrShortenedPubkey(authorPubkey);
 | 
						|
 | 
						|
  if (convoIsPrivate) {
 | 
						|
    return null;
 | 
						|
  }
 | 
						|
 | 
						|
  if (
 | 
						|
    authorPubkey === ourKey ||
 | 
						|
    (isPublic && PubKey.isBlinded(authorPubkey) && isUsAnySogsFromCache(authorPubkey))
 | 
						|
  ) {
 | 
						|
    return (
 | 
						|
      <StyledConversationFromUserInGroup>{window.i18n('you')}: </StyledConversationFromUserInGroup>
 | 
						|
    );
 | 
						|
  }
 | 
						|
  return <StyledConversationFromUserInGroup>{authorConvoName}: </StyledConversationFromUserInGroup>;
 | 
						|
};
 | 
						|
 | 
						|
const AvatarItem = (props: { source: string }) => {
 | 
						|
  return <Avatar size={AvatarSize.S} pubkey={props.source} />;
 | 
						|
};
 | 
						|
 | 
						|
const ResultBody = styled.div`
 | 
						|
  margin-top: 1px;
 | 
						|
  flex-shrink: 1;
 | 
						|
 | 
						|
  font-size: var(--font-size-sm);
 | 
						|
 | 
						|
  color: var(--conversation-tab-text-color);
 | 
						|
 | 
						|
  max-height: 3.6em;
 | 
						|
 | 
						|
  overflow: hidden;
 | 
						|
  display: -webkit-box;
 | 
						|
  -webkit-line-clamp: 1;
 | 
						|
  -webkit-box-orient: vertical;
 | 
						|
`;
 | 
						|
 | 
						|
const StyledTimestampContaimer = styled.div`
 | 
						|
  flex-shrink: 0;
 | 
						|
  margin-inline-start: 6px;
 | 
						|
 | 
						|
  font-size: var(--font-size-xs);
 | 
						|
  line-height: 16px;
 | 
						|
  letter-spacing: 0.3px;
 | 
						|
 | 
						|
  overflow-x: hidden;
 | 
						|
  white-space: nowrap;
 | 
						|
  text-overflow: ellipsis;
 | 
						|
 | 
						|
  text-transform: uppercase;
 | 
						|
 | 
						|
  color: var(--conversation-tab-text-color);
 | 
						|
`;
 | 
						|
 | 
						|
type MessageSearchResultProps = MessageResultProps & { style: CSSProperties };
 | 
						|
 | 
						|
export const MessageSearchResult = (props: MessageSearchResultProps) => {
 | 
						|
  const {
 | 
						|
    id,
 | 
						|
    conversationId,
 | 
						|
    received_at,
 | 
						|
    snippet,
 | 
						|
    source,
 | 
						|
    sent_at,
 | 
						|
    serverTimestamp,
 | 
						|
    timestamp,
 | 
						|
    direction,
 | 
						|
    style,
 | 
						|
  } = props;
 | 
						|
 | 
						|
  /** destination is only used for search results (showing the `from:` and `to`)
 | 
						|
   *  1.  for messages we sent or synced from another of our devices
 | 
						|
   *    - the conversationId for a private convo
 | 
						|
   *    - the conversationId for a closed group convo
 | 
						|
   *    - the conversationId for an opengroup
 | 
						|
   *
 | 
						|
   *  2. for messages we received
 | 
						|
   *    - our own pubkey for a private conversation
 | 
						|
   *    - the conversationID for a closed group
 | 
						|
   *    - the conversationID for a public group
 | 
						|
   */
 | 
						|
  const me = UserUtils.getOurPubKeyStrFromCache();
 | 
						|
  const convoIsPrivate = useIsPrivate(conversationId);
 | 
						|
 | 
						|
  const destination =
 | 
						|
    direction === 'incoming' ? conversationId : convoIsPrivate ? me : conversationId;
 | 
						|
 | 
						|
  if (!source && !destination) {
 | 
						|
    return null;
 | 
						|
  }
 | 
						|
 | 
						|
  return (
 | 
						|
    <StyledSearchResults
 | 
						|
      key={`div-msg-searchresult-${id}`}
 | 
						|
      style={style}
 | 
						|
      role="button"
 | 
						|
      onClick={() => {
 | 
						|
        void openConversationToSpecificMessage({
 | 
						|
          conversationKey: conversationId,
 | 
						|
          messageIdToNavigateTo: id,
 | 
						|
          shouldHighlightMessage: true,
 | 
						|
        });
 | 
						|
      }}
 | 
						|
    >
 | 
						|
      <AvatarItem source={conversationId} />
 | 
						|
      <StyledResultText>
 | 
						|
        <ResultsHeader>
 | 
						|
          <ConversationHeader source={destination} conversationId={conversationId} />
 | 
						|
          <StyledTimestampContaimer>
 | 
						|
            <Timestamp
 | 
						|
              timestamp={serverTimestamp || timestamp || sent_at || received_at}
 | 
						|
              isConversationSearchResult={true}
 | 
						|
            />
 | 
						|
          </StyledTimestampContaimer>
 | 
						|
        </ResultsHeader>
 | 
						|
        <ResultBody>
 | 
						|
          <FromUserInGroup authorPubkey={source} conversationId={conversationId} />
 | 
						|
          <MessageBodyHighlight text={snippet || ''} isGroup={!convoIsPrivate} />
 | 
						|
        </ResultBody>
 | 
						|
      </StyledResultText>
 | 
						|
    </StyledSearchResults>
 | 
						|
  );
 | 
						|
};
 |