Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Social Previews | Update Mastodon and Tumblr previews #89483

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions client/components/share/mastodon-share-preview/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export class MastodonSharePreview extends PureComponent {
articleTitle,
articleContent,
imageUrl,
media,
message,
hidePostPreview,
} = this.props;
Expand All @@ -25,6 +26,7 @@ export class MastodonSharePreview extends PureComponent {
description={ decodeEntities( articleContent ) }
customText={ decodeEntities( message ) }
image={ imageUrl }
media={ media }
user={ {
displayName: externalName,
avatarUrl: externalProfilePicture,
Expand Down
2 changes: 1 addition & 1 deletion packages/social-previews/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@automattic/social-previews",
"version": "2.0.1-beta.12",
"version": "2.0.1-beta.13",
"description": "A suite of components to generate previews for a post for both social and search engines.",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
Expand Down
7 changes: 7 additions & 0 deletions packages/social-previews/src/helpers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,13 @@ export const formatTweetDate = new Intl.DateTimeFormat( 'en-US', {
day: 'numeric',
} ).format;

export const formatMastodonDate = new Intl.DateTimeFormat( 'en-US', {
// Result: "Apr 7, 2024", "Dec 31, 2023"
month: 'short',
day: 'numeric',
year: 'numeric',
} ).format;

export type Platform = 'twitter' | 'facebook' | 'linkedin' | 'instagram' | 'mastodon' | 'nextdoor';

type PreviewTextOptions = {
Expand Down
3 changes: 3 additions & 0 deletions packages/social-previews/src/mastodon-preview/constants.ts
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
export const DEFAULT_MASTODON_INSTANCE = 'mastodon.social';

export const DEFAULT_AVATAR =
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAZAAAAGQCAMAAAC3Ycb+AAAAWlBMVEXZ4ejW3+fQ2eTN1+LL1eHI0+DG0d7U3ebY4Oi7ydm0w9a2xNe5x9i+y9vS2+XDz93BzduwwNWputKjts+rvNOmudGtvtSgtM2OpMKHn72Bmbl8lbacsMuVq8fmVh92AAAKsklEQVR4AezBMQEAAAQAMKB/ZSWc2wIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnmT17O5xVx8ItqJAFIZLUcIhqXV1/zudeZnObYCr+G2gwy91pOz7TrRKU0W0+vXLS9n/+uWb+mMIafCadV6Khi5PCemdxWtGCkXVCinGOy4KTVno0HZy8FFmfH1aRIfPmD7UWcPha3YQ+liJLrpx4pceMLFTR2sMFl9zUlFVtPBYw0a19xQOmPkzE/a3DtJiDdNRNRppsdoQttc2C39rHoeWNms9VrOyqSQHtvGB1mt6N/Fak+nK5EhJ7vQ6kkHROsLyRpNZnVsNSO6SRDjs0usVrePCeyyxoe9piV2coOtSA/b69kMOlneb/bdJgsFeXtFFdThC0hdax4fMpsn+PJKOLikiyfultZaTAq9EGRwzaLoc5XCUbekjjecs5qg/642kyMU9QWuRQU/v9RPnsgj6QI8MbEuXIiyyiPRGGDmn91OiI7Kw4iZz/sabSy9nzmsSb3p4vFP/tPdAkSKN5fy8pqQxwP2KCOSUrkr74BJGlbdHIugSAlCkSORCJrGiR73LriyKFPFcTp+1R2IDnU4bZOc1acslRfrBIDuj6WwRBfgGXJbRekAB8laDnkxcmpNI7jPsjUMJM5c3ogjX0JlidT2SB3C7oxXqu1fliwQ6j6+xR7KgBH+zRX/w84w323WD/EZ+pvFWTyRU34NnlNDeaEFmfq4JBQx0ClX1oBcddkVnkMhu4aTmGenpDA7ZcVL1jJibTPrEZ1j+Z+cMciWFYSAaIj6kFYABk77/UWc18m42X928Cn5XKLnsisGdhMNJ37A+Z1pTD451mKNuWksPM9Zpd/HuYc4a9QvkkyUypm/zRzkSfv7dd5dvIW+7E/0mMqALBFAiQ/ouGV0ggNE3i/f0Zvdyqnf1qY8CcdSj4QooEHQXWaWHrMPuR/x9saJDOiCu1/RVCrxAAHFd+SXrNAKHchDZAHsp+OS76ebCwxgoJ8PcnWN5Ww9BDEILQTylc9p6CNLMAaT1EMQwNF1Btp4cyxHeqi+AGQvtWYvsSt1AnLofXI89pUJHd0E1o3+YAgy+s+rfU8269KyX7Ke9hqLp/pAw99RCHOFfdlZsCgEIssqe9buMxaF7XK5UaCwEPMFX3X8Mm7G4hO+dDBU5ZAHm3jrrngIy63Du3XWvLR5GQ7BAnCkEYV3MGiouhgDm3joIn7S+OhTkJ93IEoKwLsVmWC4EJMMsfba3GY1LcuR1hiUEIVyJdXIIgjAsZw9BCIbllCUE+cdSkiNpWobjlDUsZwpB/M0EwSsE8UhIYK4hiD/yEhhDEL8wjmAPQfbkOGqBvekL4hEdRX56DskJxvRsQaZEo7yeLMirJBxbfa4gdUtAfm4QBLKgGhORsj51Y7iWhGR+6k59S0j+Y1q/EOQvNWeR6DwMA2E/LIXJfP9j/lhWrFdH3sy3D1nVjJQq1nrMQH62HmzMhOdQdi5LG+u8984aPcoJxvp/Z4vc2fArLFF72HMLaP0NG0YZ0fkbTICxW0JCVywg2vhHJFlyC+4ZF8cE4O+wxC/ikyvo/DNue0SiJ9j1s83ywVFsX/9pBZ219hIbt1W2DDlbMiIzuqMTqhIBCReZCmc7FkXkcvTZzKNlMm6AHGvg+CzQqgf3/BsOdrtqReJB8Xz+kTLB9+iEStyqa/d/BVd+5nZkYLMtcle4scAnCOFT3Bna2wqSiMQxl/9rH55ibhNn67ETRD44t6QkxiTipDcJVqRZuC6B+AlC+ZI1IjqlTdrRQPFwB4X1y6AniHxHgfWayOmkG4T8CsulUydIqt7TXkHQSQISGKuw2Smi2bPRFBnwm3TpG605uUyUkO0ikZyND/2E/xZLpFm0zNKsLNncQstxBxgarV66zyW+Zi0rAbFsT2EyA+LShk+uBatYPB+CMsuw/bjNdHXDZlQkZ4OdHeV5E7l6ZExCGxPGHKLhFC5ELfD0NwVDDTp4Msn3ucTvDQfUkZMvhUMDut1JDzxrwlNhbghUruhFHr0G/RThWyHRQJrIVEKx8DVrgrSQSmHxhWgi2DUWz9sJz0Rm8OkfnhavNZzKWDr+yNyEZyGnnYLjG85EyicI6pfSM5piVW8KkE8wzeqLDTfg/08FVmN1CpQDUrO+oHyhI6CpgGz9dQNpFCy7CsTWMyy92ilg3g4oLvJdv6hXbwqa3+zdC3qqMBAF4BMISeSRjAyw/6Xevm6tmipGA1Tn34BfOUxOArVVM37OnbZYu0V2gK1m3D2dwh/nQn1REz6W5MDrsp+lF5q/8ps/d9DNhQW5Ul+T1K88IF+cvjAmjQb+PqBo67hdiW+BVzRaHJRdHdcWeBIq1BHB46eB10M44qmOCA7Po9zVx1pjcczwagaFE9Y0TzkeB0639UGrHc7seS0a51y1O+o6PB1X0GcmnfGIKXglNeK8+cykDSWelLLe4lc1r2L0+J31VuFFORp5DS2ihKeGVzCQQozQRAMvbuyoRIwIRN3IS2uJDESEpTcNL6ymNwrinKZ309IF8q6EOOMCfRgXLpB3BuKMp0+7hQvkg4U4pelLvXCByJoVF+i/iReyp/8MxImCvnU9L2KiA4tjwtBBNy60wTrQOCIU/dRyfj0dcRA/aTrSLLXhJan1uEDH6uXykFqPKOjUfrE8pNYjDJ2ZOJ9xR2cqfBOeIqZl5kNGJMJQzLBoHjIi8QHJn0jfUZxFlAzIwZQ9DxmRiIJouUSGjn5lIQ4vQuL22Z5fRRi8EZouqTM9340qIWDpsoYfp6YrlOThDF2xG/lBWrrGSCCarur6TMfziFJ2WDN0U6btVYSXI+EMgfherad5rBT6VcGiGPg+BWygOYLCy5p5iYwCoFq+i8LsRKysVxdVCh90z+l6vFFGErmgoFm0wxfb3/slKVeRNHvc7GtT4KC7/3vo5ey74MX4lPW84GThxg82Fq9EVWk3as+p9O2fraTNTwSPEw2n8jgoZLd1zIXUW5Q40YiD2bstIwPyg/E4V3GiPY55I4f2m/a7oUBMwYl2OFUGCWT2A5NQOkRZTlThjNMkb0e+VFemw+EXihN5RKgrU6LxKpy+3h1x6Z0eV5hLeci50JQWlyhO0+BXVgc5GcZLNWgLIEsgFS7x55mYAq8kch2q0uI6y0lGhcucLQ19M9riVVlflLoqrcMsZcY/9+N8qXVZeuswl6g4SYE8BHGKGpmIllOUyERMnGBCLoJTGGQiSk4wOGyJdHqFXETNt9sjF6HGTW2xRODbNchGDHyz0SMXYfh2hFyE6uVMuCmdNPqmaL7dDrmIYuSbTQqZiKKX1yBbUo0b2mEJ28prqQ1RNHKCQUE8nqqakVOMBcQjOVuarpk4VQXxQF3P9wkQj9TwfTqIh6qf6f/gSSCtg3is+nnykEB2EA+3f5L9lQQyVtgQCWQosCESSK2QhZg4wRiwIRLI5LEdEshIDtmIgW9Ue2yHBDJpZCV6vkEfHDZDAulJITfRSxzbMv7BxUoCGbYUhwQyGQexFL5s3HcFtkEC6etOK4hFOY6amq7yWJcEMvbTvmk7o73DWkRHwVSVLgur8JTEv/bgQAAAAAAAyP+1EVRVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUFpJkoc9sa+hgAAAAASUVORK5CYII=';
36 changes: 25 additions & 11 deletions packages/social-previews/src/mastodon-preview/post-preview.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,42 @@
import { __ } from '@wordpress/i18n';
import classNames from 'classnames';
import MastodonPostActions from './post/actions';
import MastonPostBody from './post/body';
import MastodonPostCard from './post/card';
import MastodonPostHeader from './post/header';
import type { MastodonPreviewProps } from './types';

import './styles.scss';

export const MastodonPostPreview: React.FC< MastodonPreviewProps > = ( props ) => {
const { user, customImage, image } = props;

const img = customImage || image;
const { user, media } = props;

return (
<div className="mastodon-preview__post">
<MastodonPostHeader user={ user } />
<MastonPostBody { ...props }>
{ img && (
<img
className="mastodon-preview__img"
src={ img }
alt={ __( 'Mastodon preview thumbnail', 'social-previews' ) }
/>
) }
{ media?.length ? (
<div
className={ classNames( 'mastodon-preview__media', { 'as-grid': media.length > 1 } ) }
>
{ media.map( ( mediaItem, index ) => (
<div
key={ `mastodon-preview__media-item-${ index }` }
className="mastodon-preview__media-item"
>
{ mediaItem.type.startsWith( 'video/' ) ? (
// eslint-disable-next-line jsx-a11y/media-has-caption
<video controls>
<source src={ mediaItem.url } type={ mediaItem.type } />
</video>
) : (
<img alt={ mediaItem.alt || '' } src={ mediaItem.url } />
) }
</div>
) ) }
</div>
) : null }
</MastonPostBody>
{ ! media?.length ? <MastodonPostCard { ...props } /> : null }
<MastodonPostActions />
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,49 +1,64 @@
import { __ } from '@wordpress/i18n';
import MastodonPostIcon from '../icons';

import './styles.scss';

const MastodonPostActions: React.FC = () => (
<div className="mastodon-preview__post-actions">
<ul>
{ [
{
icon: 'reply',
// translators: "Reply" action on a Mastodon post
label: __( 'Reply', 'social-previews' ),
text: 0,
},
{
icon: 'boost',
// translators: "Boost" action on a Mastodon post
label: __( 'Boost', 'social-previews' ),
},
{
icon: 'like',
// translators: "Favourite" action on a Mastodon post
label: __( 'Favourite', 'social-previews' ),
},
{
icon: 'bookmark',
// translators: "Bookmark" action on a Mastodon post
label: __( 'Bookmark', 'social-previews' ),
},
{
icon: 'more',
// translators: "More" menu on a Mastodon post
label: __( 'More', 'social-previews' ),
},
].map( ( { icon, label, text } ) => (
<li key={ icon } aria-label={ label }>
<span className="mastodon-preview__post-icon-wrapper">
<MastodonPostIcon name={ icon } />
</span>
{ ( typeof text === 'number' || text ) && (
<span className="mastodon-preview__post-icon-text">{ text }</span>
) }
</li>
) ) }
</ul>
<div>
<svg
xmlns="http://www.w3.org/2000/svg"
height="24"
viewBox="0 -960 960 960"
width="24"
aria-hidden="true"
>
<path d="M760-200v-160q0-50-35-85t-85-35H273l144 144-57 56-240-240 240-240 57 56-144 144h367q83 0 141.5 58.5T840-360v160h-80Z"></path>
</svg>
&nbsp;
<span>{ 0 }</span>
</div>
<div>
<svg
xmlns="http://www.w3.org/2000/svg"
height="24"
viewBox="0 -960 960 960"
width="24"
aria-hidden="true"
>
<path d="M280-80 120-240l160-160 56 58-62 62h406v-160h80v240H274l62 62-56 58Zm-80-440v-240h486l-62-62 56-58 160 160-160 160-56-58 62-62H280v160h-80Z"></path>
</svg>
</div>
<div>
<svg
xmlns="http://www.w3.org/2000/svg"
height="24"
viewBox="0 -960 960 960"
width="24"
aria-hidden="true"
>
<path d="m354-287 126-76 126 77-33-144 111-96-146-13-58-136-58 135-146 13 111 97-33 143ZM233-120l65-281L80-590l288-25 112-265 112 265 288 25-218 189 65 281-247-149-247 149Zm247-350Z"></path>
</svg>
</div>
<div>
<svg
xmlns="http://www.w3.org/2000/svg"
height="24"
viewBox="0 -960 960 960"
width="24"
aria-hidden="true"
>
<path d="M200-120v-640q0-33 23.5-56.5T280-840h400q33 0 56.5 23.5T760-760v640L480-240 200-120Zm80-122 200-86 200 86v-518H280v518Zm0-518h400-400Z"></path>
</svg>
</div>
<div>
<svg
xmlns="http://www.w3.org/2000/svg"
height="24"
viewBox="0 -960 960 960"
width="24"
aria-hidden="true"
>
<path d="M240-400q-33 0-56.5-23.5T160-480q0-33 23.5-56.5T240-560q33 0 56.5 23.5T320-480q0 33-23.5 56.5T240-400Zm240 0q-33 0-56.5-23.5T400-480q0-33 23.5-56.5T480-560q33 0 56.5 23.5T560-480q0 33-23.5 56.5T480-400Zm240 0q-33 0-56.5-23.5T640-480q0-33 23.5-56.5T720-560q33 0 56.5 23.5T800-480q0 33-23.5 56.5T720-400Z"></path>
</svg>
</div>
</div>
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,19 @@

.mastodon-preview__post-actions {
margin-top: 1rem;
display: flex;
justify-content: space-between;
align-items: center;

ul {
& > div {
display: flex;
justify-content: space-between;
align-items: center;

margin: 0;
padding: 0;

list-style-type: none;

> li {
display: flex;
align-items: center;
gap: 0.25rem;

margin: 0;
}
color: #606984;
}
}

.mastodon-preview__post-icon-wrapper {
display: flex;
justify-content: center;
align-items: center;

width: 24px;
height: 24px;
svg {
fill: currentColor;
}
}

.mastodon-preview__post-icon-text {
Expand Down
22 changes: 18 additions & 4 deletions packages/social-previews/src/mastodon-preview/post/card/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { __ } from '@wordpress/i18n';
import { baseDomain, getTitleFromDescription } from '../../../helpers';
import classNames from 'classnames';
import { baseDomain, getTitleFromDescription, stripHtmlTags } from '../../../helpers';
import { mastodonTitle } from '../../helpers';
import { MastodonPreviewProps } from '../../types';

Expand All @@ -14,20 +15,33 @@ const MastodonPostCard: React.FC< MastodonPreviewProps > = ( {
customImage,
} ) => {
return (
<div className="mastodon-preview__card">
<div className={ classNames( 'mastodon-preview__card', { 'has-image': image } ) }>
<div className="mastodon-preview__card-img">
{ ( image || customImage ) && (
{ image || customImage ? (
<img
src={ image || customImage }
alt={ __( 'Mastodon preview thumbnail', 'social-previews' ) }
/>
) : (
<div className="mastodon-preview__card-img--fallback">
<svg
xmlns="http://www.w3.org/2000/svg"
height="24"
viewBox="0 -960 960 960"
width="24"
aria-hidden="true"
>
<path d="M320-240h320v-80H320v80Zm0-160h320v-80H320v80ZM240-80q-33 0-56.5-23.5T160-160v-640q0-33 23.5-56.5T240-880h320l240 240v480q0 33-23.5 56.5T720-80H240Zm280-520h200L520-800v200Z"></path>
</svg>
</div>
) }
</div>
<div className="mastodon-preview__card-text">
<span className="mastodon-preview__card-site">{ siteName || baseDomain( url ) }</span>
<span className="mastodon-preview__card-title">
{ mastodonTitle( title ) || getTitleFromDescription( description ) }
</span>
<span className="mastodon-preview__card-site">{ siteName || baseDomain( url ) }</span>
<span className="mastodon-preview__card-description">{ stripHtmlTags( description ) }</span>
</div>
</div>
);
Expand Down
50 changes: 37 additions & 13 deletions packages/social-previews/src/mastodon-preview/post/card/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,24 @@
margin-top: 1rem;
margin-bottom: 1rem;

border: solid 1px #ccd7e0;
border-radius: 4px;
border: solid 1px #d9e1e8;
/* stylelint-disable-next-line scales/radii */
border-radius: 8px;
overflow: hidden;
color: $mastodon-body-text-color;

&.has-image {
flex-direction: column;
}

&.has-image &-img {
width: 100%;
}
}

.mastodon-preview__card-img {
width: 60px;
min-height: 60px;

border-start-start-radius: inherit;
border-end-start-radius: inherit;

img {
display: block;

Expand All @@ -28,25 +34,43 @@
border-start-start-radius: inherit;
border-end-start-radius: inherit;
}

&--fallback {
aspect-ratio: 1;
background: #c0cdd9;
position: relative;
width: 120px;
display: flex;
align-items: center;
justify-content: center;
}
}

.mastodon-preview__card-text {
display: flex;
flex-direction: column;
gap: 0.25rem;

padding: 0.625rem 0.5rem;
gap: 0.5rem;
padding: 1rem;
overflow: hidden;
}

.mastodon-preview__card-title {
color: #282c37;

font-size: 0.875rem;
/* stylelint-disable-next-line scales/font-sizes */
font-size: 1.187rem;
font-weight: 700;
line-height: 1.3;
line-height: 1.4;
}

.mastodon-preview__card-site {
/* stylelint-disable-next-line scales/font-sizes */
font-size: 0.8125rem;
font-size: 0.875rem;
display: block;
}

.mastodon-preview__card-description {
font-size: 0.875rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
Loading
Loading