]> Untitled Git - lemmy-ui.git/blob - src/shared/components/post/metadata-card.tsx
Sanitize article html. Fixes #882 (#883)
[lemmy-ui.git] / src / shared / components / post / metadata-card.tsx
1 import { Component, linkEvent } from "inferno";
2 import { Post } from "lemmy-js-client";
3 import * as sanitizeHtml from "sanitize-html";
4 import { i18n } from "../../i18next";
5 import { relTags } from "../../utils";
6 import { Icon } from "../common/icon";
7
8 interface MetadataCardProps {
9   post: Post;
10 }
11
12 interface MetadataCardState {
13   expanded: boolean;
14 }
15
16 export class MetadataCard extends Component<
17   MetadataCardProps,
18   MetadataCardState
19 > {
20   private emptyState: MetadataCardState = {
21     expanded: false,
22   };
23
24   constructor(props: any, context: any) {
25     super(props, context);
26     this.state = this.emptyState;
27   }
28
29   render() {
30     let post = this.props.post;
31     return (
32       <>
33         {!this.state.expanded &&
34           post.embed_title.match({
35             some: embedTitle =>
36               post.url.match({
37                 some: url => (
38                   <div className="card border-secondary mt-3 mb-2">
39                     <div className="row">
40                       <div className="col-12">
41                         <div className="card-body">
42                           {post.name !== embedTitle && (
43                             <>
44                               <h5 className="card-title d-inline">
45                                 <a
46                                   className="text-body"
47                                   href={url}
48                                   rel={relTags}
49                                 >
50                                   {embedTitle}
51                                 </a>
52                               </h5>
53                               <span className="d-inline-block ml-2 mb-2 small text-muted">
54                                 <a
55                                   className="text-muted font-italic"
56                                   href={url}
57                                   rel={relTags}
58                                 >
59                                   {new URL(url).hostname}
60                                   <Icon icon="external-link" classes="ml-1" />
61                                 </a>
62                               </span>
63                             </>
64                           )}
65                           {post.embed_description.match({
66                             some: desc => (
67                               <div
68                                 className="card-text small text-muted md-div"
69                                 dangerouslySetInnerHTML={{
70                                   __html: sanitizeHtml(desc),
71                                 }}
72                               />
73                             ),
74                             none: <></>,
75                           })}
76                           {post.embed_video_url.isSome() && (
77                             <button
78                               className="mt-2 btn btn-secondary text-monospace"
79                               onClick={linkEvent(this, this.handleIframeExpand)}
80                             >
81                               {i18n.t("expand_here")}
82                             </button>
83                           )}
84                         </div>
85                       </div>
86                     </div>
87                   </div>
88                 ),
89                 none: <></>,
90               }),
91             none: <></>,
92           })}
93         {this.state.expanded &&
94           post.embed_video_url.match({
95             some: video_url => <iframe src={video_url}></iframe>,
96             none: <></>,
97           })}
98       </>
99     );
100   }
101
102   handleIframeExpand(i: MetadataCard) {
103     i.setState({ expanded: !i.state.expanded });
104   }
105 }