// @flow import { SORT_COMMENTS_NEW, SORT_COMMENTS_BEST, SORT_COMMENTS_CONTROVERSIAL } from 'constants/comment'; import { FREE_GLOBAL_STICKERS, PAID_GLOBAL_STICKERS } from 'constants/stickers'; import * as REACTION_TYPES from 'constants/reactions'; const ALL_VALID_STICKERS = [...FREE_GLOBAL_STICKERS, ...PAID_GLOBAL_STICKERS]; const stickerRegex = /(:[A-Z0-9_]+:)/; // Mostly taken from Reddit's sorting functions // https://github.com/reddit-archive/reddit/blob/master/r2/r2/lib/db/_sorts.pyx type SortProps = { comments: ?Array, reactionsById: {}, sort: string, isMyComment: (string) => boolean, }; export function sortComments(sortProps: SortProps): Array { const { comments, reactionsById, sort, isMyComment } = sortProps; if (!comments) return []; return comments.slice().sort((a: Comment, b: Comment) => { if (a.is_pinned) { return -1; } else if (b.is_pinned) { return 1; } if (sort === SORT_COMMENTS_NEW) return 0; const aIsMine = isMyComment(a.channel_id); const bIsMine = isMyComment(b.channel_id); if (aIsMine) { return -1; } else if (bIsMine) { return 1; } const aReactions = reactionsById[a.comment_id]; const bReactions = reactionsById[b.comment_id]; const aLikes = (aReactions && aReactions[REACTION_TYPES.LIKE]) || 0; const aDislikes = (aReactions && aReactions[REACTION_TYPES.DISLIKE]) || 0; const bLikes = (bReactions && bReactions[REACTION_TYPES.LIKE]) || 0; const bDislikes = (bReactions && bReactions[REACTION_TYPES.DISLIKE]) || 0; if (sort === SORT_COMMENTS_CONTROVERSIAL) { if (aLikes === 0 && aDislikes === 0) { return 1; } else if (bLikes === 0 && bDislikes === 0) { return -1; } const aMagnitude = aLikes + aDislikes; const bMagnitude = bLikes + bDislikes; const aBalance = aLikes > aDislikes ? aDislikes / aLikes : aLikes / aDislikes; const bBalance = bLikes > bDislikes ? bDislikes / bLikes : bLikes / bDislikes; return bMagnitude ** bBalance - aMagnitude ** aBalance; } if (sort === SORT_COMMENTS_BEST) { const aN = aLikes + aDislikes; const bN = bLikes + bDislikes; if (aLikes === 0 && bLikes === 0 && (aDislikes || bDislikes)) { return aDislikes - bDislikes; } else if (aLikes === 0 && bLikes > 0) { return 1; } else if (bLikes === 0 && aLikes > 1) { return -1; } const z = 1.281551565545; // 80% confidence const aP = aLikes / aN; const bP = bLikes / bN; const aLeft = aP + (1 / (2 * aN)) * z * z; const aRight = z * Math.sqrt((aP * (1 - aP)) / aN + (z * z) / (4 * aN * aN)); const aUnder = 1 + (1 / aN) * z * z; const bLeft = bP + (1 / (2 * bN)) * z * z; const bRight = z * Math.sqrt((bP * (1 - bP)) / bN + (z * z) / (4 * bN * bN)); const bUnder = 1 + (1 / bN) * z * z; return (bLeft - bRight) / bUnder - (aLeft - aRight) / aUnder; } return 0; }); } export const buildValidSticker = (sticker: string) => `${sticker}`; export function parseSticker(comment: string) { const matchSticker = comment.match(stickerRegex); const stickerValue = matchSticker && matchSticker[0]; const stickerName = stickerValue && stickerValue.replace(//g, ''); const commentIsSticker = stickerValue && stickerValue.length === comment.length; return commentIsSticker && ALL_VALID_STICKERS.find(({ name }) => name === stickerName); } export function getStickerUrl(comment: string) { const stickerFromComment = parseSticker(comment); return stickerFromComment && stickerFromComment.url; } export function getCommentsListTitle(totalComments: number) { const title = (totalComments === 0 && __('Leave a comment')) || (totalComments === 1 && __('1 comment')) || __('%total_comments% comments', { total_comments: totalComments }); return title; }