<commit_before>// @flow

import invariant from 'invariant';
import * as SimpleMarkdown from 'simple-markdown';

import { messageTypes } from '../../types/message-types';
import type { MessageInfo } from '../../types/message-types';
import type {
  RawTextMessageInfo,
  TextMessageData,
  TextMessageInfo,
} from '../../types/messages/text';
import type { NotifTexts } from '../../types/notif-types';
import type { ThreadInfo } from '../../types/thread-types';
import type { RelativeUserInfo } from '../../types/user-types';
import { threadIsGroupChat } from '../thread-utils';
import { stringForUser } from '../user-utils';
import type {
  MessageSpec,
  NotificationTextsParams,
  RawMessageInfoFromRowParams,
} from './message-spec';
import { assertSingleMessageInfo } from './utils';

/**
 * most of the markdown leaves contain `content` field (it is an array or a string)
 * apart from lists, which have `items` field (that holds an array)
 */
const rawTextFromMarkdownAST = (node: SimpleMarkdown.ASTNode): string => {
  if (node.content && typeof node.content === 'string') {
    return node.content;
  } else if (node.items) {
    return rawTextFromMarkdownAST(node.items);
  } else if (node.content) {
    return rawTextFromMarkdownAST(node.content);
  } else if (Array.isArray(node)) {
    return node.map(rawTextFromMarkdownAST).join('');
  }
  return '';
};

const getFirstNonQuotedRawLine = (
  nodes: $ReadOnlyArray<SimpleMarkdown.SingleASTNode>,
): string => {
  let result = 'message';
  for (const node of nodes) {
    if (node.type === 'blockQuote') {
      result = 'quoted message';
    } else {
      const rawText = rawTextFromMarkdownAST(node);
      if (!rawText || !rawText.replace(/\s/g, '')) {
        // handles the case of an empty(or containing only white spaces)
        // new line that usually occurs between a quote and the rest
        // of the message(we don't want it as a title, thus continue)
        continue;
      }
      return rawText;
    }
  }
  return result;
};

export const textMessageSpec: MessageSpec<
  TextMessageData,
  RawTextMessageInfo,
  TextMessageInfo,
> = Object.freeze({
  messageContent(data: TextMessageData): string {
    return data.text;
  },

  messageTitle({ messageInfo, markdownRules }) {
    const { text } = messageInfo;
    const parser = SimpleMarkdown.parserFor(markdownRules);
    const ast = parser(text, { disableAutoBlockNewlines: true });

    return getFirstNonQuotedRawLine(ast);
  },

  rawMessageInfoFromRow(
    row: Object,
    params: RawMessageInfoFromRowParams,
  ): RawTextMessageInfo {
    const rawTextMessageInfo: RawTextMessageInfo = {
      type: messageTypes.TEXT,
      id: row.id.toString(),
      threadID: row.threadID.toString(),
      time: row.time,
      creatorID: row.creatorID.toString(),
      text: row.content,<commit_msg>[lib] Trim newline characters from sidebar titles<commit_after>// @flow

import invariant from 'invariant';
import * as SimpleMarkdown from 'simple-markdown';

import { messageTypes } from '../../types/message-types';
import type { MessageInfo } from '../../types/message-types';
import type {
  RawTextMessageInfo,
  TextMessageData,
  TextMessageInfo,
} from '../../types/messages/text';
import type { NotifTexts } from '../../types/notif-types';
import type { ThreadInfo } from '../../types/thread-types';
import type { RelativeUserInfo } from '../../types/user-types';
import { threadIsGroupChat } from '../thread-utils';
import { stringForUser } from '../user-utils';
import type {
  MessageSpec,
  NotificationTextsParams,
  RawMessageInfoFromRowParams,
} from './message-spec';
import { assertSingleMessageInfo } from './utils';

/**
 * most of the markdown leaves contain `content` field (it is an array or a string)
 * apart from lists, which have `items` field (that holds an array)
 */
const rawTextFromMarkdownAST = (node: SimpleMarkdown.ASTNode): string => {
  if (node.content && typeof node.content === 'string') {
    return node.content;
  } else if (node.items) {
    return rawTextFromMarkdownAST(node.items);
  } else if (node.content) {
    return rawTextFromMarkdownAST(node.content);
  } else if (Array.isArray(node)) {
    return node.map(rawTextFromMarkdownAST).join('');
  }
  return '';
};

const getFirstNonQuotedRawLine = (
  nodes: $ReadOnlyArray<SimpleMarkdown.SingleASTNode>,
): string => {
  let result = 'message';
  for (const node of nodes) {
    if (node.type === 'blockQuote') {
      result = 'quoted message';
    } else {
      const rawText = rawTextFromMarkdownAST(node);
      if (!rawText || !rawText.replace(/\s/g, '')) {
        // handles the case of an empty(or containing only white spaces)
        // new line that usually occurs between a quote and the rest
        // of the message(we don't want it as a title, thus continue)
        continue;
      }
      return rawText;
    }
  }
  return result;
};

export const textMessageSpec: MessageSpec<
  TextMessageData,
  RawTextMessageInfo,
  TextMessageInfo,
> = Object.freeze({
  messageContent(data: TextMessageData): string {
    return data.text;
  },

  messageTitle({ messageInfo, markdownRules }) {
    const { text } = messageInfo;
    const parser = SimpleMarkdown.parserFor(markdownRules);
    const ast = parser(text, { disableAutoBlockNewlines: true });

    return getFirstNonQuotedRawLine(ast).trim();
  },

  rawMessageInfoFromRow(
    row: Object,
    params: RawMessageInfoFromRowParams,
  ): RawTextMessageInfo {
    const rawTextMessageInfo: RawTextMessageInfo = {
      type: messageTypes.TEXT,
      id: row.id.toString(),
      threadID: row.threadID.toString(),
      time: row.time,
      creatorID: row.creatorID.toString(),
      text: row.content,
{'fraction_non_alphanumeric': 0.13045000875503415, 'fraction_numerical': 0.0, 'mean_word_length': 3.504731861198738, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4139529', 'n_tokens_mistral': 1684, 'n_tokens_neox': 1679, 'n_words': 461}