闭社主体 forked from https://github.com/tootsuite/mastodon
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.

101 lines
2.3 KiB

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