You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

104 lines
2.5 KiB

  1. import React from 'react';
  2. import ImmutablePropTypes from 'react-immutable-proptypes';
  3. import punycode from 'punycode';
  4. import classnames from 'classnames';
  5. const IDNA_PREFIX = 'xn--';
  6. const decodeIDNA = domain => {
  7. return domain
  8. .split('.')
  9. .map(part => part.indexOf(IDNA_PREFIX) === 0 ? punycode.decode(part.slice(IDNA_PREFIX.length)) : part)
  10. .join('.');
  11. };
  12. const getHostname = url => {
  13. const parser = document.createElement('a');
  14. parser.href = url;
  15. return parser.hostname;
  16. };
  17. export default class Card extends React.PureComponent {
  18. static propTypes = {
  19. card: ImmutablePropTypes.map,
  20. };
  21. renderLink () {
  22. const { card } = this.props;
  23. let image = '';
  24. let provider = card.get('provider_name');
  25. if (card.get('image')) {
  26. image = (
  27. <div className='status-card__image'>
  28. <img src={card.get('image')} alt={card.get('title')} className='status-card__image-image' width={card.get('width')} height={card.get('height')} />
  29. </div>
  30. );
  31. }
  32. if (provider.length < 1) {
  33. provider = decodeIDNA(getHostname(card.get('url')));
  34. }
  35. const className = classnames('status-card', {
  36. 'horizontal': card.get('width') > card.get('height'),
  37. });
  38. return (
  39. <a href={card.get('url')} className={className} target='_blank' rel='noopener'>
  40. {image}
  41. <div className='status-card__content'>
  42. <strong className='status-card__title' title={card.get('title')}>{card.get('title')}</strong>
  43. <p className='status-card__description'>{(card.get('description') || '').substring(0, 50)}</p>
  44. <span className='status-card__host'>{provider}</span>
  45. </div>
  46. </a>
  47. );
  48. }
  49. renderPhoto () {
  50. const { card } = this.props;
  51. return (
  52. <a href={card.get('url')} className='status-card-photo' target='_blank' rel='noopener'>
  53. <img src={card.get('url')} alt={card.get('title')} width={card.get('width')} height={card.get('height')} />
  54. </a>
  55. );
  56. }
  57. renderVideo () {
  58. const { card } = this.props;
  59. const content = { __html: card.get('html') };
  60. return (
  61. <div
  62. className='status-card-video'
  63. dangerouslySetInnerHTML={content}
  64. />
  65. );
  66. }
  67. render () {
  68. const { card } = this.props;
  69. if (card === null) {
  70. return null;
  71. }
  72. switch(card.get('type')) {
  73. case 'link':
  74. return this.renderLink();
  75. case 'photo':
  76. return this.renderPhoto();
  77. case 'video':
  78. return this.renderVideo();
  79. case 'rich':
  80. default:
  81. return null;
  82. }
  83. }
  84. }