Skip to content
This repository was archived by the owner on Apr 19, 2021. It is now read-only.

Commit 1dcdc8a

Browse files
committed
Replace embedded tweets with self-build tweets.
Fixes gitpod-io/website#825
1 parent 484ea36 commit 1dcdc8a

20 files changed

+569
-144
lines changed

src/components/index/Testimonial.tsx

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import React from 'react'
2+
3+
import styled from '@emotion/styled'
4+
import { borders, colors } from '../../styles/variables'
5+
6+
const StyledTestimonial = styled.blockquote`
7+
display: flex;
8+
flex-direction: column;
9+
justify-content: space-between;
10+
padding: 2.5rem;
11+
max-width: 375px;
12+
min-width: 360px;
13+
border: ${borders.normal};
14+
border-radius: 16px;
15+
text-align: left;
16+
17+
@media(max-width: 700px) {
18+
min-width: 340px;
19+
}
20+
21+
@media(max-width: 500px) {
22+
min-width: 320px;
23+
padding: 1.8rem;
24+
}
25+
26+
@media(max-width: 340px) {
27+
min-width: 280px;
28+
}
29+
30+
.text-container {
31+
display: flex;
32+
font-size: 95%;
33+
}
34+
35+
footer {
36+
display: flex;
37+
align-items: center;
38+
margin-top: 2.5rem;
39+
font-size: 1.5rem;
40+
}
41+
42+
.avatar {
43+
height: 5rem;
44+
margin-right: 1.2rem;
45+
border-radius: 50%;
46+
}
47+
48+
.twitter-icon {
49+
fill: #b5b5b5;
50+
height: 2rem;
51+
margin-left: .4rem;
52+
transition: all .5s;
53+
54+
@media(max-width: 640px) {
55+
height: 1.8rem;
56+
margin-left: .3rem;
57+
}
58+
}
59+
60+
a {
61+
&:hover,
62+
&:focus {
63+
.twitter-icon {
64+
fill: ${colors.textDark};
65+
}
66+
}
67+
}
68+
`
69+
70+
export interface TestimonialProps {
71+
name: string
72+
avatar: string
73+
org: string | JSX.Element
74+
role: string | JSX.Element
75+
text: string | JSX.Element
76+
tweetId: string
77+
twitterHandle: string
78+
}
79+
80+
const Testimonial = ({ name, avatar, role, org, text, tweetId, twitterHandle }: TestimonialProps) => (
81+
<StyledTestimonial>
82+
<div className="text-container">
83+
<div>{text}</div>
84+
<a href={`https://twitter.com/${twitterHandle}/status/${tweetId}`} target="_blank">
85+
<svg xmlns="http://www.w3.org/2000/svg" className="twitter-icon" width="28.724" height="23.348" viewBox="0 0 28.724 23.348"><path d="M28.724,5.012a11.766,11.766,0,0,1-3.385.928,5.9,5.9,0,0,0,2.591-3.26,11.806,11.806,0,0,1-3.743,1.427A5.9,5.9,0,0,0,14.146,9.48,16.73,16.73,0,0,1,2,3.327,5.9,5.9,0,0,0,3.823,11.2a5.865,5.865,0,0,1-2.668-.737,5.9,5.9,0,0,0,4.726,5.856,5.906,5.906,0,0,1-2.662.1A5.9,5.9,0,0,0,8.725,20.5,11.849,11.849,0,0,1,0,22.945a16.683,16.683,0,0,0,9.034,2.647A16.666,16.666,0,0,0,25.784,8.063a12,12,0,0,0,2.941-3.051Z" transform="translate(0 -2.245)" /></svg>
86+
</a>
87+
</div>
88+
<footer>
89+
<img src={avatar} alt={name} className="avatar" />
90+
<div className="author-description">
91+
<h4>{name}</h4>
92+
<p>{role} {org}</p>
93+
</div>
94+
</footer>
95+
</StyledTestimonial>
96+
)
97+
98+
export default Testimonial

src/components/index/Testimonials.tsx

Lines changed: 126 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,100 +1,145 @@
1-
import React from 'react'
1+
// @ts-nocheck
2+
import React, { useRef, useState, useEffect } from 'react'
23

34
import styled from '@emotion/styled'
4-
import TweetEmbed from 'react-tweet-embed'
5-
import { sizes } from '../../styles/variables'
5+
import { sizes, colors } from '../../styles/variables'
6+
import Testimonial, { TestimonialProps } from './Testimonial'
7+
import { testimonials } from '../../contents'
68

79
const StyledTestimonials = styled.div`
8-
min-height: 60rem;
9-
margin: 8rem 0 10rem;
10+
min-height: 60rem;
11+
margin: 8rem 0 10rem;
1012
11-
@media(max-width: ${sizes.breakpoints.sm}) {
12-
text-align: center;
13-
}
13+
@media (max-width: ${sizes.breakpoints.sm}) {
14+
text-align: center;
15+
}
1416
15-
/* ------------------------------------------- */
16-
/* ----- Section Testimonials ----- */
17-
/* ------------------------------------------- */
17+
/* ------------------------------------------- */
18+
/* ----- Section Testimonials ----- */
19+
/* ------------------------------------------- */
1820
19-
h3 {
20-
margin-bottom: 3rem;
21-
text-align: center;
22-
}
21+
h3 {
22+
margin-bottom: 3rem;
23+
text-align: center;
24+
}
25+
26+
.tweets {
27+
display: flex;
28+
transition: all 0.3s;
2329
24-
.tweets {
25-
display: flex;
30+
&-container {
31+
padding-bottom: 4rem;
2632
overflow-x: scroll;
27-
align-items: center;
28-
29-
@media(min-width: calc(${sizes.breakpoints.md} + 1px)) {
30-
padding-left: calc((100% - ${sizes.grid.maxWidth})/2);
31-
}
32-
33-
& > div {
34-
&:first-of-type {
35-
@media(max-width: ${sizes.breakpoints.lg}) {
36-
padding-left: 7rem;
37-
}
38-
39-
@media(max-width: ${sizes.breakpoints.md}) {
40-
padding-left: 4rem;
41-
}
42-
43-
@media(max-width: ${sizes.breakpoints.sm}) {
44-
padding-left: 1rem;
45-
}
46-
}
47-
}
4833
}
4934
50-
.tweet {
51-
min-width: 400px;
35+
& > * {
36+
flex: 0 0 32%;
5237
53-
@media(max-width: 440px) {
54-
min-width: 95%;
55-
}
38+
&:not(:last-child) {
39+
margin-right: 1.333%;
40+
}
41+
}
42+
}
43+
44+
.dots {
45+
display: flex;
46+
justify-content: center;
47+
padding: 4rem 0 5rem;
5648
57-
&:not(:last-of-type) {
58-
margin-right: 3rem;
59-
}
49+
@media(max-width: 740px) {
50+
padding-top: 3rem;
6051
}
6152
53+
@media(max-width: 440px) {
54+
padding-top: 2.5rem;
55+
}
56+
}
57+
58+
.dot {
59+
height: 1.5rem;
60+
width: 1.5rem;
61+
background: ${colors.offWhite4};
62+
border: none;
63+
border-radius: 50%;
64+
transition: all .2s;
65+
66+
&:hover,
67+
&:focus {
68+
background: ${colors.lightGrey};
69+
}
70+
71+
&:not(:last-child) {
72+
margin-right: 1.5rem;
73+
}
74+
}
6275
`
6376

64-
const twitterOptions = { theme: 'light', dnt: true, cards: 'hidden' }
65-
66-
const tweets = [
67-
'1115274432958930946',
68-
'1102215129696010240',
69-
'1167463499779338243',
70-
'1191710936605831169',
71-
'1131239314346729482',
72-
'1217728632887611397',
73-
'1117695539540365312',
74-
'1116152894548582402',
75-
'1159698330764611584',
76-
'1117141675745402881',
77-
'1120015913024139265',
78-
'1221093493214310400',
79-
'1215700809104740354'
80-
]
81-
82-
const Testimonials: React.SFC<{}> = () => (
83-
<StyledTestimonials>
84-
<section className="testimonials">
85-
<div className="row">
86-
<h3>
87-
Used by 200,000+ <strong>Developers & Businesses</strong>
88-
</h3>
89-
</div>
90-
<div className="tweets">
91-
{tweets.map((t, i) => (
92-
<TweetEmbed key={i} className="tweet" id={t} options={twitterOptions} />
93-
))}
94-
<TweetEmbed className="tweet" id="1215707492740739072" options={{ ...twitterOptions, conversation: 'none' }} />
95-
</div>
96-
</section>
97-
</StyledTestimonials>
98-
)
77+
const Testimonials: React.SFC<{}> = () => {
78+
const [currentCycle, setCurrentCycle] = useState<number>(0)
79+
const tweetsRef = useRef<HTMLDivElement>(null)
80+
const tweetsContainerRef = useRef<HTMLDivElement>(null)
81+
const cycles = Math.ceil(testimonials.length / 3)
82+
83+
useEffect(() => {
84+
const tweetsContainer = tweetsContainerRef.current
85+
const dots = document.querySelectorAll('.dot')
86+
const cycleWidth = tweetsContainer?.scrollWidth / cycles
87+
88+
tweetsContainer.addEventListener('scroll', (e) => {
89+
const currentScrollPosition = tweetsContainer?.scrollLeft
90+
setCurrentCycle(cycles - Math.floor((tweetsContainer?.scrollWidth - currentScrollPosition) / cycleWidth))
91+
})
92+
})
93+
94+
const switchTweets = (to: number) => {
95+
const tweetsContainer = tweetsContainerRef.current
96+
const cycleWidth = tweetsContainer?.scrollWidth / cycles
97+
tweetsContainer.scroll({ left: parseFloat(cycleWidth * to), behavior: 'smooth' })
98+
99+
// Older method which regards the actuals tweets and the space between them and not the width of .tweets
100+
101+
// const tweets = tweetsRef.current
102+
// const firstTweet = tweets?.firstChild
103+
// const spacing = getComputedStyle(firstTweet)['margin-right']
104+
// const transform = firstTweet.offsetWidth * 3 + parseFloat(spacing.substring(0, spacing.length - 2)) * 2.8
105+
}
106+
107+
return (
108+
<StyledTestimonials>
109+
<section className="testimonials">
110+
<div className="row">
111+
<h3>
112+
Used by 200,000+ <strong>Developers & Businesses</strong>
113+
</h3>
114+
<div className="tweets-container" ref={tweetsContainerRef}>
115+
<div className="tweets" ref={tweetsRef}>
116+
{testimonials.map((t) => (
117+
<Testimonial key={t.name} {...t} />
118+
))}
119+
</div>
120+
</div>
121+
<div className="dots">
122+
{[...Array(cycles).keys()].map((d) => {
123+
return (
124+
<button
125+
className="dot"
126+
key={d}
127+
onClick={() => {
128+
setCurrentCycle(d)
129+
switchTweets(d)
130+
}}
131+
style={currentCycle === d ? { background: colors.lightGrey } : {}}
132+
title={`Switch to ${d + 1}th set of tweets.`}
133+
>
134+
&nbsp;
135+
</button>
136+
)
137+
})}
138+
</div>
139+
</div>
140+
</section>
141+
</StyledTestimonials>
142+
)
143+
}
99144

100145
export default Testimonials

0 commit comments

Comments
 (0)