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.
		
		
		
		
		
			
		
			
				
	
	
		
			178 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			TypeScript
		
	
			
		
		
	
	
			178 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			TypeScript
		
	
import React, { ReactElement, useEffect, useState } from 'react';
 | 
						|
import { useSelector } from 'react-redux';
 | 
						|
import styled from 'styled-components';
 | 
						|
import { Data } from '../../../../data/data';
 | 
						|
import { PubKey } from '../../../../session/types/PubKey';
 | 
						|
import { isDarkTheme } from '../../../../state/selectors/theme';
 | 
						|
import { nativeEmojiData } from '../../../../util/emoji';
 | 
						|
import { findAndFormatContact } from '../../../../models/message';
 | 
						|
 | 
						|
export type TipPosition = 'center' | 'left' | 'right';
 | 
						|
 | 
						|
export const POPUP_WIDTH = 216; // px
 | 
						|
 | 
						|
export const StyledPopupContainer = styled.div<{ tooltipPosition: TipPosition }>`
 | 
						|
  display: flex;
 | 
						|
  justify-content: space-between;
 | 
						|
  align-items: center;
 | 
						|
  width: ${POPUP_WIDTH}px;
 | 
						|
  height: 72px;
 | 
						|
  z-index: 5;
 | 
						|
 | 
						|
  background-color: var(--message-bubbles-received-background-color);
 | 
						|
  color: var(--message-bubbles-received-text-color);
 | 
						|
  box-shadow: 0px 0px 13px rgba(0, 0, 0, 0.51);
 | 
						|
  font-size: 12px;
 | 
						|
  font-weight: 600;
 | 
						|
  overflow-wrap: break-word;
 | 
						|
  padding: 16px;
 | 
						|
  border-radius: 12px;
 | 
						|
  cursor: pointer;
 | 
						|
 | 
						|
  &:after {
 | 
						|
    content: '';
 | 
						|
    position: absolute;
 | 
						|
    top: calc(100% - 19px);
 | 
						|
    left: ${props => {
 | 
						|
      switch (props.tooltipPosition) {
 | 
						|
        case 'left':
 | 
						|
          return '24px';
 | 
						|
        case 'right':
 | 
						|
          return 'calc(100% - 78px)';
 | 
						|
        case 'center':
 | 
						|
        default:
 | 
						|
          return 'calc(100% - 118px)';
 | 
						|
      }
 | 
						|
    }};
 | 
						|
    width: 22px;
 | 
						|
    height: 22px;
 | 
						|
    background-color: var(--message-bubbles-received-background-color);
 | 
						|
    transform: rotate(45deg);
 | 
						|
    border-radius: 3px;
 | 
						|
    transform: scaleY(1.4) rotate(45deg);
 | 
						|
    clip-path: polygon(100% 100%, 7.2px 100%, 100% 7.2px);
 | 
						|
  }
 | 
						|
`;
 | 
						|
 | 
						|
const StyledEmoji = styled.span`
 | 
						|
  font-size: 36px;
 | 
						|
  margin-left: 8px;
 | 
						|
`;
 | 
						|
 | 
						|
const StyledContacts = styled.span`
 | 
						|
  word-break: break-all;
 | 
						|
  span {
 | 
						|
    word-break: keep-all;
 | 
						|
  }
 | 
						|
`;
 | 
						|
 | 
						|
const StyledOthers = styled.span<{ darkMode: boolean }>`
 | 
						|
  color: ${props => (props.darkMode ? 'var(--primary-color)' : 'var(--text-primary-color)')};
 | 
						|
`;
 | 
						|
 | 
						|
const generateContactsString = async (
 | 
						|
  messageId: string,
 | 
						|
  senders: Array<string>
 | 
						|
): Promise<Array<string> | null> => {
 | 
						|
  let results = [];
 | 
						|
  const message = await Data.getMessageById(messageId);
 | 
						|
  if (message) {
 | 
						|
    let meIndex = -1;
 | 
						|
    results = senders.map((sender, index) => {
 | 
						|
      const contact = findAndFormatContact(sender);
 | 
						|
      if (contact.isMe) {
 | 
						|
        meIndex = index;
 | 
						|
      }
 | 
						|
      return contact?.profileName || contact?.name || PubKey.shorten(sender);
 | 
						|
    });
 | 
						|
    if (meIndex >= 0) {
 | 
						|
      results.splice(meIndex, 1);
 | 
						|
      results = [window.i18n('you'), ...results];
 | 
						|
    }
 | 
						|
    if (results && results.length > 0) {
 | 
						|
      return results;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return null;
 | 
						|
};
 | 
						|
 | 
						|
const Contacts = (contacts: Array<string>, count: number) => {
 | 
						|
  const darkMode = useSelector(isDarkTheme);
 | 
						|
 | 
						|
  if (!(contacts?.length > 0)) {
 | 
						|
    return null;
 | 
						|
  }
 | 
						|
 | 
						|
  const reactors = contacts.length;
 | 
						|
  if (reactors === 1 || reactors === 2 || reactors === 3) {
 | 
						|
    return (
 | 
						|
      <StyledContacts>
 | 
						|
        {window.i18n(
 | 
						|
          reactors === 1
 | 
						|
            ? 'reactionPopupOne'
 | 
						|
            : reactors === 2
 | 
						|
            ? 'reactionPopupTwo'
 | 
						|
            : 'reactionPopupThree',
 | 
						|
          contacts
 | 
						|
        )}{' '}
 | 
						|
        <span>{window.i18n('reactionPopup')}</span>
 | 
						|
      </StyledContacts>
 | 
						|
    );
 | 
						|
  }
 | 
						|
  if (reactors > 3) {
 | 
						|
    return (
 | 
						|
      <StyledContacts>
 | 
						|
        {window.i18n('reactionPopupMany', [contacts[0], contacts[1], contacts[3]])}{' '}
 | 
						|
        <StyledOthers darkMode={darkMode}>
 | 
						|
          {window.i18n(reactors === 4 ? 'otherSingular' : 'otherPlural', [`${count - 3}`])}
 | 
						|
        </StyledOthers>{' '}
 | 
						|
        <span>{window.i18n('reactionPopup')}</span>
 | 
						|
      </StyledContacts>
 | 
						|
    );
 | 
						|
  }
 | 
						|
  return null;
 | 
						|
};
 | 
						|
 | 
						|
type Props = {
 | 
						|
  messageId: string;
 | 
						|
  emoji: string;
 | 
						|
  count: number;
 | 
						|
  senders: Array<string>;
 | 
						|
  tooltipPosition?: TipPosition;
 | 
						|
  onClick: (...args: Array<any>) => void;
 | 
						|
};
 | 
						|
 | 
						|
export const ReactionPopup = (props: Props): ReactElement => {
 | 
						|
  const { messageId, emoji, count, senders, tooltipPosition = 'center', onClick } = props;
 | 
						|
 | 
						|
  const [contacts, setContacts] = useState<Array<string>>([]);
 | 
						|
 | 
						|
  useEffect(() => {
 | 
						|
    let isCancelled = false;
 | 
						|
    // eslint-disable-next-line more/no-then
 | 
						|
    generateContactsString(messageId, senders)
 | 
						|
      .then(async results => {
 | 
						|
        if (isCancelled) {
 | 
						|
          return;
 | 
						|
        }
 | 
						|
        if (results) {
 | 
						|
          setContacts(results);
 | 
						|
        }
 | 
						|
      })
 | 
						|
      .catch(() => {});
 | 
						|
 | 
						|
    return () => {
 | 
						|
      isCancelled = true;
 | 
						|
    };
 | 
						|
  }, [count, messageId, senders]);
 | 
						|
 | 
						|
  return (
 | 
						|
    <StyledPopupContainer tooltipPosition={tooltipPosition} onClick={onClick}>
 | 
						|
      {Contacts(contacts, count)}
 | 
						|
      <StyledEmoji role={'img'} aria-label={nativeEmojiData?.ariaLabels?.[emoji]}>
 | 
						|
        {emoji}
 | 
						|
      </StyledEmoji>
 | 
						|
    </StyledPopupContainer>
 | 
						|
  );
 | 
						|
};
 |