Skip to content

Commit

Permalink
Overhaul MetaHeadEmbed + Fix getShareUrl's (#5)
Browse files Browse the repository at this point in the history
* Update README

* Update to use PageLayout

* Implement render support + overhauled embeds

* Update README

* Fix README

* Tweak

* Refactor sharing

* Tweaks

* Add hashtag check

* Update README
  • Loading branch information
PauloMFJ authored Jul 9, 2021
1 parent ca80a22 commit 7a528dd
Show file tree
Hide file tree
Showing 8 changed files with 229 additions and 191 deletions.
149 changes: 73 additions & 76 deletions README.md

Large diffs are not rendered by default.

158 changes: 128 additions & 30 deletions src/components/MetaHeadEmbed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,53 @@ import React from "react";

import isAbsoluteUrl from "is-absolute-url";

import { commaSeparate } from "../utils";

export interface TwitterEmbedProps {
/** Summary card size. */
cardSize: "small" | "large";

/** A concise title for the related content. */
title?: string;

/**
* A description that concisely summarizes the content as appropriate for
* presentation within a Tweet. Should not be the same as title.
*/
description?: string;

/** The Twitter @username the card should be attributed to. */
siteUsername?: string;

/** The Twitter @username for the content creator / author. */
creatorUsername?: string;

/**
* Image to show in card. _Should_ only be used if image is different to
* `MetaHeadEmbed` image.
*
* Should be different based on `useLargeCard`:
* - For large cards, use a 2:1 aspect ratio (300x157 px minium or
* 4096x4096 px maximum).
* - For small cards, use a 1:1 aspect ratio (144x144 px minium or
* 4096x4096 px maximum).
*
* Images must be less than 5MB in size.
*
* Supported file types; JPG, PNG, WEBP and GIF.
*
* Note: Only the first frame of an animated GIF will be used.
*/
imageUrl?: string;

/** Image alt for users who are visually impaired. Maximum 420 characters. */
imageAlt?: string;
}

export interface MetaEmbedProps {
/** Returns meta properties to be rendered. */
render: (meta: React.ReactNode) => JSX.Element;

/** Unique page title that describes the page, such as `Home`, `About` etc. */
pageTitle: string;

Expand All @@ -23,17 +69,17 @@ export interface MetaEmbedProps {
/** Canonical URL of your webpage that will be used as its default app URL. */
canonicalUrl?: string;

/** Base URL of the site, excluding trailing slash. */
siteBaseUrl: string;
/** Base site URL, excluding trailing slash. */
baseSiteUrl: string;

/** The path of the page, excluding leading slash. */
pagePath?: string;

/**
* List of SEO keywords describing what your webpage does.
* For example, `"your, tags"` or `["your", "tags"]`.
* Example: `"your, tags"` or `["your", "tags"]`.
*/
keywords: string | string[];
keywords?: string | string[];

/**
* Image url of asset to share. Recommended aspect ratio for landscape is
Expand All @@ -51,24 +97,26 @@ export interface MetaEmbedProps {
* Defaults to `en_US`.
*/
locale?: string;

/** Twitter embed properties */
twitter?: TwitterEmbedProps;
}

const MetaHeadEmbed = ({
render,
pageTitle,
siteTitle,
titleTemplate,
description,
canonicalUrl,
siteBaseUrl,
baseSiteUrl,
pagePath,
keywords,
imageUrl,
imageAlt,
locale = "en_US",
twitter,
}: MetaEmbedProps) => {
const joinedKeywords =
typeof keywords === "string" ? keywords : keywords?.join(", ");

const title = titleTemplate
? pageTitle === siteTitle
? pageTitle
Expand All @@ -81,28 +129,78 @@ const MetaHeadEmbed = ({
canonicalUrl &&
(isAbsoluteUrl(canonicalUrl)
? canonicalUrl
: `${siteBaseUrl}/${canonicalUrl}`);

const pageUrl = pagePath ? `${siteBaseUrl}/${pagePath}` : siteBaseUrl;

return (
<>
<title>{title}</title>
<meta name="title" content={title} />
<meta name="description" content={description} />
<meta name="keywords" content={joinedKeywords} />
{canonicalUrl && <link rel="canonical" href={canonical} />}

<meta property="og:type" content="website" />
<meta property="og:url" content={pageUrl} />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:image" content={imageUrl} />
<meta property="og:image:alt" content={imageAlt} />
<meta property="og:site_name" content={siteTitle} />
<meta property="og:locale" content={locale} />
</>
);
: `${baseSiteUrl}/${canonicalUrl}`);

const pageUrl = pagePath ? `${baseSiteUrl}/${pagePath}` : baseSiteUrl;

const metaEmbed = [
<title key="title">{title}</title>,
<meta key="meta:title" name="title" content={title} />,
<meta key="meta:description" name="description" content={description} />,
keywords && (
<meta
key="meta:keywords"
name="keywords"
content={commaSeparate(keywords)}
/>
),
canonicalUrl && <link key="canonical" rel="canonical" href={canonical} />,

<meta key="og:type" property="og:type" content="website" />,
<meta key="og:url" property="og:url" content={pageUrl} />,
<meta key="og:title" property="og:title" content={title} />,
<meta
key="og:description"
property="og:description"
content={description}
/>,
<meta key="og:image" property="og:image" content={imageUrl} />,
<meta key="og:image:alt" property="og:image:alt" content={imageAlt} />,
<meta key="og:site_name" property="og:site_name" content={siteTitle} />,
<meta key="og:locale" property="og:locale" content={locale} />,
];

const twitterEmbed = ({
cardSize,
title,
description,
siteUsername,
creatorUsername,
imageUrl,
imageAlt,
}: TwitterEmbedProps) => [
<meta
key="twitter:card"
name="twitter:card"
content={cardSize === "large" ? "summary_large_image" : "summary"}
/>,
title && <meta key="twitter:title" name="twitter:title" content={title} />,
description && (
<meta
key="twitter:description"
name="twitter:description"
content={description}
/>
),
siteUsername && (
<meta key="twitter:site" name="twitter:site" content={siteUsername} />
),
creatorUsername && (
<meta
key="twitter:creator"
name="twitter:creator"
content={creatorUsername}
/>
),
imageUrl && (
<meta key="twitter:image" name="twitter:image" content={imageUrl} />
),
imageAlt && (
<meta key="twitter:alt" name="twitter:image:alt" content={imageAlt} />
),
];

return render([metaEmbed, twitter && twitterEmbed({ ...twitter })]);
};

export default MetaHeadEmbed;
69 changes: 0 additions & 69 deletions src/components/TwitterHeadEmbed.tsx

This file was deleted.

13 changes: 7 additions & 6 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
export {
default as SharingHeadEmbed,
default as MetaHeadEmbed,
MetaEmbedProps,
} from "./components/MetaHeadEmbed";

export {
default as TwitterHeadEmbed,
TwitterEmbedProps,
} from "./components/TwitterHeadEmbed";
} from "./components/MetaHeadEmbed";

export {
default as getLinkedinUrl,
Expand All @@ -20,4 +16,9 @@ export {
FacebookProps,
} from "./utils/getFacebookUrl";

export {
default as getShareUrl,
AllSocialPlatformProps,
} from "./utils/getShareUrl";

export { SocialPlatforms } from "./types";
2 changes: 2 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const commaSeparate = (words?: string | string[]) =>
typeof words === "string" ? words : words?.join(",");
4 changes: 2 additions & 2 deletions src/utils/getFacebookUrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ export interface FacebookProps extends BaseShareProps {
quote?: string;

/** Hashtag to show in Facebook card. */
hashtag: string;
hashtag?: string;
}

export const getFacebookUrl = ({ url, quote, hashtag }: FacebookProps) =>
`https://www.facebook.com/sharer/sharer.php${objectToUrlParams({
u: url,
quote,
hashtag,
hashtag: hashtag?.charAt(0) === "#" ? hashtag : `#${hashtag}`,
})}`;

export default getFacebookUrl;
6 changes: 4 additions & 2 deletions src/utils/getShareUrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import getFacebookUrl, { FacebookProps } from "./getFacebookUrl";
import getLinkedinUrl, { LinkedinProps } from "./getLinkedinUrl";
import getTwitterUrl, { TwitterProps } from "./getTwitterUrl";

interface Props extends FacebookProps, LinkedinProps, TwitterProps {}
export type AllSocialPlatformProps = FacebookProps &
LinkedinProps &
TwitterProps;

export const getShareUrl = (
socialPlatform: SocialPlatforms,
Expand All @@ -17,7 +19,7 @@ export const getShareUrl = (
text,
hashtags,
related,
}: Props
}: AllSocialPlatformProps
) => {
switch (socialPlatform) {
case SocialPlatforms.Facebook:
Expand Down
19 changes: 13 additions & 6 deletions src/utils/getTwitterUrl.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,30 @@
import { BaseShareProps } from "../types";
import { commaSeparate } from "../utils";
import objectToUrlParams from "./objectToUrlParams";

export interface TwitterProps extends BaseShareProps {
/** Text to show in card. */
text?: string;

/** Hashtags to show in Twitter card. */
hashtags?: string[];
/**
* Hashtags to show in Twitter card.
* Example: `"your, tags"` or `["your", "tags"]`.
*/
hashtags?: string | string[];

/** Accounts to recommend following. */
related?: string[];
/**
* Accounts to recommend following.
* Example: `"your, tags"` or `["your", "tags"]`.
*/
related?: string | string[];
}

export const getTwitterUrl = ({ url, text, hashtags, related }: TwitterProps) =>
`https://twitter.com/share${objectToUrlParams({
url,
text,
hashtags: hashtags?.join(","),
related: related?.join(","),
hashtags: commaSeparate(hashtags),
related: commaSeparate(related),
})}`;

export default getTwitterUrl;

0 comments on commit 7a528dd

Please sign in to comment.