No Description

ContentRender.tsx 3.1KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. import React, { useMemo } from "react";
  2. import { IMAGE_SPLIT, REGEXP } from "../config/constant";
  3. import styles from "./ContentRender.less";
  4. import { htmlEncode, isUrl } from '../utils';
  5. export function renderContent(content: any) {
  6. let newContent = content;
  7. if (newContent.indexOf(IMAGE_SPLIT) !== -1) {
  8. newContent = newContent.split(IMAGE_SPLIT);
  9. newContent.pop();
  10. newContent = newContent.join("");
  11. }
  12. // 不包含在标签内的链接
  13. const innerUrl = /((http(s)?:)?\/\/)?(www\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\.[com|net|org|cn|edu|top|gov]{2,6}\b([-a-zA-Z0-9@:%_+.~#?&//=]*)(?![^<>]*>|[^"]*?<\/a)/g;
  14. const data = htmlEncode(newContent)
  15. .replace(REGEXP, function (a, b) {
  16. const src = a.slice(1, -1);
  17. // 兼容旧的评
  18. // 因为旧的评论用 [img url] 方式存储的
  19. if (isUrl(src)) {
  20. return `<a href="${src}" rel="noopener noreferrer" target="_blank"><img class="comment-img" src="${src}" alt="${src}" /></a>`;
  21. }
  22. // 如果不存在对应的表情, 则返回原文
  23. // const emoji = emojiObejct[src];
  24. if (
  25. sessionStorage.getItem("emojiMap") &&
  26. JSON.parse(sessionStorage.getItem("emojiMap"))
  27. ) {
  28. const emoji = JSON.parse(sessionStorage.getItem("emojiMap"))[src];
  29. if (emoji) {
  30. return `<img class="comment-emoji" src="${emoji}" alt="${emoji.title}" style="width:28px"/>`;
  31. }
  32. }
  33. return `[${src}]`;
  34. })
  35. .replace(innerUrl, function (a, b) {
  36. const protocol = /^(https?:)?\/\//;
  37. const hasProtocol = protocol.test(a);
  38. const url = hasProtocol ? a : `//${a}`;
  39. // target="_blank" 存在安全性问题
  40. // return `<a href="${url}" target="_blank" rel="noopener noreferrer" >${a}</a>`;
  41. return `<a href="${url}">${a}</a>`;
  42. })
  43. .replace(/\n/g, "<br />");
  44. return data;
  45. }
  46. export function renderTextWithReply(
  47. text: string,
  48. replyContent: { content: any; reply: any }
  49. ): string {
  50. let newText = text;
  51. const { reply } = replyContent;
  52. if (reply) {
  53. newText = `${newText} //@${reply.user_name} ${reply.content}`;
  54. if (reply.reply) {
  55. return renderTextWithReply(newText, reply);
  56. }
  57. }
  58. return newText;
  59. }
  60. export function contentPreTreatment(contentString: string): string {
  61. let newContentString: string | Array<string> = contentString;
  62. let imagesString: string | undefined = "";
  63. if (contentString.indexOf(IMAGE_SPLIT) !== -1) {
  64. newContentString = newContentString.split(IMAGE_SPLIT);
  65. imagesString = newContentString.pop();
  66. newContentString = newContentString.join("");
  67. }
  68. return contentString;
  69. }
  70. export const ContentRender = ({ data, renderContent: propsRenderContent }: { data: any, renderContent?(data: any): any }) => {
  71. if (propsRenderContent) {
  72. return (
  73. <div className={styles.contentWrap}>
  74. {propsRenderContent(data.content)}
  75. </div>
  76. )
  77. }
  78. const contentString: string = useMemo(
  79. () => contentPreTreatment(data.content),
  80. [data]
  81. );
  82. return (
  83. <div className={styles.contentWrap}>
  84. <div
  85. dangerouslySetInnerHTML={{
  86. __html: renderContent(renderTextWithReply(contentString, data)),
  87. }}
  88. />
  89. </div>
  90. );
  91. };