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.
		
		
		
		
		
			
		
			
				
	
	
		
			183 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			TypeScript
		
	
			
		
		
	
	
			183 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			TypeScript
		
	
import React from 'react';
 | 
						|
 | 
						|
import { RenderTextCallbackType } from '../../types/Util';
 | 
						|
import classNames from 'classnames';
 | 
						|
 | 
						|
declare global {
 | 
						|
  interface Window {
 | 
						|
    lokiPublicChatAPI: any;
 | 
						|
    shortenPubkey: any;
 | 
						|
    pubkeyPattern: any;
 | 
						|
    getConversations: any;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
interface MentionProps {
 | 
						|
  key: number;
 | 
						|
  text: string;
 | 
						|
  convoId: string;
 | 
						|
}
 | 
						|
 | 
						|
interface MentionState {
 | 
						|
  found: any;
 | 
						|
}
 | 
						|
 | 
						|
class Mention extends React.Component<MentionProps, MentionState> {
 | 
						|
  private intervalHandle: any = null;
 | 
						|
  constructor(props: any) {
 | 
						|
    super(props);
 | 
						|
 | 
						|
    this.tryRenameMention = this.tryRenameMention.bind(this);
 | 
						|
  }
 | 
						|
 | 
						|
  public componentWillMount() {
 | 
						|
    const found = this.findMember(this.props.text.slice(1));
 | 
						|
    this.setState({ found });
 | 
						|
 | 
						|
    this.tryRenameMention();
 | 
						|
    // TODO: give up after some period of time?
 | 
						|
    this.intervalHandle = setInterval(this.tryRenameMention, 30000);
 | 
						|
  }
 | 
						|
 | 
						|
  public componentWillUnmount() {
 | 
						|
    this.clearOurInterval();
 | 
						|
  }
 | 
						|
 | 
						|
  public render() {
 | 
						|
    if (this.state.found) {
 | 
						|
      // TODO: We don't have to search the database of message just to know that the message is for us!
 | 
						|
      const us =
 | 
						|
        this.state.found.authorPhoneNumber === window.lokiPublicChatAPI.ourKey;
 | 
						|
      const className = classNames(
 | 
						|
        'mention-profile-name',
 | 
						|
        us && 'mention-profile-name-us'
 | 
						|
      );
 | 
						|
 | 
						|
      const profileName = this.state.found.authorProfileName;
 | 
						|
      const displayedName =
 | 
						|
        profileName && profileName.length > 0 ? profileName : 'Anonymous';
 | 
						|
 | 
						|
      return <span className={className}>{displayedName}</span>;
 | 
						|
    } else {
 | 
						|
      return (
 | 
						|
        <span className="mention-profile-name">
 | 
						|
          {window.shortenPubkey(this.props.text)}
 | 
						|
        </span>
 | 
						|
      );
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  private clearOurInterval() {
 | 
						|
    clearInterval(this.intervalHandle);
 | 
						|
  }
 | 
						|
 | 
						|
  private tryRenameMention() {
 | 
						|
    const found = this.findMember(this.props.text.slice(1));
 | 
						|
    if (found) {
 | 
						|
      this.setState({ found });
 | 
						|
      this.clearOurInterval();
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  private async findMember(pubkey: String) {
 | 
						|
    let groupMembers;
 | 
						|
 | 
						|
    const groupConvos = window.getConversations().models.filter((d: any) => {
 | 
						|
      return !d.isPrivate();
 | 
						|
    });
 | 
						|
    const thisConvo = groupConvos.find((d: any) => {
 | 
						|
      return d.id === this.props.convoId;
 | 
						|
    });
 | 
						|
 | 
						|
    if (!thisConvo) {
 | 
						|
      // If this gets triggered, is is likely because we deleted the conversation
 | 
						|
      this.clearOurInterval();
 | 
						|
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    if (thisConvo.isPublic()) {
 | 
						|
      groupMembers = await window.lokiPublicChatAPI.getListOfMembers();
 | 
						|
      groupMembers = groupMembers.filter((m: any) => !!m);
 | 
						|
    } else {
 | 
						|
      const privateConvos = window
 | 
						|
        .getConversations()
 | 
						|
        .models.filter((d: any) => d.isPrivate());
 | 
						|
      const members = thisConvo.attributes.members;
 | 
						|
      if (!members) {
 | 
						|
        return null;
 | 
						|
      }
 | 
						|
      const memberConversations = members
 | 
						|
        .map((m: any) => privateConvos.find((c: any) => c.id === m))
 | 
						|
        .filter((c: any) => !!c);
 | 
						|
      groupMembers = memberConversations.map((m: any) => {
 | 
						|
        const name = m.getLokiProfile()
 | 
						|
          ? m.getLokiProfile().displayName
 | 
						|
          : m.attributes.displayName;
 | 
						|
 | 
						|
        return {
 | 
						|
          id: m.id,
 | 
						|
          authorPhoneNumber: m.id,
 | 
						|
          authorProfileName: name,
 | 
						|
        };
 | 
						|
      });
 | 
						|
    }
 | 
						|
 | 
						|
    return groupMembers.find(
 | 
						|
      ({ authorPhoneNumber: pn }: any) => pn && pn === pubkey
 | 
						|
    );
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
interface Props {
 | 
						|
  text: string;
 | 
						|
  renderOther?: RenderTextCallbackType;
 | 
						|
  convoId: string;
 | 
						|
}
 | 
						|
 | 
						|
export class AddMentions extends React.Component<Props> {
 | 
						|
  public static defaultProps: Partial<Props> = {
 | 
						|
    renderOther: ({ text }) => text,
 | 
						|
  };
 | 
						|
 | 
						|
  public render() {
 | 
						|
    const { text, renderOther, convoId } = this.props;
 | 
						|
    const results: Array<any> = [];
 | 
						|
    const FIND_MENTIONS = window.pubkeyPattern;
 | 
						|
 | 
						|
    // We have to do this, because renderNonNewLine is not required in our Props object,
 | 
						|
    //  but it is always provided via defaultProps.
 | 
						|
    if (!renderOther) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    let match = FIND_MENTIONS.exec(text);
 | 
						|
    let last = 0;
 | 
						|
    let count = 1000;
 | 
						|
 | 
						|
    if (!match) {
 | 
						|
      return renderOther({ text, key: 0 });
 | 
						|
    }
 | 
						|
 | 
						|
    while (match) {
 | 
						|
      if (last < match.index) {
 | 
						|
        const otherText = text.slice(last, match.index);
 | 
						|
        results.push(renderOther({ text: otherText, key: count++ }));
 | 
						|
      }
 | 
						|
 | 
						|
      const pubkey = text.slice(match.index, FIND_MENTIONS.lastIndex);
 | 
						|
      results.push(<Mention text={pubkey} key={count++} convoId={convoId} />);
 | 
						|
 | 
						|
      // @ts-ignore
 | 
						|
      last = FIND_MENTIONS.lastIndex;
 | 
						|
      match = FIND_MENTIONS.exec(text);
 | 
						|
    }
 | 
						|
 | 
						|
    if (last < text.length) {
 | 
						|
      results.push(renderOther({ text: text.slice(last), key: count++ }));
 | 
						|
    }
 | 
						|
 | 
						|
    return results;
 | 
						|
  }
 | 
						|
}
 |