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

Make YouTube Embeds GDPR compliant #3253

Merged
merged 14 commits into from
Feb 7, 2022
59 changes: 59 additions & 0 deletions config/gatsby-remark-embedder/custom-yt-embedder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
const shouldTransform = url => {
const { host, pathname, searchParams } = new URL(url)

return (
host === 'youtu.be' ||
(['youtube.com', 'www.youtube.com'].includes(host) &&
pathname.includes('/watch') &&
Boolean(searchParams.get('v')))
)
julieg18 marked this conversation as resolved.
Show resolved Hide resolved
}

const getTimeValueInSeconds = timeValue => {
if (Number(timeValue).toString() === timeValue) {
return timeValue
}

const {
2: hours = '0',
4: minutes = '0',
6: seconds = '0'
} = timeValue.match(/((\d*)h)?((\d*)m)?((\d*)s)?/)
julieg18 marked this conversation as resolved.
Show resolved Hide resolved

return String((Number(hours) * 60 + Number(minutes)) * 60 + Number(seconds))
}

const getYouTubeIFrameSrc = urlString => {
const url = new URL(urlString)
const id =
url.host === 'youtu.be' ? url.pathname.slice(1) : url.searchParams.get('v')

const embedUrl = new URL(
`https://www.youtube-nocookie.com/embed/${id}?rel=0&controls=0&showinfo=0;`
)

url.searchParams.forEach((value, name) => {
if (name === 'v') {
return
}

if (name === 't') {
embedUrl.searchParams.append('start', getTimeValueInSeconds(value))
} else {
embedUrl.searchParams.append(name, value)
}
})
return embedUrl.toString()
}

// all code above taken from gatsby-remark-embedder (https://github.com/MichaelDeBoey/gatsby-remark-embedder)

const name = 'YouTubeCustom'

const getHTML = url => {
const iframeSrc = getYouTubeIFrameSrc(url)

return `<div class="yt-embed-wrapper"><iframe width="100%" height="315" src="${iframeSrc}" frameBorder="0" allow="autoplay; encrypted-media; gyroscope; picture-in-picture" allowFullScreen></iframe><div className="yt-embed-wrapper__overlay"><span class="yt-embed-wrapper__tooltip">By clicking play, you agree to YouTube&apos;s <a href="https://policies.google.com/u/3/privacy?hl=en">Privacy Policy</a> and <a href="https://www.youtube.com/static?template=terms">Terms of Service</a></span></div></div>`
julieg18 marked this conversation as resolved.
Show resolved Hide resolved
}

module.exports = { getHTML, name, shouldTransform }
8 changes: 7 additions & 1 deletion gatsby-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require('./config/prismjs/dvc')
require('./config/prismjs/usage')
require('./config/prismjs/dvctable')

const customYoutubeTransformer = require('./config/gatsby-remark-embedder/custom-yt-embedder')
const apiMiddleware = require('./src/server/middleware/api')
const redirectsMiddleware = require('./src/server/middleware/redirects')
const makeFeedHtml = require('./plugins/utils/makeFeedHtml')
Expand Down Expand Up @@ -70,7 +71,12 @@ const plugins = [
resolve: 'gatsby-transformer-remark',
options: {
plugins: [
'gatsby-remark-embedder',
{
resolve: 'gatsby-remark-embedder',
options: {
customTransformers: [customYoutubeTransformer]
}
},
'gatsby-remark-dvc-linker',
{
resolve: 'gatsby-remark-args-linker',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { navigate } from '@reach/router'
import Link from '../../../Link'
import Tutorials from '../../TutorialsLinks'
import { getPathWithSource } from '../../../../utils/shared/sidebar'
import setUpCustomYtEmbeds from '../../../..//utils/front/setUpCustomYtEmbeds'

import 'github-markdown-css/github-markdown-light.css'
import * as sharedStyles from '../../styles.module.css'
Expand Down Expand Up @@ -64,6 +65,8 @@ const Main: React.FC<IMainProps> = ({
}, [])

useEffect(() => {
setUpCustomYtEmbeds()

document.addEventListener('touchstart', onTouchStart, false)
document.addEventListener('touchend', onTouchEnd, false)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,53 @@
margin-left: 20px;
margin-right: 10px;
}

.yt-embed-wrapper {
julieg18 marked this conversation as resolved.
Show resolved Hide resolved
position: relative;
display: flex;

&:hover &__tooltip {
opacity: 1;
}

&__overlay {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
align-items: flex-end;

&:hover {
cursor: pointer;
}

&.hidden {
display: none;
}
}

&__tooltip {
padding: 10px;
box-sizing: border-box;
color: #fff;
opacity: 0;
width: 100%;
font-size: 16px;
background-color: rgb(23 23 23 / 59%);
text-shadow: 0 1px 0 rgb(33 45 69 / 25%);
transition: opacity 0.2s ease-in-out;

a {
color: #fff;

&::after {
display: none;
}
}
}
}
}

.content {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const setUpCustomYtEmbeds = () => {
Copy link
Contributor Author

@julieg18 julieg18 Feb 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This script takes all of our youtube embeds and attaches a click listener to our overlay. If the overlay is clicked, the video is played and the overlay disappears. It's definitely not very "reacty"... Does anybody have any ideas on how we could improve this?

Once we agree on a implementation (whether we deside to do this one or a different way) I want to have the code update local storage so that the message won't appear again if the user revisits the site :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does anybody have any ideas on how we could improve this?

Bit of a shot in the dark, but is it possible to have the custom embedder return a custom tag like <youtube> and then attach a React component to our rehype-react custom components with this logic in it? That way we can use the remark-embedder style of automatic URL grabbing with React JS more consistent with the rest of the site.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could try that but wouldn't that only work for our docs pages? We'd probably want a solution that works the same for blog and doc 🤔

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I forgot about our weird divergent blog markdown.

const ytEmbeds = document.querySelectorAll('.yt-embed-wrapper')
ytEmbeds.forEach(embed => {
const iframe = embed.querySelector('iframe')
const overlay = embed.querySelector('.yt-embed-wrapper__overlay')

overlay?.addEventListener('click', () => {
if (iframe && iframe.src) {
iframe.src = iframe?.src + `&autoplay=1`
}
overlay.classList.add('hidden')
})
})
}

export default setUpCustomYtEmbeds
49 changes: 49 additions & 0 deletions src/components/Blog/Post/Markdown/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -564,4 +564,53 @@
border-bottom: none;
}
}

:global {
.yt-embed-wrapper {
position: relative;
display: flex;

&:hover &__tooltip {
opacity: 1;
}

&__overlay {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
align-items: flex-end;

&:hover {
cursor: pointer;
}

&.hidden {
display: none;
}
}

&__tooltip {
padding: 10px;
box-sizing: border-box;
color: #fff;
opacity: 0;
width: 100%;
font-size: 16px;
background-color: rgb(23 23 23 / 59%);
text-shadow: 0 1px 0 rgb(33 45 69 / 25%);
transition: opacity 0.2s ease-in-out;

a {
color: #fff;

&::after {
display: none;
}
}
}
}
}
}
7 changes: 6 additions & 1 deletion src/components/Blog/Post/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import cn from 'classnames'

import React, { useMemo, useRef } from 'react'
import React, { useMemo, useRef, useEffect } from 'react'
import { useWindowScroll, useWindowSize } from 'react-use'

import { IBlogPostData } from '../../../templates/blog-post'

import { useCommentsCount } from 'gatsby-theme-iterative-docs/src/utils/front/api'
import { pluralizeComments } from 'gatsby-theme-iterative-docs/src/utils/front/i18n'
import tagToSlug from 'gatsby-theme-iterative-docs/src/utils/shared/tagToSlug'
import setUpCustomYtEmbeds from 'gatsby-theme-iterative-docs/src/utils/front/setUpCustomYtEmbeds'

import Markdown from './Markdown'
import FeedMeta from '../FeedMeta'
Expand Down Expand Up @@ -38,6 +39,10 @@ const Post: React.FC<IBlogPostData> = ({
const { width, height } = useWindowSize()
const { y } = useWindowScroll()

useEffect(() => {
setUpCustomYtEmbeds()
}, [])

julieg18 marked this conversation as resolved.
Show resolved Hide resolved
const isFixed = useMemo(() => {
if (!wrapperRef.current) {
return false
Expand Down
41 changes: 28 additions & 13 deletions src/components/Home/UseCases/Video/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import React, { useState, useCallback } from 'react'
import TwoRowsButton from '../../../TwoRowsButton'
import { logEvent } from 'gatsby-theme-iterative-docs/src/utils/front/plausible'

import Link from 'gatsby-theme-iterative-docs/src/components/Link'

import * as styles from './styles.module.css'

const Video: React.FC<{ id: string }> = ({ id }) => {
Expand All @@ -18,19 +20,32 @@ const Video: React.FC<{ id: string }> = ({ id }) => {
<div className={styles.handler}>
{!isWatching && (
<div className={styles.overlay}>
<TwoRowsButton
mode="azure"
title="Watch video"
description="How it works"
icon={
<img
className={styles.buttonIcon}
src="/img/watch_white.svg"
alt="Watch video"
/>
}
onClick={watchVideo}
/>
<div className={styles.content}>
<TwoRowsButton
mode="azure"
title="Watch video"
description="How it works"
className={styles.button}
icon={
<img
className={styles.buttonIcon}
src="/img/watch_white.svg"
alt="Watch video"
/>
}
onClick={watchVideo}
/>
<div className={styles.tooltip}>
By clicking play, you agree to YouTube&apos;s{' '}
<Link href="https://policies.google.com/u/3/privacy?hl=en">
Privacy Policy
</Link>{' '}
and{' '}
<Link href="https://www.youtube.com/static?template=terms">
Terms of Service
</Link>
</div>
</div>
</div>
)}
<iframe
Expand Down
27 changes: 27 additions & 0 deletions src/components/Home/UseCases/Video/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,34 @@
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
background-color: rgb(23 23 23 / 59%);

.content {
julieg18 marked this conversation as resolved.
Show resolved Hide resolved
position: absolute;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
padding-top: calc(50% - 30px);

&:hover .tooltip {
opacity: 1;
}
}

.tooltip {
margin: 16px 0;
opacity: 0;
color: #fff;
transition: opacity ease-in-out 0.2s;
text-shadow: 0 1px 0 rgb(33 45 69 / 25%);

a {
color: #fff;
}
}
}

.buttonIcon {
Expand Down