]>
cat aescling's git repositories - mastodon.git/blob - app/javascript/mastodon/features/status/components/card.js
1 import React
from 'react';
2 import PropTypes
from 'prop-types';
3 import Immutable
from 'immutable';
4 import ImmutablePropTypes
from 'react-immutable-proptypes';
5 import punycode
from 'punycode';
6 import classnames
from 'classnames';
8 const IDNA_PREFIX
= 'xn--';
10 const decodeIDNA
= domain
=> {
13 .map(part
=> part
.indexOf(IDNA_PREFIX
) === 0 ? punycode
.decode(part
.slice(IDNA_PREFIX
.length
)) : part
)
17 const getHostname
= url
=> {
18 const parser
= document
.createElement('a');
20 return parser
.hostname
;
23 const trim
= (text
, len
) => {
24 const cut
= text
.indexOf(' ', len
);
30 return text
.substring(0, cut
) + (text
.length
> len
? '…' : '');
33 const domParser
= new DOMParser();
35 const addAutoPlay
= html
=> {
36 const document
= domParser
.parseFromString(html
, 'text/html').documentElement
;
37 const iframe
= document
.querySelector('iframe');
40 if (iframe
.src
.indexOf('?') !== -1) {
46 iframe
.src
+= 'autoplay=1&auto_play=1';
48 // DOM parser creates html/body elements around original HTML fragment,
49 // so we need to get innerHTML out of the body and not the entire document
50 return document
.querySelector('body').innerHTML
;
56 export default class Card
extends React
.PureComponent
{
59 card: ImmutablePropTypes
.map
,
60 maxDescription: PropTypes
.number
,
61 onOpenMedia: PropTypes
.func
.isRequired
,
64 static defaultProps
= {
73 componentWillReceiveProps (nextProps
) {
74 if (this.props
.card
!== nextProps
.card
) {
75 this.setState({ embedded: false });
79 handlePhotoClick
= () => {
80 const { card
, onOpenMedia
} = this.props
;
86 url: card
.get('embed_url'),
87 description: card
.get('title'),
90 width: card
.get('width'),
91 height: card
.get('height'),
100 handleEmbedClick
= () => {
101 const { card
} = this.props
;
103 if (card
.get('type') === 'photo') {
104 this.handlePhotoClick();
106 this.setState({ embedded: true });
112 this.setState({ width: c
.offsetWidth
});
117 const { card
} = this.props
;
118 const content
= { __html: addAutoPlay(card
.get('html')) };
119 const { width
} = this.state
;
120 const ratio
= card
.get('width') / card
.get('height');
121 const height
= card
.get('width') > card
.get('height') ? (width
/ ratio
) : (width
* ratio
);
126 className
='status-card__image status-card-video'
127 dangerouslySetInnerHTML
={content
}
134 const { card
, maxDescription
} = this.props
;
135 const { width
, embedded
} = this.state
;
141 const provider
= card
.get('provider_name').length
=== 0 ? decodeIDNA(getHostname(card
.get('url'))) : card
.get('provider_name');
142 const horizontal
= card
.get('width') > card
.get('height') && (card
.get('width') + 100 >= width
) || card
.get('type') !== 'link';
143 const className
= classnames('status-card', { horizontal
});
144 const interactive
= card
.get('type') !== 'link';
145 const title
= interactive
? <a className
='status-card__title' href
={card
.get('url')} title
={card
.get('title')} rel
='noopener' target
='_blank'><strong
>{card
.get('title')}</strong></a> : <strong className
='status-card__title' title
={card
.get('title')}>{card
.get('title')}</strong
>;
146 const ratio
= card
.get('width') / card
.get('height');
147 const height
= card
.get('width') > card
.get('height') ? (width
/ ratio
) : (width
* ratio
);
149 const description
= (
150 <div className
='status-card__content'>
152 {!horizontal
&& <p className
='status-card__description'>{trim(card
.get('description') || '', maxDescription
)}</p
>}
153 <span className
='status-card__host'>{provider
}</span
>
158 let thumbnail
= <div style
={{ backgroundImage: `url(${card.get('image')})`, width: horizontal
? width : null, height: horizontal
? height : null }} className
='status-card__image-image' />;
162 embed
= this.renderVideo();
164 let iconVariant
= 'play';
166 if (card
.get('type') === 'photo') {
167 iconVariant
= 'search-plus';
171 <div className
='status-card__image'>
174 <div className
='status-card__actions'>
176 <button onClick
={this.handleEmbedClick
}><i className
={`fa fa-${iconVariant}`} /></button
>
177 <a href
={card
.get('url')} target
='_blank' rel
='noopener'><i className
='fa fa-external-link' /></a>
185 <div className
={className
} ref
={this.setRef
}>
190 } else if (card
.get('image')) {
192 <div className
='status-card__image'>
199 <a href
={card
.get('url')} className
={className
} target
='_blank' rel
='noopener' ref
={this.setRef
}>
This page took 0.26949 seconds and 4 git commands to generate.