* Make PreviewCard records reuseable between statuses **Warning!** Migration truncates preview_cards tablec * Allow a wider thumbnail for link preview, display it in horizontal layout (#4648) * Delete preview cards files before truncating * Rename old table instead of truncating it * Add mastodon:maintenance:remove_deprecated_preview_cards * Ignore deprecated_preview_cards in schema definition * Fix null behaviour
		
			
				
	
	
		
			105 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			105 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| import React from 'react';
 | |
| import ImmutablePropTypes from 'react-immutable-proptypes';
 | |
| import punycode from 'punycode';
 | |
| import classnames from 'classnames';
 | |
| 
 | |
| const IDNA_PREFIX = 'xn--';
 | |
| 
 | |
| const decodeIDNA = domain => {
 | |
|   return domain
 | |
|     .split('.')
 | |
|     .map(part => part.indexOf(IDNA_PREFIX) === 0 ? punycode.decode(part.slice(IDNA_PREFIX.length)) : part)
 | |
|     .join('.');
 | |
| };
 | |
| 
 | |
| const getHostname = url => {
 | |
|   const parser = document.createElement('a');
 | |
|   parser.href = url;
 | |
|   return parser.hostname;
 | |
| };
 | |
| 
 | |
| export default class Card extends React.PureComponent {
 | |
| 
 | |
|   static propTypes = {
 | |
|     card: ImmutablePropTypes.map,
 | |
|   };
 | |
| 
 | |
|   renderLink () {
 | |
|     const { card } = this.props;
 | |
| 
 | |
|     let image    = '';
 | |
|     let provider = card.get('provider_name');
 | |
| 
 | |
|     if (card.get('image')) {
 | |
|       image = (
 | |
|         <div className='status-card__image'>
 | |
|           <img src={card.get('image')} alt={card.get('title')} className='status-card__image-image' width={card.get('width')} height={card.get('height')} />
 | |
|         </div>
 | |
|       );
 | |
|     }
 | |
| 
 | |
|     if (provider.length < 1) {
 | |
|       provider = decodeIDNA(getHostname(card.get('url')));
 | |
|     }
 | |
| 
 | |
|     const className = classnames('status-card', {
 | |
|       'horizontal': card.get('width') > card.get('height'),
 | |
|     });
 | |
| 
 | |
|     return (
 | |
|       <a href={card.get('url')} className={className} target='_blank' rel='noopener'>
 | |
|         {image}
 | |
| 
 | |
|         <div className='status-card__content'>
 | |
|           <strong className='status-card__title' title={card.get('title')}>{card.get('title')}</strong>
 | |
|           <p className='status-card__description'>{(card.get('description') || '').substring(0, 50)}</p>
 | |
|           <span className='status-card__host'>{provider}</span>
 | |
|         </div>
 | |
|       </a>
 | |
|     );
 | |
|   }
 | |
| 
 | |
|   renderPhoto () {
 | |
|     const { card } = this.props;
 | |
| 
 | |
|     return (
 | |
|       <a href={card.get('url')} className='status-card-photo' target='_blank' rel='noopener'>
 | |
|         <img src={card.get('url')} alt={card.get('title')} width={card.get('width')} height={card.get('height')} />
 | |
|       </a>
 | |
|     );
 | |
|   }
 | |
| 
 | |
|   renderVideo () {
 | |
|     const { card } = this.props;
 | |
|     const content  = { __html: card.get('html') };
 | |
| 
 | |
|     return (
 | |
|       <div
 | |
|         className='status-card-video'
 | |
|         dangerouslySetInnerHTML={content}
 | |
|       />
 | |
|     );
 | |
|   }
 | |
| 
 | |
|   render () {
 | |
|     const { card } = this.props;
 | |
| 
 | |
|     if (card === null) {
 | |
|       return null;
 | |
|     }
 | |
| 
 | |
|     switch(card.get('type')) {
 | |
|     case 'link':
 | |
|       return this.renderLink();
 | |
|     case 'photo':
 | |
|       return this.renderPhoto();
 | |
|     case 'video':
 | |
|       return this.renderVideo();
 | |
|     case 'rich':
 | |
|     default:
 | |
|       return null;
 | |
|     }
 | |
|   }
 | |
| 
 | |
| }
 |